micro-cc 0.2.76__tar.gz → 0.2.78__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 (208) hide show
  1. {micro_cc-0.2.76/src/micro_cc.egg-info → micro_cc-0.2.78}/PKG-INFO +1 -1
  2. {micro_cc-0.2.76 → micro_cc-0.2.78}/pyproject.toml +1 -1
  3. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/claude_loop_.py +125 -70
  4. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/litellm.py +11 -4
  5. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/bash_tool.py +41 -8
  6. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/etype_handler_.py +1 -1
  7. {micro_cc-0.2.76 → micro_cc-0.2.78/src/micro_cc.egg-info}/PKG-INFO +1 -1
  8. {micro_cc-0.2.76 → micro_cc-0.2.78}/LICENSE +0 -0
  9. {micro_cc-0.2.76 → micro_cc-0.2.78}/README.md +0 -0
  10. {micro_cc-0.2.76 → micro_cc-0.2.78}/setup.cfg +0 -0
  11. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/__init__.py +0 -0
  12. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/browser/__init__.py +0 -0
  13. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/browser/_cookies.py +0 -0
  14. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/browser/_md_convert.py +0 -0
  15. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/browser/browser_manager.py +0 -0
  16. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/browser/simpletextbrowser.py +0 -0
  17. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/cache/__init__.py +0 -0
  18. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/cache/redis_cache.py +0 -0
  19. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/code_test/__init__.py +0 -0
  20. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/code_test/run_token_cutter.py +0 -0
  21. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/code_test/test_messages.py +0 -0
  22. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/execute_tool.py +0 -0
  23. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/__init__.py +0 -0
  24. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/anthropic.py +0 -0
  25. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/foundry.py +0 -0
  26. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/ollama.py +0 -0
  27. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/registry.py +0 -0
  28. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/models/schema.py +0 -0
  29. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/autoresearch/SKILL.md +0 -0
  30. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/SKILL.md +0 -0
  31. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-OFL.txt +0 -0
  32. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/ArsenalSC-Regular.ttf +0 -0
  33. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Bold.ttf +0 -0
  34. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-OFL.txt +0 -0
  35. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/BigShoulders-Regular.ttf +0 -0
  36. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Boldonse-OFL.txt +0 -0
  37. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Boldonse-Regular.ttf +0 -0
  38. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Bold.ttf +0 -0
  39. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-OFL.txt +0 -0
  40. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/BricolageGrotesque-Regular.ttf +0 -0
  41. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Bold.ttf +0 -0
  42. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Italic.ttf +0 -0
  43. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-OFL.txt +0 -0
  44. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/CrimsonPro-Regular.ttf +0 -0
  45. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/DMMono-OFL.txt +0 -0
  46. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/DMMono-Regular.ttf +0 -0
  47. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/EricaOne-OFL.txt +0 -0
  48. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/EricaOne-Regular.ttf +0 -0
  49. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Bold.ttf +0 -0
  50. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/GeistMono-OFL.txt +0 -0
  51. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/GeistMono-Regular.ttf +0 -0
  52. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Gloock-OFL.txt +0 -0
  53. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Gloock-Regular.ttf +0 -0
  54. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Bold.ttf +0 -0
  55. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-OFL.txt +0 -0
  56. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexMono-Regular.ttf +0 -0
  57. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Bold.ttf +0 -0
  58. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-BoldItalic.ttf +0 -0
  59. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Italic.ttf +0 -0
  60. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/IBMPlexSerif-Regular.ttf +0 -0
  61. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Bold.ttf +0 -0
  62. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-BoldItalic.ttf +0 -0
  63. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Italic.ttf +0 -0
  64. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-OFL.txt +0 -0
  65. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSans-Regular.ttf +0 -0
  66. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Italic.ttf +0 -0
  67. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/InstrumentSerif-Regular.ttf +0 -0
  68. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Italiana-OFL.txt +0 -0
  69. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Italiana-Regular.ttf +0 -0
  70. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Bold.ttf +0 -0
  71. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-OFL.txt +0 -0
  72. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/JetBrainsMono-Regular.ttf +0 -0
  73. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Jura-Light.ttf +0 -0
  74. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Jura-Medium.ttf +0 -0
  75. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Jura-OFL.txt +0 -0
  76. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-OFL.txt +0 -0
  77. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/LibreBaskerville-Regular.ttf +0 -0
  78. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Lora-Bold.ttf +0 -0
  79. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Lora-BoldItalic.ttf +0 -0
  80. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Lora-Italic.ttf +0 -0
  81. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Lora-OFL.txt +0 -0
  82. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Lora-Regular.ttf +0 -0
  83. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Bold.ttf +0 -0
  84. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/NationalPark-OFL.txt +0 -0
  85. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/NationalPark-Regular.ttf +0 -0
  86. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-OFL.txt +0 -0
  87. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/NothingYouCouldDo-Regular.ttf +0 -0
  88. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Outfit-Bold.ttf +0 -0
  89. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Outfit-OFL.txt +0 -0
  90. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Outfit-Regular.ttf +0 -0
  91. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-Medium.ttf +0 -0
  92. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/PixelifySans-OFL.txt +0 -0
  93. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-OFL.txt +0 -0
  94. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/PoiretOne-Regular.ttf +0 -0
  95. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Bold.ttf +0 -0
  96. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-OFL.txt +0 -0
  97. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/RedHatMono-Regular.ttf +0 -0
  98. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-OFL.txt +0 -0
  99. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Silkscreen-Regular.ttf +0 -0
  100. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-Medium.ttf +0 -0
  101. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/SmoochSans-OFL.txt +0 -0
  102. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Tektur-Medium.ttf +0 -0
  103. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Tektur-OFL.txt +0 -0
  104. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/Tektur-Regular.ttf +0 -0
  105. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Bold.ttf +0 -0
  106. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/WorkSans-BoldItalic.ttf +0 -0
  107. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Italic.ttf +0 -0
  108. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/WorkSans-OFL.txt +0 -0
  109. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/WorkSans-Regular.ttf +0 -0
  110. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-OFL.txt +0 -0
  111. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/canvas-fonts/YoungSerif-Regular.ttf +0 -0
  112. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/design/references/blueprint-tokens.md +0 -0
  113. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/github/SKILL.md +0 -0
  114. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/graph-orchestration/SKILL.md +0 -0
  115. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/SKILL.md +0 -0
  116. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/brief.md +0 -0
  117. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/scripts/build_deck.js +0 -0
  118. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/scripts/deck_to_images.py +0 -0
  119. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/scripts/office/__init__.py +0 -0
  120. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/scripts/office/soffice.py +0 -0
  121. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/scripts/render_svg.py +0 -0
  122. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/pptx/svg-slides.md +0 -0
  123. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/skill_loader.py +0 -0
  124. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/skills/xlsx/SKILL.md +0 -0
  125. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/start_batch_.py +0 -0
  126. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/start_headless_.py +0 -0
  127. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/start_live_tui_.py +0 -0
  128. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/start_watch_.py +0 -0
  129. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/__init__.py +0 -0
  130. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/ask_user_tool.py +0 -0
  131. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/browser_tool_.py +0 -0
  132. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/computer_tool_.py +0 -0
  133. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/file_tools_.py +0 -0
  134. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/mcp_client_.py +0 -0
  135. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/memory_tool_.py +0 -0
  136. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/message_session_.py +0 -0
  137. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/monitor_.py +0 -0
  138. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/monitor_watch_.py +0 -0
  139. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/search_history_tool_.py +0 -0
  140. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/search_tool_.py +0 -0
  141. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/skill_tools_.py +0 -0
  142. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/todo_tools_.py +0 -0
  143. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/vision_tools_.py +0 -0
  144. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tools/web_tools_.py +0 -0
  145. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/__init__.py +0 -0
  146. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/alt_screen_.py +0 -0
  147. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/detect_images_.py +0 -0
  148. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/diff_viewer_.py +0 -0
  149. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/keys_.py +0 -0
  150. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/kill_ring_.py +0 -0
  151. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/list_picker_.py +0 -0
  152. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/md_render_.py +0 -0
  153. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/message_row_.py +0 -0
  154. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/multi_select_list_.py +0 -0
  155. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/prompt_input_.py +0 -0
  156. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/question_panel_.py +0 -0
  157. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/screen_cmds_.py +0 -0
  158. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/scroll_view_.py +0 -0
  159. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/self_terminal_setup_.py +0 -0
  160. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/self_update_.py +0 -0
  161. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/stack_.py +0 -0
  162. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/stdin_buffer_.py +0 -0
  163. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/terminal_.py +0 -0
  164. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/text_.py +0 -0
  165. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/text_utils_.py +0 -0
  166. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/theme_.py +0 -0
  167. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/truncated_text_.py +0 -0
  168. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/undo_stack_.py +0 -0
  169. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/tui_native/word_navigation_.py +0 -0
  170. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/__init__.py +0 -0
  171. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/annotate_.py +0 -0
  172. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/banner_.py +0 -0
  173. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/claude_md_loader.py +0 -0
  174. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/claude_subscription.py +0 -0
  175. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/command_registry.py +0 -0
  176. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/default_status_sh.py +0 -0
  177. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/helpers.py +0 -0
  178. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/hidden_prompts.py +0 -0
  179. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/hints.py +0 -0
  180. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/history_mount_.py +0 -0
  181. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/inbox_store_.py +0 -0
  182. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/memory_store_.py +0 -0
  183. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/msg_normalize_.py +0 -0
  184. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/msg_store_.py +0 -0
  185. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/pg_store_.py +0 -0
  186. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/project_files_.py +0 -0
  187. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/session_ipc_.py +0 -0
  188. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/settings_store_.py +0 -0
  189. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/status_marker.py +0 -0
  190. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/subagent_tracker_.py +0 -0
  191. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/terminal_setup.py +0 -0
  192. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/tokenization_simple.py +0 -0
  193. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/tool_limits.py +0 -0
  194. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/utils/tool_result_storage.py +0 -0
  195. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/__init__.py +0 -0
  196. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/bridge.py +0 -0
  197. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/dist/assets/index-9B5FcRm2.css +0 -0
  198. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/dist/assets/index-CDG9HO3B.js +0 -0
  199. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/dist/index.html +0 -0
  200. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/history.py +0 -0
  201. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/launch.py +0 -0
  202. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/server.py +0 -0
  203. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc/webui/session.py +0 -0
  204. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc.egg-info/SOURCES.txt +0 -0
  205. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc.egg-info/dependency_links.txt +0 -0
  206. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc.egg-info/entry_points.txt +0 -0
  207. {micro_cc-0.2.76 → micro_cc-0.2.78}/src/micro_cc.egg-info/requires.txt +0 -0
  208. {micro_cc-0.2.76 → micro_cc-0.2.78}/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.76
