micro-cc 0.2.85__tar.gz → 0.2.87__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.85/src/micro_cc.egg-info → micro_cc-0.2.87}/PKG-INFO +1 -1
  2. {micro_cc-0.2.85 → micro_cc-0.2.87}/pyproject.toml +1 -1
  3. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/claude_loop_.py +1 -1
  4. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/postgres_store/pg_store_.py +24 -0
  5. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/postgres_store/schema_.py +17 -0
  6. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/start_live_tui_.py +79 -10
  7. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_.py +1 -1
  8. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/alt_screen_.py +47 -2
  9. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/diff_viewer_.py +8 -8
  10. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/etype_handler_.py +4 -4
  11. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/list_picker_.py +3 -3
  12. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/md_render_.py +39 -1
  13. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/message_row_.py +15 -6
  14. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/multi_select_list_.py +3 -3
  15. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/question_panel_.py +3 -3
  16. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/screen_cmds_.py +18 -3
  17. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/text_.py +7 -2
  18. micro_cc-0.2.87/src/micro_cc/tui_native/theme_.py +54 -0
  19. micro_cc-0.2.87/src/micro_cc/utils/banner_.py +55 -0
  20. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/command_registry.py +1 -0
  21. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/hidden_prompts.py +17 -8
  22. micro_cc-0.2.87/src/micro_cc/utils/hints.py +43 -0
  23. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/status_marker.py +13 -1
  24. micro_cc-0.2.87/src/micro_cc/utils/theme_store_.py +249 -0
  25. {micro_cc-0.2.85 → micro_cc-0.2.87/src/micro_cc.egg-info}/PKG-INFO +1 -1
  26. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/SOURCES.txt +1 -0
  27. micro_cc-0.2.85/src/micro_cc/tui_native/theme_.py +0 -33
  28. micro_cc-0.2.85/src/micro_cc/utils/banner_.py +0 -41
  29. micro_cc-0.2.85/src/micro_cc/utils/hints.py +0 -64
  30. {micro_cc-0.2.85 → micro_cc-0.2.87}/LICENSE +0 -0
  31. {micro_cc-0.2.85 → micro_cc-0.2.87}/README.md +0 -0
  32. {micro_cc-0.2.85 → micro_cc-0.2.87}/setup.cfg +0 -0
  33. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/__init__.py +0 -0
  34. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/__init__.py +0 -0
  35. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/_cookies.py +0 -0
  36. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/_md_convert.py +0 -0
  37. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/browser_manager.py +0 -0
  38. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/browser/simpletextbrowser.py +0 -0
  39. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/cache/__init__.py +0 -0
  40. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/cache/state_store.py +0 -0
  41. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/execute_tool.py +0 -0
  42. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/__init__.py +0 -0
  43. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/__init__.py +0 -0
  44. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/anthropic.py +0 -0
  45. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/chat.py +0 -0
  46. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/adapters/responses.py +0 -0
  47. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/anthropic.py +0 -0
  48. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/client.py +0 -0
  49. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/foundry.py +0 -0
  50. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/litellm.py +0 -0
  51. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/ollama.py +0 -0
  52. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/openai.py +0 -0
  53. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/openrouter.py +0 -0
  54. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/priming.py +0 -0
  55. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/registry.py +0 -0
  56. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/schema.py +0 -0
  57. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/models/types.py +0 -0
  58. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/postgres_store/__init__.py +0 -0
  59. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/self_heal_.py +0 -0
  60. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/SKILL.md +0 -0
  61. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-OFL.txt +0 -0
  62. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  63. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  64. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-OFL.txt +0 -0
  65. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  66. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Boldonse-OFL.txt +0 -0
  67. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  68. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  69. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -0
  70. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  71. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  72. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  73. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-OFL.txt +0 -0
  74. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  75. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/DMMono-OFL.txt +0 -0
  76. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/DMMono-Regular.ttf +0 -0
  77. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/EricaOne-OFL.txt +0 -0
  78. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  79. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  80. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/GeistMono-OFL.txt +0 -0
  81. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  82. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Gloock-OFL.txt +0 -0
  83. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Gloock-Regular.ttf +0 -0
  84. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  85. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-OFL.txt +0 -0
  86. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  87. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  88. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  89. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  90. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  91. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  92. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  93. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  94. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-OFL.txt +0 -0
  95. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  96. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  97. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  98. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Italiana-OFL.txt +0 -0
  99. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Italiana-Regular.ttf +0 -0
  100. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  101. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-OFL.txt +0 -0
  102. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  103. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Jura-Light.ttf +0 -0
  104. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Jura-Medium.ttf +0 -0
  105. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Jura-OFL.txt +0 -0
  106. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-OFL.txt +0 -0
  107. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  108. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-Bold.ttf +0 -0
  109. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  110. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-Italic.ttf +0 -0
  111. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-OFL.txt +0 -0
  112. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Lora-Regular.ttf +0 -0
  113. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  114. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NationalPark-OFL.txt +0 -0
  115. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  116. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -0
  117. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  118. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Outfit-Bold.ttf +0 -0
  119. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Outfit-OFL.txt +0 -0
  120. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Outfit-Regular.ttf +0 -0
  121. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  122. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-OFL.txt +0 -0
  123. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-OFL.txt +0 -0
  124. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  125. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  126. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-OFL.txt +0 -0
  127. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  128. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-OFL.txt +0 -0
  129. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  130. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  131. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-OFL.txt +0 -0
  132. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Tektur-Medium.ttf +0 -0
  133. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Tektur-OFL.txt +0 -0
  134. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/Tektur-Regular.ttf +0 -0
  135. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  136. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  137. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  138. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-OFL.txt +0 -0
  139. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  140. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-OFL.txt +0 -0
  141. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  142. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/design/references/blueprint-tokens.md +0 -0
  143. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/github/SKILL.md +0 -0
  144. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/graph-orchestration/SKILL.md +0 -0
  145. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/SKILL.md +0 -0
  146. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/brief.md +0 -0
  147. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/build_deck.js +0 -0
  148. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/deck_to_images.py +0 -0
  149. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/office/__init__.py +0 -0
  150. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/office/soffice.py +0 -0
  151. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/scripts/render_svg.py +0 -0
  152. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/pptx/svg-slides.md +0 -0
  153. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/skill_loader.py +0 -0
  154. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/REFERENCE.md +0 -0
  155. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/SKILL.md +0 -0
  156. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/scripts/design_graph.py +0 -0
  157. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/system-design-review/scripts/design_review.py +0 -0
  158. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/skills/xlsx/SKILL.md +0 -0
  159. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/start_headless_.py +0 -0
  160. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/__init__.py +0 -0
  161. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/ask_user_tool.py +0 -0
  162. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/bash_tool.py +0 -0
  163. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/browser_tool_.py +0 -0
  164. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/computer_tool_.py +0 -0
  165. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/file_tools_.py +0 -0
  166. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/mcp_client_.py +0 -0
  167. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/memory_tool_.py +0 -0
  168. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/message_session_.py +0 -0
  169. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_schedule_.py +0 -0
  170. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_schedule_runtime.py +0 -0
  171. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/monitor_watch_.py +0 -0
  172. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/search_history_tool_.py +0 -0
  173. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/search_tool_.py +0 -0
  174. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/skill_tools_.py +0 -0
  175. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/todo_tools_.py +0 -0
  176. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/vision_tools_.py +0 -0
  177. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tools/web_tools_.py +0 -0
  178. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/__init__.py +0 -0
  179. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/detect_images_.py +0 -0
  180. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/keys_.py +0 -0
  181. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/kill_ring_.py +0 -0
  182. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/prompt_input_.py +0 -0
  183. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/scroll_view_.py +0 -0
  184. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/self_terminal_setup_.py +0 -0
  185. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/self_update_.py +0 -0
  186. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/stack_.py +0 -0
  187. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/stdin_buffer_.py +0 -0
  188. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/terminal_.py +0 -0
  189. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/text_utils_.py +0 -0
  190. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/truncated_text_.py +0 -0
  191. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/undo_stack_.py +0 -0
  192. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/tui_native/word_navigation_.py +0 -0
  193. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/__init__.py +0 -0
  194. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/annotate_.py +0 -0
  195. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/claude_md_loader.py +0 -0
  196. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/claude_subscription.py +0 -0
  197. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/default_status_sh.py +0 -0
  198. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/helpers.py +0 -0
  199. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/history_mount_.py +0 -0
  200. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/inbox_store_.py +0 -0
  201. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/memory_store_.py +0 -0
  202. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/msg_normalize_.py +0 -0
  203. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/msg_store_.py +0 -0
  204. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/project_files_.py +0 -0
  205. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/self_reload_.py +0 -0
  206. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/session_ipc_.py +0 -0
  207. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/settings_store_.py +0 -0
  208. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/subagent_tracker_.py +0 -0
  209. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/terminal_setup.py +0 -0
  210. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/tokenization_simple.py +0 -0
  211. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/tool_limits.py +0 -0
  212. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/utils/tool_result_storage.py +0 -0
  213. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/__init__.py +0 -0
  214. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/bridge.py +0 -0
  215. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/dist/assets/index-9B5FcRm2.css +0 -0
  216. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/dist/assets/index-CDG9HO3B.js +0 -0
  217. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/dist/index.html +0 -0
  218. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/history.py +0 -0
  219. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/launch.py +0 -0
  220. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/server.py +0 -0
  221. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc/webui/session.py +0 -0
  222. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/dependency_links.txt +0 -0
  223. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/entry_points.txt +0 -0
  224. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/requires.txt +0 -0
  225. {micro_cc-0.2.85 → micro_cc-0.2.87}/src/micro_cc.egg-info/top_level.txt +0 -0
  226. {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_model_consumers.py +0 -0
  227. {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_models.py +0 -0
  228. {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_schedule.py +0 -0
  229. {micro_cc-0.2.85 → micro_cc-0.2.87}/tests/test_self_reload.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: micro-cc
3
- Version: 0.2.85
3
+ Version: 0.2.87
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: MIT License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "micro-cc"
7
- version = "0.2.85"
7
+ version = "0.2.87"
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"
@@ -232,7 +232,7 @@ async def claude_loop(
232
232
  - You are running LOCALLY on the user's machine (on the metal), NOT a remote server
233
233
  - You have direct access to the local filesystem, Desktop, Documents, etc.
234
234
  - Project directory: {project_dir}
235
- - Your own source code lives at: {package_dir} (not project_dir)
235
+ - Your own source code lives at: {package_dir} (not project_dir) — plain, editable .py. You can change the harness itself: edit it and the running process restarts into your change (auto at the next idle turn, or /reload to force it now); the conversation persists across the restart.
236
236
  - Date: {datetime.datetime.now().strftime("%B %d, %Y")}
237
237
 
238
238
  ## Core Tools (always available)
@@ -470,6 +470,30 @@ def load_settings() -> dict | None:
470
470
  return row[0] if row else None
471
471
 
472
472
 
473
+ def load_theme() -> dict | None:
474
+ """None on a missing row — same "no row yet" contract as load_settings,
475
+ so theme_store_._load() can tell "never written" apart from "written"."""
476
+ with _connect() as conn, conn.cursor() as cur:
477
+ cur.execute("SELECT data FROM micro_cc_theme WHERE id = 'global'")
478
+ row = cur.fetchone()
479
+ return row[0] if row else None
480
+
481
+
482
+ def save_theme(store: dict) -> None:
483
+ """Whole-dict upsert, mirroring save_settings — a look is edited by a
484
+ human via /theme, not a hot path."""
485
+ with _connect() as conn, conn.cursor() as cur:
486
+ cur.execute(
487
+ """
488
+ INSERT INTO micro_cc_theme (id, data, updated_at)
489
+ VALUES ('global', %s, now())
490
+ ON CONFLICT (id) DO UPDATE
491
+ SET data = EXCLUDED.data, updated_at = now()
492
+ """,
493
+ (json.dumps(store),),
494
+ )
495
+
496
+
473
497
  def save_settings(store: dict) -> None:
474
498
  """Whole-dict upsert — settings are edited by a human via the TUI/webui,
475
499
  not a per-message hot path, so there's no append/lock story to match
@@ -5,6 +5,7 @@ _sessions_table_ready = False
5
5
  _messages_table_ready = False
6
6
  _memory_table_ready = False
7
7
  _settings_table_ready = False
8
+ _theme_table_ready = False
8
9
 
9
10
 
10
11
  def _ensure_sessions_table(conn) -> None:
@@ -102,8 +103,24 @@ def _ensure_settings_table(conn) -> None:
102
103
  _settings_table_ready = True
103
104
 
104
105
 
106
+ def _ensure_theme_table(conn) -> None:
107
+ global _theme_table_ready
108
+ if _theme_table_ready:
109
+ return
110
+ with conn.cursor() as cur:
111
+ cur.execute("""
112
+ CREATE TABLE IF NOT EXISTS micro_cc_theme (
113
+ id TEXT PRIMARY KEY DEFAULT 'global',
114
+ data JSONB NOT NULL,
115
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
116
+ )
117
+ """)
118
+ _theme_table_ready = True
119
+
120
+
105
121
  def ensure_all(conn) -> None:
106
122
  _ensure_sessions_table(conn)
107
123
  _ensure_messages_table(conn)
108
124
  _ensure_memory_table(conn)
109
125
  _ensure_settings_table(conn)
126
+ _ensure_theme_table(conn)
@@ -27,7 +27,12 @@ from micro_cc.utils.helpers import (
27
27
  sanitize_and_encode_image_,
28
28
  )
29
29
  from micro_cc.cache import state_store
30
- from micro_cc.utils.banner_ import BANNER, BANNER_COLOR
30
+ from micro_cc.utils import banner_
31
+ from micro_cc.utils import theme_store_
32
+
33
+ # Theme tokens used inline in markup strings are read via theme_store_.get()
34
+ # right where they're interpolated — never bound to a module constant, which
35
+ # would freeze the palette at import and make a live /theme switch a no-op.
31
36
  from micro_cc.tui_native.screen_cmds_ import SLASH_COMMANDS, PREFIX_COMMANDS, _pending_subagents
32
37
  from micro_cc.utils import command_registry
33
38
  from micro_cc.tui_native.self_update_ import check_and_update
@@ -417,7 +422,10 @@ class MicroTui():
417
422
 
418
423
  # --- widget tree -----------------------------------------------------
419
424
  def _build_widget_tree(self) -> None:
420
- self.banner = RichStatic(BANNER)
425
+ # banner() returns (markup, hue); the hue is kept so the hint bar
426
+ # can tint to match this launch's wordmark.
427
+ _banner_markup, self._banner_hue = banner_.banner()
428
+ self.banner = RichStatic(_banner_markup)
421
429
 
422
430
  self.prompt = PromptInput()
423
431
  # handle_key("enter") calls on_submit synchronously (no await) —
@@ -532,6 +540,11 @@ class MicroTui():
532
540
  self.ask_user_picker = ListPicker()
533
541
  self.ask_user_select = MultiSelectList()
534
542
  self.bgproc_detail_picker = ListPicker()
543
+ self.theme_picker = ListPicker()
544
+ self.theme_picker.set_items([
545
+ PickerItem(n, theme_store_.THEME_LABELS.get(n, n))
546
+ for n in theme_store_.PRESETS
547
+ ])
535
548
 
536
549
  self.model_picker.on_select = self._on_model_picked
537
550
  self.model_picker.on_cancel = lambda: self._close_picker("_model_picker_overlay")
@@ -552,6 +565,8 @@ class MicroTui():
552
565
  # _show_bgproc_detail/ctrl+b).
553
566
  self.bgproc_detail_picker.on_select = lambda item: self._hide_bgproc_detail()
554
567
  self.bgproc_detail_picker.on_cancel = self._hide_bgproc_detail
568
+ self.theme_picker.on_select = self._on_theme_picked
569
+ self.theme_picker.on_cancel = lambda: self._close_picker("_theme_picker_overlay")
555
570
 
556
571
  self._model_picker_overlay = None
557
572
  self._rewind_picker_overlay = None
@@ -559,6 +574,7 @@ class MicroTui():
559
574
  self._slash_picker_overlay = None
560
575
  self._at_picker_overlay = None
561
576
  self._login_picker_overlay = None
577
+ self._theme_picker_overlay = None
562
578
 
563
579
  # --- render loop / focus ---------------------------------------------
564
580
  # Used everywhere below — foundational, not tied to any one feature.
@@ -1056,6 +1072,54 @@ class MicroTui():
1056
1072
  self._refresh_status()
1057
1073
  self.set_focus(self.prompt)
1058
1074
 
1075
+ def _on_theme_picked(self, item: PickerItem) -> None:
1076
+ """Apply a preset. theme_store_.set_preset persists the color set and
1077
+ fires the on_change listeners — _apply_theme is the one registered
1078
+ here, which re-emits the terminal's default bg/fg and invalidates the
1079
+ render caches so every row repaints in the new set."""
1080
+ self._close_picker("_theme_picker_overlay")
1081
+ msg = theme_store_.set_preset(item.value)
1082
+ self._flash_status(msg, seconds=3)
1083
+ self.set_focus(self.prompt)
1084
+
1085
+ def _apply_theme(self, _name: str = "") -> None:
1086
+ """Repaint an already-running UI after the color set changed.
1087
+
1088
+ Two halves, and both are needed: (1) the terminal's own default
1089
+ bg/fg must be re-emitted, because OSC 11/10 only ran once at
1090
+ alt-screen entry — without this the ground stays whatever it was;
1091
+ (2) every cached render must be dropped, because widgets cache their
1092
+ flattened lines per width (message_row_.RichStatic/MessageRow,
1093
+ prompt_input_, the pickers) and would otherwise keep serving lines
1094
+ painted in the old palette until something else invalidated them.
1095
+ """
1096
+ from micro_cc.tui_native.alt_screen_ import default_colors_sequence
1097
+
1098
+ sys.stdout.write(default_colors_sequence())
1099
+ sys.stdout.flush()
1100
+
1101
+ # The splash is a RichStatic built from a theme-derived string, so it
1102
+ # has to be rebuilt, not just invalidated — invalidate() would repaint
1103
+ # the SAME stale markup in the old palette. banner() also hands back
1104
+ # the hue it chose, which the hint bar tints to match.
1105
+ banner_markup, hue = banner_.banner()
1106
+ self._banner_hue = hue
1107
+ self.banner.update(banner_markup)
1108
+ self.banner.invalidate()
1109
+ for entry in self.message_list.entries:
1110
+ entry.component.invalidate()
1111
+ for entry in self.subagent_message_list.entries:
1112
+ entry.component.invalidate()
1113
+ for widget in (
1114
+ self.working, self.ask_question_text, self.memory_flash,
1115
+ self.static_hintbar, self.statusbar, self.subagent_status,
1116
+ self.bgproc_status, self.prompt,
1117
+ ):
1118
+ widget.invalidate()
1119
+ self.tui.invalidate_overlays()
1120
+ self._static_hint_text()
1121
+ self.request_render()
1122
+
1059
1123
  def _on_rewind_picked(self, item: PickerItem) -> None:
1060
1124
  self._close_picker("_rewind_picker_overlay")
1061
1125
  self._safe_task(self._do_rewind(int(item.value)), "rewind")
@@ -1522,11 +1586,11 @@ class MicroTui():
1522
1586
  # A self-change is queued but the app hasn't restarted yet (mid-turn
1523
1587
  # or a subagent still live) — say so, so the delay is legible rather
1524
1588
  # than looking like nothing happened.
1525
- text += f" | [#ff69b4]{self._self_reload_hint()}[/#ff69b4]"
1589
+ text += f" | [{theme_store_.get('accent')}]{self._self_reload_hint()}[/{theme_store_.get('accent')}]"
1526
1590
  elif self._update_available:
1527
- text += f" | [#ff69b4]⚠ micro-cc {self._update_available} available — run /update[/#ff69b4]"
1591
+ text += f" | [{theme_store_.get('accent')}]⚠ micro-cc {self._update_available} available — run /update[/{theme_store_.get('accent')}]"
1528
1592
  elif settings_store_.was_reset_for_corruption():
1529
- text += " | [red]⚠ ~/.micro-cc/settings.json was corrupt — reset to defaults[/red]"
1593
+ text += f" | [{theme_store_.get('error')}]⚠ ~/.micro-cc/settings.json was corrupt — reset to defaults[/{theme_store_.get('error')}]"
1530
1594
  else:
1531
1595
  # newline_hint_text() returns None once the user has actually
1532
1596
  # used a newline once (backslash+enter/shift+enter/ctrl+j) — no
@@ -1535,7 +1599,8 @@ class MicroTui():
1535
1599
  # Tinted with the same colour as this launch's banner, so the
1536
1600
  # hint reads as "micro-cc talking" rather than dim status text.
1537
1601
  hint = newline_hint_text() or self.get_random_hint()
1538
- text += f" | [{BANNER_COLOR}]{hint}[/{BANNER_COLOR}]"
1602
+ hue = self._banner_hue
1603
+ text += f" | [{hue}]{hint}[/{hue}]"
1539
1604
  self.static_hintbar.update(
1540
1605
  text[: self._STATIC_HINT_LINE_MAXLEN]
1541
1606
  )
@@ -1629,7 +1694,7 @@ class MicroTui():
1629
1694
  # ever got a chance to render. _flash_status's own timer is what
1630
1695
  # brings the normal text back after `seconds`.
1631
1696
  self._flash_status(
1632
- "[bold yellow]✂ compacted past conversation just now[/bold yellow]",
1697
+ f"[bold {theme_store_.get('warn')}]✂ compacted past conversation just now[/bold {theme_store_.get('warn')}]",
1633
1698
  seconds=8,
1634
1699
  )
1635
1700
  return
@@ -1825,7 +1890,7 @@ class MicroTui():
1825
1890
  # this used to be a second, longer copy that could (and did) drift out
1826
1891
  # of sync with the pending hint shown in the bar.
1827
1892
  msg = reason or self._self_reload_hint()
1828
- self.static_hintbar.update(f"[#ff69b4]{msg}[/#ff69b4]")
1893
+ self.static_hintbar.update(f"[{theme_store_.get('accent')}]{msg}[/{theme_store_.get('accent')}]")
1829
1894
  self.request_render()
1830
1895
  try:
1831
1896
  # Let the frame land before the screen tears down. Cancellation
@@ -1903,7 +1968,7 @@ class MicroTui():
1903
1968
  if landed:
1904
1969
  self._flash_status(f"✓ copied {len(text)} chars")
1905
1970
  else:
1906
- self._flash_status(f"[red]✗ pyperclip failed ({pyperclip_error}) — tried terminal clipboard, enable clipboard access if it didn't land[/red]", seconds=5)
1971
+ self._flash_status(f"[{theme_store_.get('error')}]✗ pyperclip failed ({pyperclip_error}) — tried terminal clipboard, enable clipboard access if it didn't land[/{theme_store_.get('error')}]", seconds=5)
1907
1972
 
1908
1973
  # ============================================= statusbar
1909
1974
 
@@ -2659,7 +2724,7 @@ class MicroTui():
2659
2724
  # looks like it's waiting on an interactive prompt) gets a distinct glyph/color
2660
2725
  # so it's visible at a glance without waiting for the model to next mention it.
2661
2726
  lines = [
2662
- f"[yellow]⚠ {p['pid']} · {p['age']}s · stalled[/yellow]" if p.get("stalled")
2727
+ f"[{theme_store_.get('warn')}]⚠ {p['pid']} · {p['age']}s · stalled[/{theme_store_.get('warn')}]" if p.get("stalled")
2663
2728
  else f"◇ {p['pid']} · {p['age']}s"
2664
2729
  for p in procs
2665
2730
  ]
@@ -2986,6 +3051,10 @@ class MicroTui():
2986
3051
  # cleared implicitly by never being set on the headless path.
2987
3052
  from micro_cc.tools import monitor_schedule_runtime as _schedule_rt
2988
3053
 
3054
+ # A /theme switch repaints an already-running UI through this
3055
+ # listener — see _apply_theme (re-emits the terminal's default
3056
+ # bg/fg, then invalidates every cached render).
3057
+ theme_store_.on_change(self._apply_theme)
2989
3058
  _schedule_rt.set_wake_callback(self._on_monitor_event)
2990
3059
  _schedule_rt.set_start_tick(self._ensure_schedule_tick)
2991
3060
  self._ensure_schedule_tick()
@@ -176,7 +176,7 @@ def format_status_glyph(name: str, status: str, pending: int = 0, tokens: dict |
176
176
  is the verbose sibling for monitor_'s tool-call output, where
177
177
  the model actually needs the full path/summary."""
178
178
  symbol = status_marker.SYMBOL.get(status, "?")
179
- color = status_marker.COLOR.get(status, "yellow")
179
+ color = status_marker.color(status)
180
180
  if len(name) > _STATUS_GLYPH_NAME_MAXLEN:
181
181
  name = name[: _STATUS_GLYPH_NAME_MAXLEN - 1] + "…"
182
182
  mail = "✉" if pending else ""
@@ -26,8 +26,43 @@ class Component(Protocol):
26
26
  BEGIN_SYNCHRONIZED_OUTPUT = "\x1b[?2026h" # one atomic terminal paint starts
27
27
  END_SYNCHRONIZED_OUTPUT = "\x1b[?2026l" # ...and ends here
28
28
 
29
- ENTER_ALT_SCREEN = "\x1b[?1049h\x1b[2J"
30
- EXIT_ALT_SCREEN = "\x1b[?1049l"
29
+ # The alt screen also sets the terminal's own DEFAULT background/foreground
30
+ # (OSC 11/10) from the active theme set. Doing it here rather than painting a
31
+ # background per rendered line is what themes the whole UI with no renderer
32
+ # changes: every span that doesn't set its own bgcolor (body text, padding,
33
+ # dividers) inherits this default, and every widget that DOES set one keeps
34
+ # working. OSC 111/110 on exit restore the user's own terminal profile — the
35
+ # alt screen already restores its contents.
36
+ #
37
+ # Both sequences are FUNCTIONS, not constants: a /theme switch has to be able
38
+ # to re-emit them mid-session (see reapply_default_colors below), so binding
39
+ # them once at import would freeze the ground for the process's whole life.
40
+ def _enter() -> str:
41
+ from micro_cc.utils import theme_store_
42
+ return (
43
+ "\x1b[?1049h"
44
+ f"\x1b]11;{theme_store_.get('bg')}\x07"
45
+ f"\x1b]10;{theme_store_.get('fg')}\x07"
46
+ "\x1b[2J"
47
+ )
48
+
49
+
50
+ def _exit() -> str:
51
+ return "\x1b]111\x07\x1b]110\x07\x1b[?1049l"
52
+
53
+
54
+ def default_colors_sequence() -> str:
55
+ """Just the OSC 11/10 pair, no alt-screen switch — what a live theme
56
+ change re-emits to repaint the ground under an already-running UI."""
57
+ from micro_cc.utils import theme_store_
58
+ return (
59
+ f"\x1b]11;{theme_store_.get('bg')}\x07"
60
+ f"\x1b]10;{theme_store_.get('fg')}\x07"
61
+ )
62
+
63
+
64
+ ENTER_ALT_SCREEN = _enter()
65
+ EXIT_ALT_SCREEN = _exit()
31
66
 
32
67
  # A focused component emits this at the exact spot in its own render()
33
68
  # output where the hardware cursor should land. extract_cursor_position
@@ -454,6 +489,16 @@ class TuiAltScreen:
454
489
  return size.columns, size.lines
455
490
 
456
491
  # --- overlays ---------------------------------------------------
492
+ def invalidate_overlays(self) -> None:
493
+ """Drop every open overlay's cached render — what a live theme switch
494
+ calls so pickers/panels repaint in the new color set instead of
495
+ serving lines built with the old one."""
496
+ for entry in self.overlay_stack:
497
+ try:
498
+ entry.component.invalidate()
499
+ except Exception:
500
+ pass
501
+
457
502
  def show_overlay(self, component: Component, options: OverlayOptions | None = None) -> OverlayHandle:
458
503
  self.focus_order_counter += 1
459
504
  entry = OverlayEntry(component, options or OverlayOptions(), self.focused_overlay, self.focus_order_counter)
@@ -5,9 +5,9 @@ from rich.text import Text
5
5
 
6
6
  # Matches CleanMarkdown's own code-block theme (md_render_.py) — a diff and
7
7
  # a plain code block should read as the same visual family.
8
- _THEME = "monokai"
9
- _DEL_BG = "on #3a1414"
10
- _ADD_BG = "on #10301a"
8
+ # Colors come from the active theme set (utils/theme_store_) — the syntax
9
+ # theme is a Pygments name, the line tints are hex backgrounds.
10
+ from micro_cc.utils import theme_store_
11
11
 
12
12
 
13
13
  def _format_range(start: int, stop: int) -> str:
@@ -21,7 +21,7 @@ def _format_range(start: int, stop: int) -> str:
21
21
  def _highlighted_lines(code: str, lexer_name: str) -> list[Text]:
22
22
  if not code:
23
23
  return []
24
- syntax = Syntax(code, lexer_name, theme=_THEME, word_wrap=False, background_color="default")
24
+ syntax = Syntax(code, lexer_name, theme=theme_store_.get("syntax"), word_wrap=False, background_color="default")
25
25
  return syntax.highlight(code).split("\n")
26
26
 
27
27
 
@@ -60,13 +60,13 @@ def build_diff_lines(old: str, new: str, file_path: str = "", context: int = 3)
60
60
  continue
61
61
  if tag in ("delete", "replace"):
62
62
  for i in range(i1, i2):
63
- line = Text("- ", style="bold red") + old_hi[i]
64
- line.stylize(_DEL_BG)
63
+ line = Text("- ", style=f"bold {theme_store_.get('diff_del_fg')}") + old_hi[i]
64
+ line.stylize(f"on {theme_store_.get('diff_del_bg')}")
65
65
  out.append(line)
66
66
  if tag in ("insert", "replace"):
67
67
  for j in range(j1, j2):
68
- line = Text("+ ", style="bold green") + new_hi[j]
69
- line.stylize(_ADD_BG)
68
+ line = Text("+ ", style=f"bold {theme_store_.get('diff_add_fg')}") + new_hi[j]
69
+ line.stylize(f"on {theme_store_.get('diff_add_bg')}")
70
70
  out.append(line)
71
71
  return out
72
72
 
@@ -14,6 +14,7 @@ touchpoint list this mirrors.
14
14
 
15
15
  import asyncio
16
16
 
17
+ from micro_cc.utils import theme_store_
17
18
  from micro_cc.utils.msg_store_ import store_msgs
18
19
  from micro_cc.utils.tokenization_simple import CACHE_TTL_S
19
20
  from micro_cc.tui_native.message_row_ import MessageRow, StreamingRow
@@ -199,10 +200,9 @@ async def handle_cache_invalidate(app, event, container, tool_call_rows, approva
199
200
 
200
201
  tokens = f"{event.get('missed_tokens', 0):,}"
201
202
  label = f"cache miss ({reason})" if reason else "cache miss"
202
- # [yellow] isn't in TextLine's markup vocabulary (text_.py: only [red] and
203
- # [#rrggbb] hex are translated) — [red] is the closest supported warning
204
- # color, same choice screen_cmds_.py's own flashes already make.
205
- app._flash_status(f"[red]⚠ {label}: {tokens} tokens re-billed[/red]", seconds=5)
203
+ # TextLine's markup vocabulary is [red] and [#rrggbb] hex only — no
204
+ # [yellow] — so this uses the theme's error token directly.
205
+ app._flash_status(f"[{theme_store_.get('error')}]⚠ {label}: {tokens} tokens re-billed[/{theme_store_.get('error')}]", seconds=5)
206
206
 
207
207
 
208
208
  ETYPES = {
@@ -16,7 +16,7 @@ the current Textual pickers do: Option(label, id=label)).
16
16
  from dataclasses import dataclass, field
17
17
 
18
18
  from micro_cc.tui_native.text_utils_ import slice_by_column, visible_width
19
- from micro_cc.tui_native.theme_ import DEFAULT_THEME, Theme
19
+ from micro_cc.tui_native.theme_ import default_theme, Theme
20
20
 
21
21
  DEFAULT_MAX_VISIBLE = 8
22
22
  PRIMARY_COLUMN_GAP = 2
@@ -114,14 +114,14 @@ class ListPicker:
114
114
 
115
115
  def __init__(self, items: list[PickerItem] | None = None, max_visible: int = DEFAULT_MAX_VISIBLE,
116
116
  primary_min: int = DEFAULT_PRIMARY_MIN, primary_max: int = DEFAULT_PRIMARY_MAX,
117
- theme: Theme = DEFAULT_THEME, max_desc_lines: int = MAX_DESC_LINES):
117
+ theme: Theme | None = None, max_desc_lines: int = MAX_DESC_LINES):
118
118
  self.on_select = None # Callable[[PickerItem], None]
119
119
  self.on_cancel = None # Callable[[], None]
120
120
  self.on_selection_change = None # Callable[[PickerItem], None]
121
121
  self.max_visible = max(1, max_visible)
122
122
  self.primary_min = max(1, min(primary_min, primary_max))
123
123
  self.primary_max = max(1, max(primary_min, primary_max))
124
- self.theme = theme
124
+ self.theme = theme or default_theme()
125
125
  # Per-item description wrap cap. Was the module constant MAX_DESC_LINES
126
126
  # baked in directly, which ellipsized any hint past 3 lines even when
127
127
  # the terminal had plenty of room — callers with a real height
@@ -11,6 +11,38 @@ from rich.syntax import Syntax
11
11
  from rich.style import Style
12
12
  from rich.console import Console, ConsoleOptions, RenderResult
13
13
  from rich.text import Text
14
+ from rich.theme import Theme
15
+
16
+ from micro_cc.utils import theme_store_
17
+
18
+
19
+ def rich_theme() -> Theme:
20
+ """Rich's built-in markdown styles, rebuilt from the active color set.
21
+
22
+ Rich's own defaults were picked against a dark terminal — `markdown.code`
23
+ is literally cyan-on-black, and the table/bullet accents are pale cyans
24
+ that vanish on white. So these are overridden from theme_store_ rather
25
+ than left at Rich's defaults; every value is a hex the file supplies, so
26
+ editing theme.json restyles the markdown too.
27
+
28
+ Built fresh per call (a Theme is cheap, and rendering is cached upstream
29
+ per width) so a live /theme switch can't keep painting the old set.
30
+ """
31
+ c = theme_store_.get
32
+ return Theme({
33
+ "markdown.code": f"bold {c('inline_code')} on {c('code_bg')}",
34
+ "markdown.code_block": c("code_fg"),
35
+ "markdown.block_quote": c("quote"),
36
+ "markdown.h2": f"bold {c('heading')} underline",
37
+ "markdown.h3": f"bold {c('heading')}",
38
+ "markdown.h4": f"italic {c('heading')}",
39
+ "markdown.link": c("link"),
40
+ "markdown.link_url": f"{c('link_url')} underline",
41
+ "markdown.list": c("list"),
42
+ "markdown.item.number": c("list"),
43
+ "markdown.table.border": c("table_border"),
44
+ "markdown.table.header": f"bold {c('table_header')}",
45
+ })
14
46
 
15
47
 
16
48
  class CleanCodeBlock(CodeBlock):
@@ -58,8 +90,14 @@ class CleanMarkdown(Markdown):
58
90
  elements["heading_open"] = CleanHeading
59
91
 
60
92
  def __init__(self, markup: str, **kwargs):
93
+ # code_theme is a Pygments theme NAME from the active color set — a
94
+ # palette can't express a syntax theme, so theme.json carries it as
95
+ # the "syntax" token (e.g. "friendly" for light, "monokai" for dark).
96
+ # Applies to fenced blocks and inline `code` alike (Rich falls back
97
+ # to code_theme for inline unless inline_code_theme says otherwise).
98
+ kwargs.setdefault("code_theme", theme_store_.get("syntax"))
61
99
  super().__init__(markup, **kwargs)
62
- self.style = "grey85"
100
+ self.style = theme_store_.get("fg")
63
101
 
64
102
 
65
103
  class DimMarkdown(CleanMarkdown):
@@ -27,7 +27,8 @@ from rich.console import Console, Group
27
27
  from rich.text import Text
28
28
  from PIL import Image as PILImage
29
29
 
30
- from micro_cc.tui_native.md_render_ import render_md
30
+ from micro_cc.tui_native.md_render_ import render_md, rich_theme
31
+ from micro_cc.utils import theme_store_
31
32
  from micro_cc.tui_native.diff_viewer_ import build_diff_lines
32
33
  from micro_cc.tui_native.detect_images_ import (
33
34
  detect_capabilities, calculate_image_cell_size, encode_kitty,
@@ -64,7 +65,14 @@ def _cap_diff_lines(diff_lines: list, max_lines: int = _MAX_EXPANDED_LINES) -> l
64
65
  def _render_to_lines(renderable, width: int) -> list[str]:
65
66
  """Flatten a Rich renderable into plain ANSI-coded lines at exactly
66
67
  `width` columns — real SGR escape codes, not Segment objects, so the
67
- result is a drop-in list[str] for the Component protocol."""
68
+ result is a drop-in list[str] for the Component protocol.
69
+
70
+ LIGHT_THEME (md_render_.py) is applied here rather than left to each
71
+ renderable, because this is the single Console every widget in the tree
72
+ funnels through — banner, message rows, RichStatic status/hint lines
73
+ alike. One theme at the choke point is what keeps Rich's built-in
74
+ markdown styles (inline code is cyan-on-black by default) from landing
75
+ unreadable on the alt screen's white background."""
68
76
  buf = io.StringIO()
69
77
  console = Console(
70
78
  file=buf,
@@ -73,6 +81,7 @@ def _render_to_lines(renderable, width: int) -> list[str]:
73
81
  color_system="truecolor",
74
82
  highlight=False,
75
83
  soft_wrap=False,
84
+ theme=rich_theme(),
76
85
  )
77
86
  console.print(renderable, end="")
78
87
  return buf.getvalue().split("\n")
@@ -158,7 +167,7 @@ class MessageRow:
158
167
  if t == "user":
159
168
  return Text(f"› {msg['content']}", style="bold")
160
169
  elif t == "user_queued":
161
- return Text(f"⋯queued {msg['content']}", style="dim italic #ffA500")
170
+ return Text(f"⋯queued {msg['content']}", style=f"dim italic {theme_store_.get('queued')}")
162
171
  elif t == "text":
163
172
  return render_md(msg["content"])
164
173
  elif t == "thinking":
@@ -172,7 +181,7 @@ class MessageRow:
172
181
  result = msg.get('result')
173
182
  expanded = msg.get('expanded')
174
183
  if result is None:
175
- return Text(f"◇ {name} ", style="italic yellow")
184
+ return Text(f"◇ {name} ", style=f"italic {theme_store_.get('warn')}")
176
185
  if expanded:
177
186
  if name == "edit_":
178
187
  fp = inp.get('file_path', '')
@@ -212,14 +221,14 @@ class MessageRow:
212
221
  preview = first_line[:40] + ("…" if truncated else "")
213
222
  return Text(f"⟐ {name} → {preview} (click to expand)", style="dim")
214
223
  elif t == "error":
215
- return Text(f"△ {msg['content']}", style="italic red")
224
+ return Text(f"△ {msg['content']}", style=f"italic {theme_store_.get('error')}")
216
225
  elif t == "approval":
217
226
  inp = msg.get('input', {})
218
227
  name = msg['name']
219
228
  expanded = msg.get('expanded')
220
229
  hint = "click to collapse" if expanded else "click to view full"
221
230
  header = Text.assemble(
222
- ("◆ ", "bold yellow"), (name, "bold yellow"),
231
+ (f"◆ ", f"bold {theme_store_.get('warn')}"), (name, f"bold {theme_store_.get('warn')}"),
223
232
  (f" enter to approve · esc to reject · {hint}", "dim"),
224
233
  )
225
234
  if name == "bash_":
@@ -7,15 +7,15 @@ rendering/navigation/filtering by composition rather than duplicating it."""
7
7
  from dataclasses import dataclass
8
8
 
9
9
  from micro_cc.tui_native.list_picker_ import MAX_DESC_LINES, ListPicker, PickerItem, wrap_in_border
10
- from micro_cc.tui_native.theme_ import DEFAULT_THEME, Theme
10
+ from micro_cc.tui_native.theme_ import default_theme, Theme
11
11
 
12
12
 
13
13
  class MultiSelectList:
14
14
  def __init__(self, items: list[PickerItem] | None = None, max_visible: int = 8,
15
- primary_min: int = 12, primary_max: int = 32, theme: Theme = DEFAULT_THEME,
15
+ primary_min: int = 12, primary_max: int = 32, theme: Theme | None = None,
16
16
  max_desc_lines: int = MAX_DESC_LINES):
17
17
  self._picker = ListPicker(items, max_visible, primary_min, primary_max, theme, max_desc_lines)
18
- self.theme = theme
18
+ self.theme = theme or default_theme()
19
19
  self.checked: set[str] = set()
20
20
 
21
21
  self.on_confirm = None # Callable[[list[PickerItem]], None]
@@ -15,7 +15,7 @@ tools/ask_user_tool.py: {question, header, options: [{label, description}], mult
15
15
 
16
16
  from micro_cc.tui_native.list_picker_ import ListPicker, PickerItem
17
17
  from micro_cc.tui_native.multi_select_list_ import MultiSelectList
18
- from micro_cc.tui_native.theme_ import DEFAULT_THEME, Theme
18
+ from micro_cc.tui_native.theme_ import default_theme, Theme
19
19
 
20
20
 
21
21
  def _options_to_items(options: list[dict]) -> list[PickerItem]:
@@ -24,8 +24,8 @@ def _options_to_items(options: list[dict]) -> list[PickerItem]:
24
24
 
25
25
 
26
26
  class QuestionPanel:
27
- def __init__(self, questions: list[dict], theme: Theme = DEFAULT_THEME):
28
- self.theme = theme
27
+ def __init__(self, questions: list[dict], theme: Theme | None = None):
28
+ self.theme = theme or default_theme()
29
29
  self.questions = questions
30
30
  self.stage = 0
31
31
  self.answers: dict[str, object] = {}
@@ -89,6 +89,15 @@ async def cmd_model(app):
89
89
  app.prompt.clear()
90
90
 
91
91
 
92
+ async def cmd_theme(app):
93
+ """Open the theme picker. The actual switch happens in
94
+ app._on_theme_picked -> theme_store_.set_preset, which persists the new
95
+ color set and fires the on_change listeners that repaint the ground and
96
+ invalidate the render caches."""
97
+ app._open_picker(app.theme_picker, "_theme_picker_overlay")
98
+ app.prompt.clear()
99
+
100
+
92
101
  async def cmd_copy(app):
93
102
  lines = []
94
103
  for msg in app._msg_rows:
@@ -156,7 +165,12 @@ async def cmd_update(app):
156
165
  await _run_update(app)
157
166
 
158
167
 
159
- _PINK = "#ff69b4"
168
+ def _pink() -> str:
169
+ """The accent color, read live from the active theme set (see
170
+ theme_store_) — a function rather than a constant so a /theme switch
171
+ isn't frozen at import time."""
172
+ from micro_cc.utils import theme_store_
173
+ return theme_store_.get("accent")
160
174
 
161
175
 
162
176
  async def _run_update(app):
@@ -191,7 +205,7 @@ async def _run_update(app):
191
205
  i = 0
192
206
  while True:
193
207
  frame = app._SPINNER[i % len(app._SPINNER)]
194
- app.static_hintbar.update(f"[{_PINK}]{frame} updating micro-cc {current_version} → {latest}…[/{_PINK}]")
208
+ app.static_hintbar.update(f"[{_pink()}]{frame} updating micro-cc {current_version} → {latest}…[/{_pink()}]")
195
209
  app.request_render()
196
210
  i += 1
197
211
  try:
@@ -209,7 +223,7 @@ async def _run_update(app):
209
223
  app._static_hint_text()
210
224
  return
211
225
 
212
- app.static_hintbar.update(f"[{_PINK}]✓ updated {current_version} → {latest} — restarting…[/{_PINK}]")
226
+ app.static_hintbar.update(f"[{_pink()}]✓ updated {current_version} → {latest} — restarting…[/{_pink()}]")
213
227
  app.request_render()
214
228
  await asyncio.sleep(0.8)
215
229
 
@@ -463,6 +477,7 @@ SLASH_COMMANDS = {
463
477
  "/clear": cmd_clear,
464
478
  "/copy": cmd_copy,
465
479
  "/model": cmd_model,
480
+ "/theme": cmd_theme,
466
481
  "/dangerous": cmd_dangerous,
467
482
  "/rewind": cmd_rewind,
468
483
  "/login": cmd_login,