micro-cc 0.2.30__tar.gz → 0.2.32__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 (161) hide show
  1. {micro_cc-0.2.30 → micro_cc-0.2.32}/PKG-INFO +1 -4
  2. {micro_cc-0.2.30 → micro_cc-0.2.32}/README.md +0 -2
  3. {micro_cc-0.2.30 → micro_cc-0.2.32}/pyproject.toml +1 -2
  4. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/browser/_md_convert.py +20 -6
  5. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/screens/microcc-styles.tcss +1 -1
  6. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/start_live_.py +121 -148
  7. micro_cc-0.2.32/src/micro_cc/utils/helpers.py +133 -0
  8. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc.egg-info/PKG-INFO +1 -4
  9. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc.egg-info/SOURCES.txt +1 -2
  10. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc.egg-info/requires.txt +0 -1
  11. micro_cc-0.2.30/src/micro_cc/utils/helpers.py +0 -101
  12. micro_cc-0.2.30/src/micro_cc/utils/utils.py +0 -46
  13. {micro_cc-0.2.30 → micro_cc-0.2.32}/setup.cfg +0 -0
  14. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/__init__.py +0 -0
  15. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/browser/__init__.py +0 -0
  16. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/browser/_cookies.py +0 -0
  17. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/browser/browser_manager.py +0 -0
  18. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/browser/simpletextbrowser.py +0 -0
  19. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/cache/__init__.py +0 -0
  20. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/cache/redis_cache.py +0 -0
  21. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/claude_loop_.py +0 -0
  22. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/code_test/__init__.py +0 -0
  23. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/code_test/run_token_cutter.py +0 -0
  24. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/code_test/test_messages.py +0 -0
  25. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/execute_tool.py +0 -0
  26. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/models/__init__.py +0 -0
  27. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/models/anthropic.py +0 -0
  28. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/models/litellm.py +0 -0
  29. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/models/ollama.py +0 -0
  30. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/models/schema.py +0 -0
  31. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/screens/__init__.py +0 -0
  32. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/screens/md_render_.py +0 -0
  33. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/screens/window_overlay_.py +0 -0
  34. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/SKILL.md +0 -0
  35. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/ArsenalSC-OFL.txt +0 -0
  36. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  37. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  38. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/BigShoulders-OFL.txt +0 -0
  39. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  40. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Boldonse-OFL.txt +0 -0
  41. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  42. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  43. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -0
  44. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  45. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  46. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  47. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/CrimsonPro-OFL.txt +0 -0
  48. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  49. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/DMMono-OFL.txt +0 -0
  50. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/DMMono-Regular.ttf +0 -0
  51. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/EricaOne-OFL.txt +0 -0
  52. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  53. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  54. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/GeistMono-OFL.txt +0 -0
  55. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  56. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Gloock-OFL.txt +0 -0
  57. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Gloock-Regular.ttf +0 -0
  58. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  59. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexMono-OFL.txt +0 -0
  60. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  61. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  62. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  63. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  64. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  65. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  66. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  67. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  68. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSans-OFL.txt +0 -0
  69. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  70. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  71. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  72. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Italiana-OFL.txt +0 -0
  73. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Italiana-Regular.ttf +0 -0
  74. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  75. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/JetBrainsMono-OFL.txt +0 -0
  76. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  77. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Jura-Light.ttf +0 -0
  78. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Jura-Medium.ttf +0 -0
  79. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Jura-OFL.txt +0 -0
  80. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/LibreBaskerville-OFL.txt +0 -0
  81. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  82. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Lora-Bold.ttf +0 -0
  83. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  84. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Lora-Italic.ttf +0 -0
  85. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Lora-OFL.txt +0 -0
  86. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Lora-Regular.ttf +0 -0
  87. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  88. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/NationalPark-OFL.txt +0 -0
  89. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  90. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -0
  91. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  92. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Outfit-Bold.ttf +0 -0
  93. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Outfit-OFL.txt +0 -0
  94. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Outfit-Regular.ttf +0 -0
  95. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  96. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/PixelifySans-OFL.txt +0 -0
  97. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/PoiretOne-OFL.txt +0 -0
  98. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  99. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  100. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/RedHatMono-OFL.txt +0 -0
  101. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  102. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Silkscreen-OFL.txt +0 -0
  103. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  104. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  105. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/SmoochSans-OFL.txt +0 -0
  106. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Tektur-Medium.ttf +0 -0
  107. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Tektur-OFL.txt +0 -0
  108. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/Tektur-Regular.ttf +0 -0
  109. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  110. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  111. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  112. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/WorkSans-OFL.txt +0 -0
  113. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  114. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/YoungSerif-OFL.txt +0 -0
  115. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/canvas-design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  116. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/github/SKILL.md +0 -0
  117. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/SKILL.md +0 -0
  118. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/editing.md +0 -0
  119. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/pptxgenjs.md +0 -0
  120. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/__init__.py +0 -0
  121. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/add_slide.py +0 -0
  122. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/clean.py +0 -0
  123. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/helpers/__init__.py +0 -0
  124. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/helpers/merge_runs.py +0 -0
  125. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/helpers/simplify_redlines.py +0 -0
  126. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/pack.py +0 -0
  127. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/soffice.py +0 -0
  128. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/unpack.py +0 -0
  129. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/validate.py +0 -0
  130. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/validators/__init__.py +0 -0
  131. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/validators/base.py +0 -0
  132. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/validators/docx.py +0 -0
  133. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/validators/pptx.py +0 -0
  134. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/office/validators/redlining.py +0 -0
  135. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/pptx/scripts/thumbnail.py +0 -0
  136. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/skill_loader.py +0 -0
  137. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/skills/xlsx/SKILL.md +0 -0
  138. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/__init__.py +0 -0
  139. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/bash_tool.py +0 -0
  140. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/browser_tool_.py +0 -0
  141. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/computer_tool_.py +0 -0
  142. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/file_tools_.py +0 -0
  143. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/mcp_client_.py +0 -0
  144. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/memory_tool_.py +0 -0
  145. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/search_tool_.py +0 -0
  146. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/skill_tools_.py +0 -0
  147. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/todo_tools_.py +0 -0
  148. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/vision_tools_.py +0 -0
  149. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/tools/web_tools_.py +0 -0
  150. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/__init__.py +0 -0
  151. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/annotate_.py +0 -0
  152. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/claude_md_loader.py +0 -0
  153. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/file_watcher_.py +0 -0
  154. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/memory_store_.py +0 -0
  155. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/msg_store_.py +0 -0
  156. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/process_tracker.py +0 -0
  157. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/utils/tokenization_simple.py +0 -0
  158. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc/web_.py +0 -0
  159. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc.egg-info/dependency_links.txt +0 -0
  160. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc.egg-info/entry_points.txt +0 -0
  161. {micro_cc-0.2.30 → micro_cc-0.2.32}/src/micro_cc.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: micro-cc