3
+ Version: 0.2.78
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: Copyright (c) 2026 George Salapa. All rights reserved.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "micro-cc"
7
- version = "0.2.76"
7
+ version = "0.2.78"
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"
@@ -128,6 +128,13 @@ async def claude_loop(
128
128
  skills_summary = get_skill_summary(project_dir)
129
129
  claude_md_content = load_claude_md_file(project_dir)
130
130
  mcp_catalog = get_effective_mcp_catalog(project_dir)
131
+ # Where micro_cc's OWN source lives on disk — never project_dir (the
132
+ # user's project) and never wherever a skill file happens to live. This
133
+ # file (claude_loop_.py) sits directly in the package dir, so its own
134
+ # dirname IS that resolved path, editable checkout or real pip install
135
+ # alike — same fact graph-orchestration/SKILL.md has the model resolve
136
+ # itself via `import micro_cc, os; os.path.dirname(micro_cc.__file__)`.
137
+ package_dir = os.path.dirname(os.path.abspath(__file__))
131
138
 
132
139
  # Core tools: bash, file ops, search, planning, skills
133
140
  default_tools = [
@@ -233,6 +240,7 @@ async def claude_loop(
233
240
  - You are running LOCALLY on the user's machine (on the metal), NOT a remote server
234
241
  - You have direct access to the local filesystem, Desktop, Documents, etc.
235
242
  - Project directory: {project_dir}
243
+ - Your own source code lives at: {package_dir} (not project_dir)
236
244
  - Date: {datetime.datetime.now().strftime("%B %d, %Y")}
237
245
 
238
246
  ## Core Tools (always available)
@@ -252,25 +260,16 @@ message_session_ (message another live micro-cc session), etc.
252
260
  - todo_tool_: For multi-step complex work, write a todo plan and keep on track.
253
261
 
254
262
  ## Long-term Memory
255
- use memory_ (action=add|edit|get|delete|list, scope=global|project): two
256
- independent key spaces. scope="global" (default) persists across ALL
257
- projects and sessions, stored at ~/.micro-cc/memory.json — use for durable
258
- facts that hold regardless of project: user preferences, standing
259
- decisions, cross-project gotchas, references. scope="project" persists only
260
- for this project_dir, stored under
261
- ~/.micro-cc/projects/{{name}}_{{hash}}/memory.json — use for durable facts
262
- specific to this project (conventions, decisions, recurring gotchas) that
263
- are worth recalling next time you're invoked here. A manifest of
264
- keys+descriptions for both scopes is injected into your context each loop
265
- as <memory-manifest> — use memory_(action="get", key=..., scope=...) to
266
- expand an entry. For merely in-progress state within the current session,
267
- use todo_tool_ or the project's own files instead.
263
+ memory_ (action=add|edit|get|delete|list, scope=global|project).
264
+ scope="global": cross-project facts (~/.micro-cc/memory.json). scope="project":
265
+ this project only (~/.micro-cc/projects/{{name}}_{{hash}}/memory.json).
266
+ <memory-manifest> lists keys each loop; memory_(action="get", key=...) expands
267
+ one. In-progress state: use todo_tool_ instead.
268
268
 
269
269
  ## Conversation History Search
270
- search_history_(query=..., limit=...): full-text search over this
271
- project's ENTIRE conversation history, including turns already folded out
272
- of your live context by checkpoint compaction — nothing folded is ever
273
- actually deleted.
270
+ conversation grows forever
271
+ search_history_(query=..., limit=...): full-text search over this project's
272
+ whole conversation history, including compacted turns.
274
273
 
275
274
  {skills_summary}
276
275
  {claude_md_section}
@@ -278,6 +277,7 @@ actually deleted.
278
277
  ## Guidelines
279
278
  - Read files before editing them
280
279
  - Use absolute paths when working outside project_dir
280
+ - When writing to user, remove all mannered prose
281
281
  """,
282
282
  # stable prefix -> cache tools + this system prompt. Content is
283
283
  # byte-identical across loop iterations of this same call (built
@@ -316,6 +316,61 @@ actually deleted.
316
316
  """
317
317
  msgs.append({"role": "user", "content": content})
318
318
 
319
+ def _build_memory_manifest() -> str | None:
320
+ """Key+description index of both memory scopes (see memory_tool_'s
321
+ docstring) — None if both are empty. Cheap: list_memories() is one
322
+ small JSON read per scope, no directory walk."""
323
+ parts = []
324
+ global_items = memory_store_.list_memories()
325
+ if global_items:
326
+ parts.append(
327
+ "Global (all projects):\n"
328
+ + "\n".join(f"- {it['key']} — {it['description']}" for it in global_items)
329
+ )
330
+ project_items = memory_store_.list_memories(project_dir=project_dir)
331
+ if project_items:
332
+ parts.append(
333
+ "Project (this project_dir only):\n"
334
+ + "\n".join(f"- {it['key']} — {it['description']}" for it in project_items)
335
+ )
336
+ return "\n\n".join(parts) if parts else None
337
+
338
+ # Memory manifest rides as its OWN role:"system" block, right after
339
+ # system_prompt_msg — not folded into it, so it gets its own independent
340
+ # cache_control breakpoint (anthropic.py/litellm.py/foundry.py all
341
+ # already support N system messages, each optionally cache_control'd —
342
+ # see their system_prompts-block handling). Built once here, frozen for
343
+ # every internal loop iteration of THIS turn, same timing as
344
+ # system_prompt_msg itself: memory changes are rare relative to loop
345
+ # iterations, so this block matches system_prompt_msg's own cache
346
+ # profile (byte-identical turn to turn in the common case -> cache hit
347
+ # on this block too) instead of eating a rolling-breakpoint miss on
348
+ # every single tool round-trip the way a tail-appended manifest would.
349
+ # A turn where memory actually changed since the last turn pays a
350
+ # one-time miss on THIS block only — the system_prompt_msg breakpoint
351
+ # and the separate rolling transcript breakpoint are untouched, so nei-
352
+ # ther your tools+system-prompt cache nor your accumulated transcript
353
+ # cache gets invalidated by a memory edit.
354
+ _memory_manifest_baseline = _build_memory_manifest()
355
+ memory_manifest_msg = (
356
+ {
357
+ "role": "system",
358
+ "content": f"## Long-term Memory\nKey+description index for both scopes — call memory_(action=\"get\", key=..., scope=...) to expand an entry.\n\n{_memory_manifest_baseline}",
359
+ "cache_control": {"type": "ephemeral"},
360
+ }
361
+ if _memory_manifest_baseline is not None
362
+ else None
363
+ )
364
+ # Mid-turn notify-once tracking for memory_ calls Claude makes DURING
365
+ # this turn's own tool loop: the block above is frozen for the whole
366
+ # turn (see comment above), so a mid-turn add/edit/delete can't safely
367
+ # update it without eating the miss on every remaining iteration of
368
+ # this turn. Instead it surfaces as a cheap tail delta (see below),
369
+ # gated the same notify-once way format_background_status and
370
+ # subagent_tracker_ already are — shown once when it first diverges
371
+ # from the frozen baseline, silent after, until it changes again.
372
+ _last_shown_manifest = _memory_manifest_baseline
373
+
319
374
  while True:
320
375
  checkpoint = load_checkpoint(project_dir)
321
376
  # If history is now shorter than as_of_index (e.g. /rewind cut
@@ -331,31 +386,26 @@ actually deleted.
331
386
  # takes effect starting next turn.
332
387
  asyncio.create_task(compact_checkpoint(project_dir, msgs, len(msgs), model))
333
388
 
334
- # Dynamic per-loop status (file-changes/process-status/summary/memory).
335
- # Collected as plain sections, then sent as ONE role:"user" message
336
- # appended AFTER the transcript — never as role:"system", never
337
- # prepended before it. Two reasons, both load-bearing (see TODO.md):
338
- # 1. anthropic.py always pulls every role:"system" message into one
339
- # block that the API places before `messages`, regardless of
340
- # Python-list order — so a role:"system" prepend here sits
341
- # between the two cache breakpoints (system_prompt_msg's, and
342
- # the rolling one anthropic.py/litellm.py set on the last
343
- # stable transcript message) no matter where in the list it's
344
- # built. Since this content changes almost every turn once a
345
- # checkpoint summary is active, that busts the rolling
346
- # breakpoint's match on most turns, even though the actual old
347
- # transcript messages never changed.
348
- # 2. Recency matters for the model too, not just for caching — the
349
- # freshest situational read (what changed since I last looked,
350
- # what's running now) belongs closest to the point where the
351
- # model has to act next, not buried ahead of the whole
352
- # conversation.
389
+ # Dynamic per-loop status (file-changes/process-status/summary, plus
390
+ # a memory delta — see below). Collected as plain sections, sent as
391
+ # ONE role:"user" message appended AFTER the transcript, flagged
392
+ # _ephemeral. This block changes on essentially every iteration
393
+ # (file writes, process starts/finishes, checkpoint summaries), so
394
+ # it stays out of any cache_control breakpoint on purpose — a
395
+ # role:"system" prepend for genuinely-every-iteration content would
396
+ # sit ahead of the rolling transcript breakpoint and bust it on
397
+ # most turns. The memory manifest doesn't belong to this category
398
+ # any more (see memory_manifest_msg above, built once per turn as
399
+ # its own cached role:"system" block) — what's built below for
400
+ # memory is only the rare mid-turn DELTA against that frozen
401
+ # baseline, which is exactly as volatile as file-changes/
402
+ # process-status and belongs in the same ephemeral tail bucket.
353
403
  # Wrapped in <system-reminder> — the same non-real-user-query marker
354
404
  # token_cutter's is_real_user_msg already recognizes — and flagged
355
- # _ephemeral so anthropic.py/litellm.py's cache-breakpoint walk-back
356
- # (which already exists for exactly this purpose — see both files'
357
- # bp_idx logic) skips past it and anchors on the last REAL stable
358
- # transcript message instead of on this ever-changing block.
405
+ # _ephemeral so anthropic.py/litellm.py/foundry.py's cache-breakpoint
406
+ # walk-back (see each file's bp_idx logic) skips past it and anchors
407
+ # on the last REAL stable transcript message instead of on this
408
+ # ever-changing block.
359
409
  status_sections = []
360
410
 
361
411
  # File changes — self-scoped to paths WE'VE read/written (see
@@ -384,27 +434,25 @@ actually deleted.
384
434
  if conversation_summary:
385
435
  status_sections.append(f"<conversation-summary>\n{conversation_summary}\n</conversation-summary>")
386
436
 
387
- # Long-term memory manifest — two scopes (see memory_tool_'s docstring):
388
- # global (~/.micro-cc/memory.json, shared across ALL projects) and
389
- # project (this project_dir's own ~/.micro-cc/projects/.../memory.json).
390
- # Key+description only; call memory_(action="get", key=..., scope=...)
391
- # to expand an entry's full content.
392
- manifest_parts = []
393
- global_memory_items = memory_store_.list_memories()
394
- if global_memory_items:
395
- manifest_parts.append(
396
- "Global (all projects):\n"
397
- + "\n".join(f"- {it['key']} — {it['description']}" for it in global_memory_items)
398
- )
399
- project_memory_items = memory_store_.list_memories(project_dir=project_dir)
400
- if project_memory_items:
401
- manifest_parts.append(
402
- "Project (this project_dir only):\n"
403
- + "\n".join(f"- {it['key']} — {it['description']}" for it in project_memory_items)
437
+ # Memory-manifest DELTA only — the manifest itself lives in
438
+ # memory_manifest_msg (role:"system", built once above, frozen for
439
+ # this whole turn so it can carry its own cache_control breakpoint).
440
+ # If Claude called memory_ mid-turn, that frozen block is now stale;
441
+ # rebuilding it here instead of just noting the diff would touch a
442
+ # role:"system" block mid-turn and cost every remaining iteration of
443
+ # this turn a cache miss on it. So: compare live state to the frozen
444
+ # baseline, and if it moved, say so once in the ephemeral tail
445
+ # (same notify-once contract as format_background_status/
446
+ # subagent_tracker_) — silent again once shown, until it moves
447
+ # again. Fully caught up again next turn, when a fresh
448
+ # memory_manifest_msg gets built from current state.
449
+ current_manifest = _build_memory_manifest()
450
+ if current_manifest is not None and current_manifest != _last_shown_manifest:
451
+ status_sections.append(
452
+ "<memory-manifest-update>\nMemory changed since this turn started "
453
+ f"(still call memory_(action=\"get\", ...) to expand an entry):\n\n{current_manifest}\n</memory-manifest-update>"
404
454
  )
405
- if manifest_parts:
406
- manifest = "\n\n".join(manifest_parts)
407
- status_sections.append(f"<memory-manifest>\n{manifest}\n</memory-manifest>")
455
+ _last_shown_manifest = current_manifest
408
456
 
409
457
  status_msgs = []
410
458
  if status_sections:
@@ -414,15 +462,17 @@ actually deleted.
414
462
  "_ephemeral": True,
415
463
  })
416
464
 
417
- # Assemble: stable system prompt FIRST so the cached prefix (tools +
418
- # system prompt) stays byte-identical across loops; the transcript
419
- # follows so its own rolling breakpoint covers it; dynamic status
420
- # goes LAST, after both cache anchors, never in front of content
421
- # meant to be reused from cache. msgs itself never carries a system
422
- # message (see system_prompt_msg above) — token_cutter still drops
423
- # any role=="system" it finds in `rest` regardless, which is what
424
- # self-heals a project whose messages.jsonl predates this and still
425
- # has one stored in history.
465
+ # Assemble: system_prompt_msg then memory_manifest_msg — both
466
+ # role:"system", both frozen for this whole turn, each its own
467
+ # cache_control breakpoint — so the cached prefix stays byte-
468
+ # identical across loops in the common case; the transcript follows
469
+ # so its own rolling breakpoint covers it; dynamic status (incl. the
470
+ # memory delta) goes LAST, after every cache anchor, never in front
471
+ # of content meant to be reused from cache. msgs itself never
472
+ # carries a system message (see system_prompt_msg above) —
473
+ # token_cutter still drops any role=="system" it finds in `rest`
474
+ # regardless, which is what self-heals a project whose
475
+ # messages.jsonl predates this and still has one stored in history.
426
476
  if checkpoint:
427
477
  trimmed_rest = token_cutter(
428
478
  msgs, max_tokens,
@@ -432,7 +482,12 @@ actually deleted.
432
482
  )
433
483
  else:
434
484
  trimmed_rest = token_cutter(msgs, max_tokens)
435
- trimmed_loop_msgs = [system_prompt_msg] + trimmed_rest + status_msgs
485
+ trimmed_loop_msgs = (
486
+ [system_prompt_msg]
487
+ + ([memory_manifest_msg] if memory_manifest_msg else [])
488
+ + trimmed_rest
489
+ + status_msgs
490
+ )
436
491
 
437
492
  try:
438
493
  # Imported lazily, per-branch: anthropic/openai are the two
@@ -360,15 +360,22 @@ def _msgs_to_responses(anthropic_msgs):
360
360
  reasoning each turn from the transcript; no signature/id replay needed
361
361
  for multi-turn tool calling to work.
362
362
  """
363
- instructions = None
363
+ instructions_parts = []
364
364
  out = []
365
365
  for msg in anthropic_msgs:
366
366
  role = msg.get("role")
367
367
  content = msg.get("content", "")
368
368
 
369
369
  if role == "system":
370
- if isinstance(content, str):
371
- instructions = content
370
+ # Callers may send more than one system message now (e.g.
371
+ # claude_loop_'s system_prompt_msg + memory_manifest_msg, each
372
+ # its own cache_control-bearing block for the Anthropic-native
373
+ # path) — the Responses API has no such multi-block system
374
+ # concept, so join them into the one `instructions` string in
375
+ # message order instead of last-write-wins dropping everything
376
+ # but the last one.
377
+ if isinstance(content, str) and content:
378
+ instructions_parts.append(content)
372
379
  continue
373
380
 
374
381
  if role == "assistant" and isinstance(content, list):
@@ -439,7 +446,7 @@ def _msgs_to_responses(anthropic_msgs):
439
446
  # --- Simple string content (user/assistant plain turns) ---
440
447
  out.append({"role": role, "content": content})
441
448
 
442
- return instructions, out
449
+ return ("\n\n".join(instructions_parts) if instructions_parts else None), out
443
450
 
444
451
 
445
452
  def _wrap_responses_output(resp):
@@ -251,10 +251,21 @@ async def _track_survivors(pgid: int, command: str, cwd: str, project_dir: str)
251
251
  "started_at": time.time(),
252
252
  "pgid": pgid,
253
253
  "output_path": str(out_path),
254
+ "notified": False,
254
255
  }
255
256
  return out_path if found else None
256
257
 
257
258
 
259
+ # One-shot "this pid just finished" notices, queued by list_background_processes'
260
+ # pruning below and drained by format_background_status. Mirrors
261
+ # subagent_tracker_'s notified-flag pattern (see that module's docstring):
262
+ # a still-running process is worth polling for on demand (monitor_/bash_
263
+ # ps), a state CHANGE (started, finished) is worth pushing once. Plain
264
+ # list, not keyed by pid, since a finished entry is removed from
265
+ # _background_procs the same tick it's queued here — nothing to dedupe.
266
+ _finished_procs: list[dict] = []
267
+
268
+
258
269
  def list_background_processes() -> list[dict]:
259
270
  """Prune exited processes and return survivors — the same self-scoped
260
271
  set format_background_status formats for the model, structured for a
@@ -263,7 +274,11 @@ def list_background_processes() -> list[dict]:
263
274
  try:
264
275
  os.kill(pid, 0)
265
276
  except ProcessLookupError:
266
- del _background_procs[pid]
277
+ info = _background_procs.pop(pid)
278
+ _finished_procs.append({
279
+ "pid": pid, "command": info["command"], "cwd": info["cwd"],
280
+ "output_path": info["output_path"],
281
+ })
267
282
  # A pgid's drain tasks (see _drain_to_file) outlive any one child pid
268
283
  # entry — only cancel them once nothing left in _background_procs still
269
284
  # points at that pgid, i.e. every pid ps -g ever found there is gone.
@@ -306,14 +321,32 @@ def get_output_tail(pid: int, max_chars: int = 2000) -> str | None:
306
321
 
307
322
 
308
323
  def format_background_status() -> str | None:
309
- """Format survivors for the per-loop reminder."""
310
- procs = list_background_processes()
311
- if not procs:
324
+ """Format state CHANGES for the per-loop reminder — same notify-once
325
+ contract as subagent_tracker_ (see that module's docstring: still-alive
326
+ is worth showing once, up front; after that it's pull-on-demand via
327
+ monitor_/bash_, not a repeated per-loop restatement). Concretely:
328
+ - a newly-tracked pid gets exactly one "started" line, right after
329
+ list_background_processes() has already pruned/queued any finished
330
+ ones for this same call, so the notified flag flips only for pids
331
+ that are still genuinely alive this tick.
332
+ - a pid that died since the last check gets exactly one "finished" line,
333
+ drained from _finished_procs and not re-shown.
334
+ An already-notified, still-running pid is silent here — the model was
335
+ told once it exists and can check on it itself; no per-iteration nag.
336
+ """
337
+ procs = list_background_processes() # prunes dead pids into _finished_procs first
338
+ lines = []
339
+ if _finished_procs:
340
+ for f in _finished_procs:
341
+ lines.append(f" PID {f['pid']} finished: {f['command']} (cwd={f['cwd']}) — output was captured to {f['output_path']}, read it if you need to check what happened")
342
+ _finished_procs.clear()
343
+ new_procs = [p for p in procs if not _background_procs[p["pid"]]["notified"]]
344
+ for p in new_procs:
345
+ lines.append(f" PID {p['pid']} started: {p['command']} (cwd={p['cwd']}) — stop with bash_(\"kill {p['pid']}\"), check output with monitor_([\"{p['pid']}\"])")
346
+ _background_procs[p["pid"]]["notified"] = True
347
+ if not lines:
312
348
  return None
313
- lines = ["Processes still running in the background (started via bash_):"]
314
- for p in procs:
315
- lines.append(f" PID {p['pid']}: {p['command']} (cwd={p['cwd']}, running {p['age']}s) — stop with bash_(\"kill {p['pid']}\"), check output with monitor_([\"{p['pid']}\"])")
316
- return "\n".join(lines)
349
+ return "Background process update:\n" + "\n".join(lines)
317
350
 
318
351
 
319
352
  def _kill_tree(proc):
@@ -80,7 +80,7 @@ async def handle_tool_result(app, event, container, tool_call_rows, approval_row
80
80
  # delta finishes trickling out — the pacing throttles the source, so there's
81
81
  # never a backlog to burst-flush later (see StreamingRow's docstring).
82
82
  _FRAME = 0.012
83
- _CHARS_PER_TICK = 3
83
+ _CHARS_PER_TICK = 6
84
84
 
85
85
 
86
86
  async def _stream_delta(app, event, container, mode):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: micro-cc
3
- Version: 0.2.76
3
+ Version: 0.2.78
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: Copyright (c) 2026 George Salapa. All rights reserved.
File without changes
File without changes
File without changes