3
- Version: 0.2.30
3
+ Version: 0.2.32
4
4
  Summary: Harness that gives frontier models full system access — shell, filesystem, browser, MCP — running directly on the metal.
5
5
  Author: George Salapa
6
6
  License-Expression: MIT
@@ -21,7 +21,6 @@ Requires-Dist: mcp>=1.26
21
21
  Requires-Dist: numpy>=2.4
22
22
  Requires-Dist: openai>=2.16
23
23
  Requires-Dist: openpyxl>=3.1
24
- Requires-Dist: pandas>=3.0
25
24
  Requires-Dist: pathvalidate>=3.3
26
25
  Requires-Dist: pdfminer.six>=20260107
27
26
  Requires-Dist: pillow>=12.0
@@ -60,8 +59,6 @@ Frontier models with full system access — shell, filesystem, browser, MCP —
60
59
 
61
60
  ## Install
62
61
 
63
- Please use `iTerm2`. Highly recommended terminal UI.
64
-
65
62
  ```bash
66
63
  pip install micro-cc
67
64
  ```
@@ -16,8 +16,6 @@ Frontier models with full system access — shell, filesystem, browser, MCP —
16
16
 
17
17
  ## Install
18
18
 
19
- Please use `iTerm2`. Highly recommended terminal UI.
20
-
21
19
  ```bash
22
20
  pip install micro-cc
23
21
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "micro-cc"
7
- version = "0.2.30"
7
+ version = "0.2.32"
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"
@@ -27,7 +27,6 @@ dependencies = [
27
27
  "numpy>=2.4",
28
28
  "openai>=2.16",
29
29
  "openpyxl>=3.1",
30
- "pandas>=3.0",
31
30
  "pathvalidate>=3.3",
32
31
  "pdfminer.six>=20260107",
33
32
  "pillow>=12.0",
@@ -6,7 +6,6 @@ from micro_cc.models.anthropic import a_model_call
6
6
  from micro_cc.models.litellm import l_model_call
7
7
  from bs4 import BeautifulSoup
8
8
  import pdfminer.high_level
9
- import pandas as pd
10
9
  import markdownify
11
10
  import subprocess
12
11
  import traceback
@@ -456,12 +455,27 @@ class XlsxConverter(HtmlConverter):
456
455
  if extension.lower() not in [".xlsx", ".xls"]:
457
456
  return None
458
457
 
459
- sheets = pd.read_excel(local_path, sheet_name=None)
458
+ # Imported lazily — this is the only xlsx-reading path in the app,
459
+ # no reason to pay openpyxl's cost on every startup for a feature
460
+ # most sessions never touch.
461
+ import openpyxl
462
+
463
+ workbook = openpyxl.load_workbook(local_path, data_only=True, read_only=True)
460
464
  md_content = ""
461
- for s in sheets:
462
- md_content += f"## {s}\n"
463
- html_content = sheets[s].to_html(index=False)
464
- md_content += self._convert(html_content).text_content.strip() + "\n\n"
465
+ for sheet_name in workbook.sheetnames:
466
+ rows = list(workbook[sheet_name].iter_rows(values_only=True))
467
+ md_content += f"## {sheet_name}\n"
468
+ if rows:
469
+ header, *body = rows
470
+ cell = lambda tag, v: f"<{tag}>{'' if v is None else v}</{tag}>"
471
+ html_content = "<table><thead><tr>"
472
+ html_content += "".join(cell("th", v) for v in header)
473
+ html_content += "</tr></thead><tbody>"
474
+ for row in body:
475
+ html_content += "<tr>" + "".join(cell("td", v) for v in row) + "</tr>"
476
+ html_content += "</tbody></table>"
477
+ md_content += self._convert(html_content).text_content.strip() + "\n\n"
478
+ workbook.close()
465
479
 
466
480
  return DocumentConverterResult(
467
481
  title=None,
@@ -33,7 +33,7 @@ PromptInput > .text-area--cursor {
33
33
  display: none;
34
34
  color: green;
35
35
  }
36
- #model-picker, #rewind-picker, #slash-picker, #dangerous-picker {
36
+ #model-picker, #rewind-picker, #slash-picker, #dangerous-picker, #login-picker {
37
37
  display: none;
38
38
  height: auto;
39
39
  max-height: 10;
@@ -4,9 +4,12 @@ import re
4
4
  import subprocess
5
5
  import sys
6
6
  import platform
7
+
8
+ from textual.getters import app
7
9
  from micro_cc.claude_loop_ import claude_loop, DEFAULT_DANGEROUS, GATEABLE_TOOLS
8
10
  from micro_cc.utils.msg_store_ import load_msgs, rewind_msgs, summarize_and_trim, store_msgs, erase_msgs, erase_summary
9
11
  from micro_cc.utils.tokenization_simple import token_stats
12
+ from micro_cc.utils.helpers import has_configured_endpoint, apply_login, check_ollama
10
13
  from micro_cc.tools.browser_tool_ import close_browser
11
14
  from micro_cc.utils.process_tracker import init as init_process_tracker
12
15
  from micro_cc.cache.redis_cache import RedisStateManager
@@ -52,6 +55,7 @@ class PromptInput(TextArea):
52
55
  ("/copy", "clipboard"),
53
56
  ("/dangerous", "approve"),
54
57
  ("/exit", "quit"),
58
+ ("/login", "endpoint"),
55
59
  ("/model", "switch"),
56
60
  ("/rewind", "undo"),
57
61
  ]
@@ -129,6 +133,20 @@ class MicroApp(App):
129
133
 
130
134
  MODEL_OPTIONS = ["sonnet-5", "opus-4.8", "opus-4.7", "opus-4.6", "sonnet-4.6", "haiku-4.5"]
131
135
 
136
+ LOGIN_SEQUENCE = {
137
+ "Anthropic": ["ANTHROPIC_API_KEY", "OPENAI_API_KEY", "SERPAPI_KEY"],
138
+ "LiteLLM": ["LITELLM_BASE_URL", "LITELLM_API_KEY", "SERPAPI_KEY"],
139
+ "Ollama": ["OLLAMA_MODEL", "SERPAPI_KEY"],
140
+ }
141
+ LOGIN_PROMPTS = {
142
+ "ANTHROPIC_API_KEY": "Paste ANTHROPIC_API_KEY (sk-ant-...)",
143
+ "OPENAI_API_KEY": "Paste OPENAI_API_KEY (embeddings for search_tools)",
144
+ "LITELLM_BASE_URL": "Paste your LiteLLM proxy base URL",
145
+ "LITELLM_API_KEY": "Paste your LITELLM_API_KEY",
146
+ "OLLAMA_MODEL": "Model tag already pulled, e.g. qwen3:14b",
147
+ "SERPAPI_KEY": "SERPAPI_KEY — optional, Enter to skip",
148
+ }
149
+
132
150
  _current_model = "sonnet-4.6"
133
151
  _trim_budget = 80000
134
152
 
@@ -172,7 +190,7 @@ class MicroApp(App):
172
190
 
173
191
  # How to discover what you can style
174
192
 
175
- # Every Textual widget exposes a set of CSS properties.
193
+ # Every Textual widget exposes a set of CSS properties.
176
194
 
177
195
  # For PromptInput (inherits from TextArea):
178
196
  # PromptInput {
@@ -227,6 +245,12 @@ class MicroApp(App):
227
245
  ## "working on…" nudge (above the input) while a query is active
228
246
  self._working_timer = None
229
247
  self._spin_i = 0
248
+ # api keys — _login_stage is None, or the exact env-var name currently
249
+ # being collected (see LOGIN_SEQUENCE); _login_values accumulates
250
+ # {env_var_name: typed_value} across the /login flow
251
+ self._login_stage = None
252
+ self._login_provider = None
253
+ self._login_values = {}
230
254
  init_process_tracker()
231
255
 
232
256
  _pending_approval = None # asyncio.Event when waiting
@@ -234,6 +258,52 @@ class MicroApp(App):
234
258
  _streaming = None # active StreamingRow widget
235
259
  _SPINNER = "⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
236
260
 
261
+ def _handle_login_input(self, query: str) -> None:
262
+ key_name = self._login_stage
263
+ self._login_values[key_name] = query
264
+
265
+ sequence = self.LOGIN_SEQUENCE[self._login_provider]
266
+ next_index = sequence.index(key_name) + 1
267
+
268
+ if next_index < len(sequence):
269
+ self._login_stage = sequence[next_index]
270
+ self.query_one(PromptInput).placeholder = self.LOGIN_PROMPTS[self._login_stage]
271
+ return
272
+
273
+ if self._login_provider == "Ollama":
274
+ model = self._login_values.get("OLLAMA_MODEL", "").strip()
275
+ error = check_ollama(model)
276
+ if error:
277
+ # Don't finish the flow — bounce back to the same field so the
278
+ # user can retype the tag instead of restarting /login.
279
+ self.notify(error, title="micro-cc", severity="error", timeout=8)
280
+ self._login_stage = "OLLAMA_MODEL"
281
+ self.query_one(PromptInput).placeholder = self.LOGIN_PROMPTS["OLLAMA_MODEL"]
282
+ return
283
+ # get_endpoint()/has_configured_endpoint() key off OLLAMA_BASE_URL,
284
+ # not OLLAMA_MODEL — must persist it even though we never asked
285
+ # for it (default localhost, same as the old .env template).
286
+ self._login_values.setdefault("OLLAMA_BASE_URL", "http://localhost:11434/v1")
287
+ # Instance attrs, not class attrs — this app instance already
288
+ # exists, so there's no reason to mutate MicroApp itself (that
289
+ # was only ever done pre-instantiation, in the old start_()).
290
+ self._current_model = model
291
+ self._trim_budget = int(os.getenv("MICRO_CC_TRIM_BUDGET", "16384"))
292
+ # #model-picker was populated once from MODEL_OPTIONS at compose()
293
+ # time — reassigning self.MODEL_OPTIONS now wouldn't touch the
294
+ # already-mounted widget, so rebuild its options directly.
295
+ model_picker = self.query_one("#model-picker")
296
+ model_picker.clear_options()
297
+ model_picker.add_option(Option(model, id=model))
298
+
299
+ apply_login({k: v for k, v in self._login_values.items() if v}) # drop blanks (skipped SERPAPI)
300
+ self.notify(f"{self._login_provider} configured", title="micro-cc")
301
+ self._login_stage = None
302
+ self._login_provider = None
303
+ self._login_values = {}
304
+ self.query_one(PromptInput).placeholder = ""
305
+ self._refresh_status()
306
+
237
307
  def on_paste(self, event) -> None:
238
308
  prompt = self.query_one(PromptInput)
239
309
  if not prompt.has_focus:
@@ -263,6 +333,7 @@ class MicroApp(App):
263
333
  yield OptionList(id="rewind-picker")
264
334
  yield SelectionList(id="dangerous-picker")
265
335
  yield OptionList(id="slash-picker")
336
+ yield OptionList(id="login-picker")
266
337
  yield Static("", id="working") # task nudge, sits just above input
267
338
  yield PromptInput(id="prompt")
268
339
  yield Static("", id="statusbar")
@@ -394,13 +465,26 @@ class MicroApp(App):
394
465
 
395
466
  def action_cancel_query(self):
396
467
  # If a picker is open, ESC just closes it — no cancel
397
- for picker_id in ("#model-picker", "#rewind-picker", "#dangerous-picker", "#slash-picker"):
468
+ for picker_id in ("#model-picker", "#rewind-picker", "#dangerous-picker", "#slash-picker", "#login-picker"):
398
469
  picker = self.query_one(picker_id)
399
470
  if picker.display:
400
471
  picker.display = False
401
472
  self.query_one(PromptInput).focus()
402
473
  return
403
474
 
475
+ # Mid-/login field entry (provider already picked, so the picker
476
+ # above is already closed) — drop the in-progress state. apply_login()
477
+ # only ever runs at the end of a completed sequence, so bailing out
478
+ # here means .env is never touched.
479
+ if self._login_stage is not None:
480
+ self._login_stage = None
481
+ self._login_provider = None
482
+ self._login_values = {}
483
+ prompt = self.query_one(PromptInput)
484
+ prompt.placeholder = ""
485
+ prompt.focus()
486
+ return
487
+
404
488
  self.workers.cancel_all()
405
489
  # Clear pending approval so next submit isn't swallowed
406
490
  if self._pending_approval is not None:
@@ -680,6 +764,7 @@ class MicroApp(App):
680
764
  # exclusive=True flag cancels any previous worker on the same node before starting this one.
681
765
 
682
766
  async def on_prompt_input_submitted(self, event):
767
+
683
768
  # replaces your prompt_toolkit loop
684
769
  query = event.value.strip()
685
770
  query = re.sub(
@@ -689,6 +774,23 @@ class MicroApp(App):
689
774
  )
690
775
  PromptInput._paste_store.clear()
691
776
  PromptInput._paste_id = 0
777
+
778
+ # api keys setting menu
779
+ if self._login_stage is not None:
780
+ self._handle_login_input(query)
781
+ self.query_one(PromptInput).clear()
782
+ return
783
+
784
+ if query.lower() == "/login":
785
+ picker = self.query_one("#login-picker")
786
+ picker.clear_options()
787
+ for name in ("Anthropic", "LiteLLM", "Ollama"):
788
+ picker.add_option(Option(name, id=name))
789
+ picker.display = True
790
+ picker.focus()
791
+ self.query_one(PromptInput).clear()
792
+ return
793
+
692
794
  if self._pending_approval is not None and not self._pending_approval.is_set():
693
795
  self._approval_result = query.lower() in ("", "y", "yes")
694
796
  self._pending_approval.set()
@@ -796,6 +898,14 @@ class MicroApp(App):
796
898
  # Normal query — mount user message row, then run loop
797
899
  await self._mount_row({"type": "user", "content": query})
798
900
  self.query_one(PromptInput).clear()
901
+
902
+ # preflight check
903
+ if not has_configured_endpoint():
904
+ err = {"type": "error", "content": "No API endpoint configured — run /login"}
905
+ await self._mount_row(err)
906
+ self.query_one(PromptInput).clear()
907
+ return
908
+
799
909
  self._query_active = True
800
910
  self.run_worker(self.do_query(query), exclusive=True)
801
911
 
@@ -811,6 +921,15 @@ class MicroApp(App):
811
921
  prompt.focus()
812
922
  prompt.post_message(PromptInput.Submitted(prompt))
813
923
 
924
+ elif picker_id == "login-picker":
925
+ self._login_provider = str(event.option.prompt)
926
+ self._login_stage = self.LOGIN_SEQUENCE[str(event.option.prompt)][0]
927
+ self.query_one("#login-picker").display = False
928
+ prompt = self.query_one(PromptInput)
929
+ prompt.placeholder = self.LOGIN_PROMPTS[self._login_stage]
930
+ self._refresh_status()
931
+ self.query_one(PromptInput).focus()
932
+
814
933
  elif picker_id == "model-picker":
815
934
  self._current_model = str(event.option.prompt)
816
935
  self.query_one("#model-picker").display = False
@@ -1013,8 +1132,6 @@ class MicroApp(App):
1013
1132
  self._pending_approval = None
1014
1133
  self._finalize_streaming()
1015
1134
 
1016
- # self.query_one(PromptInput).disabled = False ## now we are queueing all user msgs
1017
-
1018
1135
  self._set_busy(False)
1019
1136
  self.query_one(PromptInput).focus()
1020
1137
  self._refresh_status()
@@ -1040,154 +1157,10 @@ class MicroApp(App):
1040
1157
  self._state_mgr.stop_cleanup_task()
1041
1158
 
1042
1159
 
1043
- OLLAMA_ENV_BLOCK = """\
1044
- # OR Option 3 Ollama (local open-weight main loop)
1045
- # Install: brew install ollama && brew services start ollama
1046
- # Pull a model — pick one that fits your RAM:
1047
- # ollama pull qwen3:4b (2.5GB, any Mac — smoke test baseline)
1048
- # ollama pull qwen3:8b (5.2GB, good on 16GB+)
1049
- # ollama pull qwen3:14b (9.3GB, needs 24GB+ comfortably)
1050
- # ollama pull qwen3-coder:30b (19GB MoE, needs 36GB+, best coder)
1051
- # Then uncomment below and set OLLAMA_MODEL to the tag you pulled.
1052
- # OLLAMA_BASE_URL=http://localhost:11434/v1
1053
- # OLLAMA_MODEL=qwen3:4b
1054
- #
1055
- # Context budget — local models are much smaller than Sonnet (200K).
1056
- # KV cache grows linearly with context. Smaller model = more headroom.
1057
- # qwen3:4b → NUM_CTX=16384, TRIM=12288 (safe everywhere)
1058
- # qwen3:8b → NUM_CTX=24576, TRIM=20480
1059
- # qwen3:14b → NUM_CTX=32768, TRIM=28000 (36GB Macs only)
1060
- # OLLAMA_NUM_CTX=16384
1061
- # OLLAMA_MAX_OUTPUT=4096
1062
- # MICRO_CC_TRIM_BUDGET=12288
1063
- #
1064
- # IMPORTANT — Ollama handles only the main reasoning loop. Vision, web
1065
- # search, and semantic tool discovery still hit cloud APIs. You must keep
1066
- # OPENAI_API_KEY set even when using Ollama:
1067
- # - search_tools uses embeddings (OpenAI text-embedding-3-small)
1068
- # - vision tool disabled on Ollama
1069
- # OPENAI_API_KEY=sk-proj-...
1070
- """
1071
-
1072
-
1073
- ENV_EXAMPLE = f"""\
1074
- # Choose one only:
1075
- # Choose which to use uncomment by removing # and add API keys
1076
-
1077
- # Option 1 Anthropic for models + OpenAI for embeddings
1078
- # ANTHROPIC_API_KEY=sk-ant-
1079
- # OPENAI_API_KEY=sk-proj-
1080
-
1081
- # OR Option 2 if using LiteLLM proxy for Bedrock Anthropic
1082
- # LITELLM_BASE_URL=https://your-proxy.com
1083
- # LITELLM_API_KEY=sk-...
1084
-
1085
- {OLLAMA_ENV_BLOCK}
1086
- # Shared always needed for web-search
1087
- # SERPAPI_KEY=
1088
- """
1089
-
1090
-
1091
1160
  from importlib.metadata import version as _pkg_version
1092
1161
  __version__ = _pkg_version("micro-cc")
1093
1162
 
1094
-
1095
- def _ensure_home_dir():
1096
- """Create ~/.micro-cc with example .env on first run.
1097
-
1098
- For existing installs, append new option blocks (e.g. Ollama) to the
1099
- user's .env non-destructively — we only append if the sentinel string
1100
- is absent. Their existing keys and customizations are untouched.
1101
- """
1102
- home = os.path.expanduser("~/.micro-cc")
1103
- os.makedirs(os.path.join(home, "projects"), exist_ok=True)
1104
- env_path = os.path.join(home, ".env")
1105
-
1106
- if not os.path.exists(env_path):
1107
- with open(env_path, "w") as f:
1108
- f.write(ENV_EXAMPLE)
1109
- print(f"\n Created {env_path} — add your API keys before starting.\n")
1110
- sys.exit(0)
1111
-
1112
- # Upgrade path — existing .env, append new option blocks if missing.
1113
- with open(env_path) as f:
1114
- current = f.read()
1115
-
1116
- if "OLLAMA_BASE_URL" not in current:
1117
- with open(env_path, "a") as f:
1118
- if not current.endswith("\n"):
1119
- f.write("\n")
1120
- f.write("\n" + OLLAMA_ENV_BLOCK)
1121
- print(f"\n ✓ micro-cc added a new option to {env_path}")
1122
- print(" Option 3: Ollama (local open-weight models). Your existing")
1123
- print(" config is untouched — open the file to review the new block.\n")
1124
-
1125
-
1126
- def _preflight_ollama():
1127
- """Validate Ollama setup when OLLAMA_BASE_URL is set.
1128
-
1129
- Checks: OLLAMA_MODEL set, daemon reachable, model pulled.
1130
- Prints friendly onboarding on failure, exits cleanly.
1131
- Returns (model, trim_budget) for the caller to apply to MicroApp.
1132
- """
1133
- import urllib.request
1134
- import json as _json
1135
-
1136
- base_url = os.getenv("OLLAMA_BASE_URL", "http://localhost:11434/v1")
1137
- tags_url = base_url.rstrip("/").removesuffix("/v1") + "/api/tags"
1138
- model = os.getenv("OLLAMA_MODEL")
1139
-
1140
- if not model:
1141
- print("\n[micro-cc] Ollama selected but OLLAMA_MODEL is not set.")
1142
- print("[micro-cc] Edit ~/.micro-cc/.env and set e.g. OLLAMA_MODEL=qwen3:14b\n")
1143
- sys.exit(1)
1144
-
1145
- try:
1146
- with urllib.request.urlopen(tags_url, timeout=2) as resp:
1147
- data = _json.loads(resp.read())
1148
- except Exception:
1149
- print(f"\n[micro-cc] Ollama daemon not reachable at {base_url}")
1150
- print("[micro-cc] Install and start Ollama:")
1151
- print(" brew install ollama")
1152
- print(" brew services start ollama # auto-starts on login, runs in background")
1153
- print("[micro-cc] Then pull the model you want:")
1154
- print(f" ollama pull {model}\n")
1155
- sys.exit(1)
1156
-
1157
- tags = [m.get("name", "") for m in data.get("models", [])]
1158
- if model not in tags:
1159
- print(f"\n[micro-cc] Ollama model '{model}' is not pulled on this machine.")
1160
- print(f"[micro-cc] Run: ollama pull {model}")
1161
- if tags:
1162
- print("[micro-cc] Currently pulled:")
1163
- for t in tags:
1164
- print(f" {t}")
1165
- print()
1166
- sys.exit(1)
1167
-
1168
- num_ctx = int(os.getenv("OLLAMA_NUM_CTX", "32768"))
1169
- max_out = int(os.getenv("OLLAMA_MAX_OUTPUT", "4096"))
1170
- trim = int(os.getenv("MICRO_CC_TRIM_BUDGET", str(max(num_ctx - max_out, 4096))))
1171
-
1172
- print(f"\n[micro-cc] ✓ Ollama ready — model={model}")
1173
- print(f"[micro-cc] num_ctx={num_ctx} trim_budget={trim} max_output={max_out}")
1174
- print("[micro-cc] Local models have much smaller context than Sonnet (200K).")
1175
- print("[micro-cc] Qwen3 native is 40K — we reserve 32K input + 4K output so KV cache")
1176
- print("[micro-cc] fits in RAM. Context will fill faster. Override via OLLAMA_NUM_CTX /")
1177
- print("[micro-cc] MICRO_CC_TRIM_BUDGET in ~/.micro-cc/.env.\n")
1178
-
1179
- return model, trim
1180
-
1181
-
1182
1163
  def start_():
1183
- _ensure_home_dir()
1184
-
1185
- from micro_cc.utils.helpers import get_endpoint
1186
- if get_endpoint() == "Ollama":
1187
- ollama_model, ollama_trim = _preflight_ollama()
1188
- MicroApp._current_model = ollama_model
1189
- MicroApp.MODEL_OPTIONS = [ollama_model]
1190
- MicroApp._trim_budget = ollama_trim
1191
1164
 
1192
1165
  if len(sys.argv) > 1:
1193
1166
  project_dir = os.path.abspath(sys.argv[1])
@@ -0,0 +1,133 @@
1
+ from dotenv import load_dotenv
2
+ from PIL import Image
3
+ import tiktoken
4
+ import base64
5
+ import json
6
+ import os
7
+ import io
8
+ import sys
9
+
10
+
11
+ ############################################################################################################
12
+
13
+ load_dotenv(os.path.expanduser("~/.micro-cc/.env"))
14
+ load_dotenv()
15
+
16
+ ############################################################################################################
17
+
18
+ def get_endpoint() -> str:
19
+ """Infer endpoint from env vars. Ollama > Anthropic > LiteLLM (explicit local wins)."""
20
+ if os.getenv("OLLAMA_BASE_URL"):
21
+ return "Ollama"
22
+ if os.getenv("ANTHROPIC_API_KEY"):
23
+ return "Anthropic"
24
+ if os.getenv("LITELLM_BASE_URL") and os.getenv("LITELLM_API_KEY"):
25
+ return "LiteLLM"
26
+ return "Anthropic"
27
+
28
+ ############################################################################################################
29
+
30
+
31
+ def has_configured_endpoint() -> bool:
32
+ return bool(
33
+ os.getenv("ANTHROPIC_API_KEY")
34
+ or (os.getenv("LITELLM_BASE_URL") and os.getenv("LITELLM_API_KEY"))
35
+ or os.getenv("OLLAMA_BASE_URL")
36
+ )
37
+
38
+
39
+ ############################################################################################################
40
+
41
+ _ROUTING_KEYS = ["OLLAMA_BASE_URL", "ANTHROPIC_API_KEY", "LITELLM_BASE_URL", "LITELLM_API_KEY"]
42
+
43
+
44
+ def apply_login(values: dict):
45
+ """Recreate ~/.micro-cc/.env from `values` — /login always sends the
46
+ full field set for the chosen provider, so each login replaces the file
47
+ instead of accumulating stale keys from a previous provider."""
48
+ for key in _ROUTING_KEYS:
49
+ os.environ.pop(key, None) # clear this session's env, not just the file
50
+ os.environ.update(values)
51
+
52
+ env_path = os.path.expanduser("~/.micro-cc/.env")
53
+ with open(env_path, "w") as f:
54
+ for k, v in values.items():
55
+ f.write(f"{k}={v}\n")
56
+
57
+
58
+ ############################################################################################################
59
+ ##tokenizer
60
+ # tiktoken.get_encoding() parses a ~1.6MB BPE rank file (~2s) — deferred to
61
+ # first actual use instead of import time. Callers use get_tokenizer();
62
+ # start_live_.py also warms this in a background thread right after the app
63
+ # mounts, so it's usually ready before the first message needs it.
64
+
65
+ _tokenizer = None
66
+
67
+
68
+ def get_tokenizer():
69
+ global _tokenizer
70
+ if _tokenizer is None:
71
+ _tokenizer = tiktoken.get_encoding("cl100k_base")
72
+ return _tokenizer
73
+
74
+ ############################################################################################################
75
+ ##dirs
76
+
77
+ WORK_FOLDER = os.path.join(os.getcwd(), "workspace/")
78
+
79
+ ############################################################################################################
80
+
81
+
82
+ def sanitize_and_encode_image_(img_data):
83
+ try:
84
+ if isinstance(img_data, str) and os.path.exists(img_data):
85
+ with Image.open(img_data) as img:
86
+ img = img.convert("RGB")
87
+ buffer = io.BytesIO()
88
+ img.save(buffer, format="JPEG")
89
+ return base64.b64encode(buffer.getvalue()).decode("utf-8")
90
+ else:
91
+ with Image.open(io.BytesIO(img_data)) as img:
92
+ img = img.convert("RGB")
93
+ buffer = io.BytesIO()
94
+ img.save(buffer, format="JPEG")
95
+ return base64.b64encode(buffer.getvalue()).decode("utf-8")
96
+ except Exception as e:
97
+ print(f"Image encoding error: {e}")
98
+ return None
99
+
100
+ #################################
101
+
102
+
103
+ def check_ollama(model: str, base_url: str = None) -> str:
104
+ """Validate Ollama daemon reachable and model pulled.
105
+
106
+ Non-fatal — called from inside /login while the TUI is already running,
107
+ so it returns a message instead of printing+sys.exit like a CLI preflight
108
+ would. Empty string means OK.
109
+ """
110
+ import urllib.request
111
+ import json as _json
112
+
113
+ if not model:
114
+ return "Enter a model tag, e.g. qwen3:14b"
115
+
116
+ base_url = base_url or os.getenv("OLLAMA_BASE_URL", "http://localhost:11434/v1")
117
+ tags_url = base_url.rstrip("/").removesuffix("/v1") + "/api/tags"
118
+
119
+ try:
120
+ with urllib.request.urlopen(tags_url, timeout=2) as resp:
121
+ data = _json.loads(resp.read())
122
+ except Exception:
123
+ return (
124
+ f"Ollama daemon not reachable at {base_url}. "
125
+ f"Run: brew install ollama && brew services start ollama, then: ollama pull {model}"
126
+ )
127
+
128
+ tags = [m.get("name", "") for m in data.get("models", [])]
129
+ if model not in tags:
130
+ pulled = ", ".join(tags) if tags else "none"
131
+ return f"Model '{model}' not pulled — run: ollama pull {model} (pulled: {pulled})"
132
+
133
+ return ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: micro-cc
3
- Version: 0.2.30
3
+ Version: 0.2.32
4
4
  Summary: Harness that gives frontier models full system access — shell, filesystem, browser, MCP — running directly on the metal.
5
5
  Author: George Salapa
6
6
  License-Expression: MIT
@@ -21,7 +21,6 @@ Requires-Dist: mcp>=1.26
21
21
  Requires-Dist: numpy>=2.4
22
22
  Requires-Dist: openai>=2.16
23
23
  Requires-Dist: openpyxl>=3.1
24
- Requires-Dist: pandas>=3.0
25
24
  Requires-Dist: pathvalidate>=3.3
26
25
  Requires-Dist: pdfminer.six>=20260107
27
26
  Requires-Dist: pillow>=12.0
@@ -60,8 +59,6 @@ Frontier models with full system access — shell, filesystem, browser, MCP —
60
59
 
61
60
  ## Install
62
61
 
63
- Please use `iTerm2`. Highly recommended terminal UI.
64
-
65
62
  ```bash
66
63
  pip install micro-cc
67
64
  ```
@@ -154,5 +154,4 @@ src/micro_cc/utils/helpers.py
154
154
  src/micro_cc/utils/memory_store_.py
155
155
  src/micro_cc/utils/msg_store_.py
156
156
  src/micro_cc/utils/process_tracker.py
157
- src/micro_cc/utils/tokenization_simple.py
158
- src/micro_cc/utils/utils.py
157
+ src/micro_cc/utils/tokenization_simple.py
@@ -13,7 +13,6 @@ mcp>=1.26
13
13
  numpy>=2.4
14
14
  openai>=2.16
15
15
  openpyxl>=3.1
16
- pandas>=3.0
17
16
  pathvalidate>=3.3
18
17
  pdfminer.six>=20260107
19
18
  pillow>=12.0