dulus 3.8.0__tar.gz → 3.9.1__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 (335) hide show
  1. {dulus-3.8.0/dulus.egg-info → dulus-3.9.1}/PKG-INFO +15 -3
  2. {dulus-3.8.0 → dulus-3.9.1}/README.md +14 -2
  3. {dulus-3.8.0 → dulus-3.9.1}/docs/news.md +4 -0
  4. {dulus-3.8.0 → dulus-3.9.1/dulus.egg-info}/PKG-INFO +15 -3
  5. {dulus-3.8.0 → dulus-3.9.1}/dulus.egg-info/SOURCES.txt +3 -0
  6. {dulus-3.8.0 → dulus-3.9.1}/dulus.egg-info/top_level.txt +1 -0
  7. {dulus-3.8.0 → dulus-3.9.1}/dulus.py +153 -0
  8. {dulus-3.8.0 → dulus-3.9.1}/dulus_mcp/client.py +14 -1
  9. dulus-3.9.1/dulus_mcp/commands.py +182 -0
  10. dulus-3.9.1/dulus_mcp/hub.py +845 -0
  11. {dulus-3.8.0 → dulus-3.9.1}/pyproject.toml +2 -1
  12. dulus-3.9.1/updater.py +178 -0
  13. {dulus-3.8.0 → dulus-3.9.1}/LICENSE +0 -0
  14. {dulus-3.8.0 → dulus-3.9.1}/MANIFEST.in +0 -0
  15. {dulus-3.8.0 → dulus-3.9.1}/agent.py +0 -0
  16. {dulus-3.8.0 → dulus-3.9.1}/analytics.py +0 -0
  17. {dulus-3.8.0 → dulus-3.9.1}/backend/__init__.py +0 -0
  18. {dulus-3.8.0 → dulus-3.9.1}/backend/agents_bridge.py +0 -0
  19. {dulus-3.8.0 → dulus-3.9.1}/backend/compressor.py +0 -0
  20. {dulus-3.8.0 → dulus-3.9.1}/backend/context.py +0 -0
  21. {dulus-3.8.0 → dulus-3.9.1}/backend/githook.py +0 -0
  22. {dulus-3.8.0 → dulus-3.9.1}/backend/marketplace.py +0 -0
  23. {dulus-3.8.0 → dulus-3.9.1}/backend/mempalace_bridge.py +0 -0
  24. {dulus-3.8.0 → dulus-3.9.1}/backend/personas.py +0 -0
  25. {dulus-3.8.0 → dulus-3.9.1}/backend/plugins.py +0 -0
  26. {dulus-3.8.0 → dulus-3.9.1}/backend/server.py +0 -0
  27. {dulus-3.8.0 → dulus-3.9.1}/backend/tasks.py +0 -0
  28. {dulus-3.8.0 → dulus-3.9.1}/batch_api.py +0 -0
  29. {dulus-3.8.0 → dulus-3.9.1}/checkpoint/__init__.py +0 -0
  30. {dulus-3.8.0 → dulus-3.9.1}/checkpoint/hooks.py +0 -0
  31. {dulus-3.8.0 → dulus-3.9.1}/checkpoint/store.py +0 -0
  32. {dulus-3.8.0 → dulus-3.9.1}/checkpoint/types.py +0 -0
  33. {dulus-3.8.0 → dulus-3.9.1}/claude_code_watcher.py +0 -0
  34. {dulus-3.8.0 → dulus-3.9.1}/clipboard_utils.py +0 -0
  35. {dulus-3.8.0 → dulus-3.9.1}/cloudsave.py +0 -0
  36. {dulus-3.8.0 → dulus-3.9.1}/common.py +0 -0
  37. {dulus-3.8.0 → dulus-3.9.1}/compaction.py +0 -0
  38. {dulus-3.8.0 → dulus-3.9.1}/config.py +0 -0
  39. {dulus-3.8.0 → dulus-3.9.1}/context.py +0 -0
  40. {dulus-3.8.0 → dulus-3.9.1}/data/__init__.py +0 -0
  41. {dulus-3.8.0 → dulus-3.9.1}/data/active_persona.json +0 -0
  42. {dulus-3.8.0 → dulus-3.9.1}/data/marketplace.json +0 -0
  43. {dulus-3.8.0 → dulus-3.9.1}/data/personas.json +0 -0
  44. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/__init__.py +0 -0
  45. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/composio/__init__.py +0 -0
  46. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/composio/composio_plugin/__init__.py +0 -0
  47. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
  48. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
  49. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/composio/plugin.json +0 -0
  50. {dulus-3.8.0 → dulus-3.9.1}/data/plugins/composio/plugin_tool.py +0 -0
  51. {dulus-3.8.0 → dulus-3.9.1}/data/tasks.json +0 -0
  52. {dulus-3.8.0 → dulus-3.9.1}/docs/API.md +0 -0
  53. {dulus-3.8.0 → dulus-3.9.1}/docs/BRAND_GUIDE.md +0 -0
  54. {dulus-3.8.0 → dulus-3.9.1}/docs/CHANGELOG.md +0 -0
  55. {dulus-3.8.0 → dulus-3.9.1}/docs/CONTRIBUTING.md +0 -0
  56. {dulus-3.8.0 → dulus-3.9.1}/docs/DEPLOYMENT.md +0 -0
  57. {dulus-3.8.0 → dulus-3.9.1}/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
  58. {dulus-3.8.0 → dulus-3.9.1}/docs/FAQ.md +0 -0
  59. {dulus-3.8.0 → dulus-3.9.1}/docs/GETTING_STARTED.md +0 -0
  60. {dulus-3.8.0 → dulus-3.9.1}/docs/INVESTOR_BRIEF.md +0 -0
  61. {dulus-3.8.0 → dulus-3.9.1}/docs/README.md +0 -0
  62. {dulus-3.8.0 → dulus-3.9.1}/docs/README_AR.md +0 -0
  63. {dulus-3.8.0 → dulus-3.9.1}/docs/README_EN.md +0 -0
  64. {dulus-3.8.0 → dulus-3.9.1}/docs/README_ES.md +0 -0
  65. {dulus-3.8.0 → dulus-3.9.1}/docs/README_FR.md +0 -0
  66. {dulus-3.8.0 → dulus-3.9.1}/docs/README_JA.md +0 -0
  67. {dulus-3.8.0 → dulus-3.9.1}/docs/README_KO.md +0 -0
  68. {dulus-3.8.0 → dulus-3.9.1}/docs/README_PT.md +0 -0
  69. {dulus-3.8.0 → dulus-3.9.1}/docs/README_RU.md +0 -0
  70. {dulus-3.8.0 → dulus-3.9.1}/docs/README_ZH.md +0 -0
  71. {dulus-3.8.0 → dulus-3.9.1}/docs/SECURITY.md +0 -0
  72. {dulus-3.8.0 → dulus-3.9.1}/docs/__init__.py +0 -0
  73. {dulus-3.8.0 → dulus-3.9.1}/docs/api.html +0 -0
  74. {dulus-3.8.0 → dulus-3.9.1}/docs/architecture.md +0 -0
  75. {dulus-3.8.0 → dulus-3.9.1}/docs/azure-speech-template.json +0 -0
  76. {dulus-3.8.0 → dulus-3.9.1}/docs/dashboard/index.html +0 -0
  77. {dulus-3.8.0 → dulus-3.9.1}/docs/divider.svg +0 -0
  78. {dulus-3.8.0 → dulus-3.9.1}/docs/dulus-bird.png +0 -0
  79. {dulus-3.8.0 → dulus-3.9.1}/docs/generate.py +0 -0
  80. {dulus-3.8.0 → dulus-3.9.1}/docs/hero.svg +0 -0
  81. {dulus-3.8.0 → dulus-3.9.1}/docs/index.html +0 -0
  82. {dulus-3.8.0 → dulus-3.9.1}/docs/nvidia-models.svg +0 -0
  83. {dulus-3.8.0 → dulus-3.9.1}/docs/particle-playground.html +0 -0
  84. {dulus-3.8.0 → dulus-3.9.1}/docs/personas/index.html +0 -0
  85. {dulus-3.8.0 → dulus-3.9.1}/docs/poetry-banner.png +0 -0
  86. {dulus-3.8.0 → dulus-3.9.1}/docs/preview.html +0 -0
  87. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-agents.svg +0 -0
  88. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-brainstorm.svg +0 -0
  89. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-bridges.svg +0 -0
  90. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-features.svg +0 -0
  91. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-freetier.svg +0 -0
  92. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-memory.svg +0 -0
  93. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-models.svg +0 -0
  94. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-perms.svg +0 -0
  95. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-plugins.svg +0 -0
  96. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-quickstart.svg +0 -0
  97. {dulus-3.8.0 → dulus-3.9.1}/docs/sec-ssj.svg +0 -0
  98. {dulus-3.8.0 → dulus-3.9.1}/docs/spinners.svg +0 -0
  99. {dulus-3.8.0 → dulus-3.9.1}/docs/split-pane.svg +0 -0
  100. {dulus-3.8.0 → dulus-3.9.1}/docs/terminal-boot.svg +0 -0
  101. {dulus-3.8.0 → dulus-3.9.1}/dulus.egg-info/dependency_links.txt +0 -0
  102. {dulus-3.8.0 → dulus-3.9.1}/dulus.egg-info/entry_points.txt +0 -0
  103. {dulus-3.8.0 → dulus-3.9.1}/dulus.egg-info/requires.txt +0 -0
  104. {dulus-3.8.0 → dulus-3.9.1}/dulus_gui.py +0 -0
  105. {dulus-3.8.0 → dulus-3.9.1}/dulus_gui_web.py +0 -0
  106. {dulus-3.8.0 → dulus-3.9.1}/dulus_mcp/__init__.py +0 -0
  107. {dulus-3.8.0 → dulus-3.9.1}/dulus_mcp/config.py +0 -0
  108. {dulus-3.8.0 → dulus-3.9.1}/dulus_mcp/tools.py +0 -0
  109. {dulus-3.8.0 → dulus-3.9.1}/dulus_mcp/types.py +0 -0
  110. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/__init__.py +0 -0
  111. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/add_dir_manager.py +0 -0
  112. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/afk_mode.py +0 -0
  113. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/approval_runtime.py +0 -0
  114. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/background_tasks.py +0 -0
  115. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/clipboard_utils.py +0 -0
  116. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/diff_visualizer.py +0 -0
  117. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/export_import.py +0 -0
  118. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/hook_engine.py +0 -0
  119. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/notification_manager.py +0 -0
  120. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/session_fork.py +0 -0
  121. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/shell_mode.py +0 -0
  122. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/todo_visualizer.py +0 -0
  123. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/tree_ls.py +0 -0
  124. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/wire_events.py +0 -0
  125. {dulus-3.8.0 → dulus-3.9.1}/dulus_tools/yolo_mode.py +0 -0
  126. {dulus-3.8.0 → dulus-3.9.1}/governance.py +0 -0
  127. {dulus-3.8.0 → dulus-3.9.1}/gui/__init__.py +0 -0
  128. {dulus-3.8.0 → dulus-3.9.1}/gui/agent_bridge.py +0 -0
  129. {dulus-3.8.0 → dulus-3.9.1}/gui/chat_widget.py +0 -0
  130. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/App.tsx +0 -0
  131. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/THIRD_PARTY_NOTICES.md +0 -0
  132. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/dist/index.html +0 -0
  133. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/dist/standalone.html +0 -0
  134. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/index.html +0 -0
  135. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/index.tsx +0 -0
  136. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/metadata.json +0 -0
  137. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/package-lock.json +0 -0
  138. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/package.json +0 -0
  139. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/types.ts +0 -0
  140. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/utils.ts +0 -0
  141. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/vertex-ai-proxy-interceptor.js +0 -0
  142. {dulus-3.8.0 → dulus-3.9.1}/gui/dulus_orchestrator/vite.config.ts +0 -0
  143. {dulus-3.8.0 → dulus-3.9.1}/gui/main_window.py +0 -0
  144. {dulus-3.8.0 → dulus-3.9.1}/gui/personas.py +0 -0
  145. {dulus-3.8.0 → dulus-3.9.1}/gui/sandbox_server.py +0 -0
  146. {dulus-3.8.0 → dulus-3.9.1}/gui/session_utils.py +0 -0
  147. {dulus-3.8.0 → dulus-3.9.1}/gui/settings_dialog.py +0 -0
  148. {dulus-3.8.0 → dulus-3.9.1}/gui/sidebar.py +0 -0
  149. {dulus-3.8.0 → dulus-3.9.1}/gui/tasks_view.py +0 -0
  150. {dulus-3.8.0 → dulus-3.9.1}/gui/themes.py +0 -0
  151. {dulus-3.8.0 → dulus-3.9.1}/gui/tool_panel.py +0 -0
  152. {dulus-3.8.0 → dulus-3.9.1}/input.py +0 -0
  153. {dulus-3.8.0 → dulus-3.9.1}/license_manager.py +0 -0
  154. {dulus-3.8.0 → dulus-3.9.1}/memory/__init__.py +0 -0
  155. {dulus-3.8.0 → dulus-3.9.1}/memory/audit.py +0 -0
  156. {dulus-3.8.0 → dulus-3.9.1}/memory/consolidator.py +0 -0
  157. {dulus-3.8.0 → dulus-3.9.1}/memory/context.py +0 -0
  158. {dulus-3.8.0 → dulus-3.9.1}/memory/offload.py +0 -0
  159. {dulus-3.8.0 → dulus-3.9.1}/memory/palace.py +0 -0
  160. {dulus-3.8.0 → dulus-3.9.1}/memory/scan.py +0 -0
  161. {dulus-3.8.0 → dulus-3.9.1}/memory/sessions.py +0 -0
  162. {dulus-3.8.0 → dulus-3.9.1}/memory/store.py +0 -0
  163. {dulus-3.8.0 → dulus-3.9.1}/memory/tools.py +0 -0
  164. {dulus-3.8.0 → dulus-3.9.1}/memory/types.py +0 -0
  165. {dulus-3.8.0 → dulus-3.9.1}/memory/vector_search.py +0 -0
  166. {dulus-3.8.0 → dulus-3.9.1}/multi_agent/__init__.py +0 -0
  167. {dulus-3.8.0 → dulus-3.9.1}/multi_agent/subagent.py +0 -0
  168. {dulus-3.8.0 → dulus-3.9.1}/multi_agent/tools.py +0 -0
  169. {dulus-3.8.0 → dulus-3.9.1}/offload_helper.py +0 -0
  170. {dulus-3.8.0 → dulus-3.9.1}/personality_config.py +0 -0
  171. {dulus-3.8.0 → dulus-3.9.1}/plugin/__init__.py +0 -0
  172. {dulus-3.8.0 → dulus-3.9.1}/plugin/autoadapter.py +0 -0
  173. {dulus-3.8.0 → dulus-3.9.1}/plugin/loader.py +0 -0
  174. {dulus-3.8.0 → dulus-3.9.1}/plugin/recommend.py +0 -0
  175. {dulus-3.8.0 → dulus-3.9.1}/plugin/store.py +0 -0
  176. {dulus-3.8.0 → dulus-3.9.1}/plugin/types.py +0 -0
  177. {dulus-3.8.0 → dulus-3.9.1}/providers.py +0 -0
  178. {dulus-3.8.0 → dulus-3.9.1}/sandbox/README.md +0 -0
  179. {dulus-3.8.0 → dulus-3.9.1}/sandbox/__init__.py +0 -0
  180. {dulus-3.8.0 → dulus-3.9.1}/sandbox/components.json +0 -0
  181. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
  182. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
  183. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/index.html +0 -0
  184. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/wallpaper-default.jpg +0 -0
  185. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/wallpapers/default.jpeg +0 -0
  186. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/wallpapers/light.jpeg +0 -0
  187. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/wallpapers/nature.jpeg +0 -0
  188. {dulus-3.8.0 → dulus-3.9.1}/sandbox/dist/wallpapers/tech.jpeg +0 -0
  189. {dulus-3.8.0 → dulus-3.9.1}/sandbox/eslint.config.js +0 -0
  190. {dulus-3.8.0 → dulus-3.9.1}/sandbox/index.html +0 -0
  191. {dulus-3.8.0 → dulus-3.9.1}/sandbox/info.md +0 -0
  192. {dulus-3.8.0 → dulus-3.9.1}/sandbox/package-lock.json +0 -0
  193. {dulus-3.8.0 → dulus-3.9.1}/sandbox/package.json +0 -0
  194. {dulus-3.8.0 → dulus-3.9.1}/sandbox/postcss.config.js +0 -0
  195. {dulus-3.8.0 → dulus-3.9.1}/sandbox/tailwind.config.js +0 -0
  196. {dulus-3.8.0 → dulus-3.9.1}/sandbox/tsconfig.app.json +0 -0
  197. {dulus-3.8.0 → dulus-3.9.1}/sandbox/tsconfig.json +0 -0
  198. {dulus-3.8.0 → dulus-3.9.1}/sandbox/tsconfig.node.json +0 -0
  199. {dulus-3.8.0 → dulus-3.9.1}/sandbox/vite.config.ts +0 -0
  200. {dulus-3.8.0 → dulus-3.9.1}/sandbox_bootstrap.py +0 -0
  201. {dulus-3.8.0 → dulus-3.9.1}/setup.cfg +0 -0
  202. {dulus-3.8.0 → dulus-3.9.1}/skill/__init__.py +0 -0
  203. {dulus-3.8.0 → dulus-3.9.1}/skill/builtin.py +0 -0
  204. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/LICENSE-obsidian-skills +0 -0
  205. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/README.md +0 -0
  206. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/defuddle/SKILL.md +0 -0
  207. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/json-canvas/SKILL.md +0 -0
  208. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
  209. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-bases/SKILL.md +0 -0
  210. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
  211. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-cli/SKILL.md +0 -0
  212. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
  213. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
  214. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
  215. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
  216. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/README.md +0 -0
  217. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/SKILL.md +0 -0
  218. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
  219. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
  220. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
  221. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
  222. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
  223. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
  224. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
  225. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
  226. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
  227. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
  228. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
  229. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
  230. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
  231. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
  232. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
  233. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
  234. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
  235. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
  236. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
  237. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
  238. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
  239. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
  240. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
  241. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/legal/drop.md +0 -0
  242. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
  243. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/methods.md +0 -0
  244. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
  245. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/references/state-machine.md +0 -0
  246. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
  247. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/badbool.py +0 -0
  248. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/brokers.py +0 -0
  249. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/cdp.py +0 -0
  250. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/config.py +0 -0
  251. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/crypto.py +0 -0
  252. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/dossier.py +0 -0
  253. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
  254. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/emailer.py +0 -0
  255. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/ledger.py +0 -0
  256. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/legal.py +0 -0
  257. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/paths.py +0 -0
  258. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/pdd.py +0 -0
  259. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/registry.py +0 -0
  260. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/report.py +0 -0
  261. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/scan.py +0 -0
  262. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/storage.py +0 -0
  263. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/tiers.py +0 -0
  264. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/scripts/vectors.py +0 -0
  265. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
  266. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
  267. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
  268. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
  269. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
  270. {dulus-3.8.0 → dulus-3.9.1}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
  271. {dulus-3.8.0 → dulus-3.9.1}/skill/clawhub.py +0 -0
  272. {dulus-3.8.0 → dulus-3.9.1}/skill/executor.py +0 -0
  273. {dulus-3.8.0 → dulus-3.9.1}/skill/loader.py +0 -0
  274. {dulus-3.8.0 → dulus-3.9.1}/skill/tools.py +0 -0
  275. {dulus-3.8.0 → dulus-3.9.1}/skills.py +0 -0
  276. {dulus-3.8.0 → dulus-3.9.1}/soul.py +0 -0
  277. {dulus-3.8.0 → dulus-3.9.1}/spinner.py +0 -0
  278. {dulus-3.8.0 → dulus-3.9.1}/string_utils.py +0 -0
  279. {dulus-3.8.0 → dulus-3.9.1}/subagent.py +0 -0
  280. {dulus-3.8.0 → dulus-3.9.1}/task/__init__.py +0 -0
  281. {dulus-3.8.0 → dulus-3.9.1}/task/store.py +0 -0
  282. {dulus-3.8.0 → dulus-3.9.1}/task/tools.py +0 -0
  283. {dulus-3.8.0 → dulus-3.9.1}/task/types.py +0 -0
  284. {dulus-3.8.0 → dulus-3.9.1}/tests/test_afk_yolo.py +0 -0
  285. {dulus-3.8.0 → dulus-3.9.1}/tests/test_approval_runtime.py +0 -0
  286. {dulus-3.8.0 → dulus-3.9.1}/tests/test_background_task_tools.py +0 -0
  287. {dulus-3.8.0 → dulus-3.9.1}/tests/test_background_tasks.py +0 -0
  288. {dulus-3.8.0 → dulus-3.9.1}/tests/test_checkpoint.py +0 -0
  289. {dulus-3.8.0 → dulus-3.9.1}/tests/test_clipboard_utils.py +0 -0
  290. {dulus-3.8.0 → dulus-3.9.1}/tests/test_compaction.py +0 -0
  291. {dulus-3.8.0 → dulus-3.9.1}/tests/test_diff_view.py +0 -0
  292. {dulus-3.8.0 → dulus-3.9.1}/tests/test_diff_visualization.py +0 -0
  293. {dulus-3.8.0 → dulus-3.9.1}/tests/test_display_blocks.py +0 -0
  294. {dulus-3.8.0 → dulus-3.9.1}/tests/test_export_import.py +0 -0
  295. {dulus-3.8.0 → dulus-3.9.1}/tests/test_hook_engine.py +0 -0
  296. {dulus-3.8.0 → dulus-3.9.1}/tests/test_injection_fix.py +0 -0
  297. {dulus-3.8.0 → dulus-3.9.1}/tests/test_license.py +0 -0
  298. {dulus-3.8.0 → dulus-3.9.1}/tests/test_mcp.py +0 -0
  299. {dulus-3.8.0 → dulus-3.9.1}/tests/test_memory.py +0 -0
  300. {dulus-3.8.0 → dulus-3.9.1}/tests/test_notification_manager.py +0 -0
  301. {dulus-3.8.0 → dulus-3.9.1}/tests/test_plugin.py +0 -0
  302. {dulus-3.8.0 → dulus-3.9.1}/tests/test_session_fork.py +0 -0
  303. {dulus-3.8.0 → dulus-3.9.1}/tests/test_shell_mode.py +0 -0
  304. {dulus-3.8.0 → dulus-3.9.1}/tests/test_skills.py +0 -0
  305. {dulus-3.8.0 → dulus-3.9.1}/tests/test_steer_input.py +0 -0
  306. {dulus-3.8.0 → dulus-3.9.1}/tests/test_subagent.py +0 -0
  307. {dulus-3.8.0 → dulus-3.9.1}/tests/test_task.py +0 -0
  308. {dulus-3.8.0 → dulus-3.9.1}/tests/test_telegram_buffer.py +0 -0
  309. {dulus-3.8.0 → dulus-3.9.1}/tests/test_think_tool.py +0 -0
  310. {dulus-3.8.0 → dulus-3.9.1}/tests/test_todo_tool.py +0 -0
  311. {dulus-3.8.0 → dulus-3.9.1}/tests/test_todo_visualization.py +0 -0
  312. {dulus-3.8.0 → dulus-3.9.1}/tests/test_tool_registry.py +0 -0
  313. {dulus-3.8.0 → dulus-3.9.1}/tests/test_tree_ls.py +0 -0
  314. {dulus-3.8.0 → dulus-3.9.1}/tests/test_voice.py +0 -0
  315. {dulus-3.8.0 → dulus-3.9.1}/tests/test_wire_events.py +0 -0
  316. {dulus-3.8.0 → dulus-3.9.1}/tmux_offloader.py +0 -0
  317. {dulus-3.8.0 → dulus-3.9.1}/tmux_tools.py +0 -0
  318. {dulus-3.8.0 → dulus-3.9.1}/tool_registry.py +0 -0
  319. {dulus-3.8.0 → dulus-3.9.1}/tools.py +0 -0
  320. {dulus-3.8.0 → dulus-3.9.1}/ui/__init__.py +0 -0
  321. {dulus-3.8.0 → dulus-3.9.1}/ui/input.py +0 -0
  322. {dulus-3.8.0 → dulus-3.9.1}/ui/render.py +0 -0
  323. {dulus-3.8.0 → dulus-3.9.1}/voice/__init__.py +0 -0
  324. {dulus-3.8.0 → dulus-3.9.1}/voice/audio_utils.py +0 -0
  325. {dulus-3.8.0 → dulus-3.9.1}/voice/keyterms.py +0 -0
  326. {dulus-3.8.0 → dulus-3.9.1}/voice/recorder.py +0 -0
  327. {dulus-3.8.0 → dulus-3.9.1}/voice/stt.py +0 -0
  328. {dulus-3.8.0 → dulus-3.9.1}/voice/tts.py +0 -0
  329. {dulus-3.8.0 → dulus-3.9.1}/voice/wake_word.py +0 -0
  330. {dulus-3.8.0 → dulus-3.9.1}/webbridge/__init__.py +0 -0
  331. {dulus-3.8.0 → dulus-3.9.1}/webbridge/core.py +0 -0
  332. {dulus-3.8.0 → dulus-3.9.1}/webbridge/tools.py +0 -0
  333. {dulus-3.8.0 → dulus-3.9.1}/webchat.py +0 -0
  334. {dulus-3.8.0 → dulus-3.9.1}/webchat_server.py +0 -0
  335. {dulus-3.8.0 → dulus-3.9.1}/welcome.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 3.8.0
3
+ Version: 3.9.1
4
4
  Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
5
5
  Author: KevRojo
6
6
  License: GPL-3.0
@@ -134,6 +134,10 @@ SET /sticky_input ON since the first run for the best experience!
134
134
 
135
135
  > Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
136
136
 
137
+ - **🔄 Self-update — the fleet moves as one.** Dulus now keeps itself current: a quiet, cached, non-blocking check against PyPI at startup, and if there's a newer release it upgrades in place. `/update`, `/update now`, `/update status`, `/update on|off`. On by default (the organism heals fastest when every node runs the latest), handles PEP 668, and never blocks your boot. 🦅
138
+
139
+ - **🔌 MCP Marketplace — 2000+ servers, zero friction.** MCP was always powerful but nobody used it — too much friction: find a server, dig up its launch command, hand-edit JSON, hope it connects. Now: `/mcp search <anything>` browses **2000+ servers** (official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list, cached & offline-safe), and `/mcp install <name>` installs *and auto-connects* in one shot — tools go live in the same session. Plus a Windows fix so node servers (`npx`) actually launch. From "never touched MCP" to "connected in 5 seconds." 🦅
140
+
137
141
  - **🌍 dulus.work — THE HUB. The secret is out.** This is what Kevin had been hiding (even from Dulus itself 😂): **a global hub where every Dulus instance on the planet shares what it learns.** Your Dulus finds a fix, polishes a skill, adapts a plugin → it can publish it to the hub → every other Dulus in the world can pull it down. Not an update server — an **organism**. Every installation is a node. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Other agents have N devs improving one product (linear). Dulus makes every user a dev (exponential). The pieces have been quietly shipping for months — SelfImprove engine, Auto-Adapter, the skills format, cloudsave — and now they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
138
142
 
139
143
  - **Dulus Agent — Telegram communities.** Dulus evolved from a CLI tool into a live AI agent inside Telegram groups. The first fully autonomous AI moderating and conversing in real communities — not a bot, not a filter, a real agent. Groups pay in $DULUS to activate him. We host Dulus for them — that's the business model. The CLI stays free forever. This is the paid layer. 🦅🇩🇴
@@ -245,9 +249,17 @@ Answering `a` at any prompt upgrades the session to accept-all.
245
249
 
246
250
  Drop a `.mcp.json` in your project — every server registers instantly as `mcp__server__tool`. stdio, SSE, and HTTP transports.
247
251
 
252
+ **New: a built-in MCP marketplace.** Browse **2000+ servers** (the official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list) and install any of them by name — no hunting for the launch command, no hand-editing JSON. Zero friction.
253
+
248
254
  ```
249
- /mcp # list servers and their tools
250
- /mcp add <name> <cmd> # add a stdio server
255
+ /mcp list [query] # browse the 2000+ server catalog
256
+ /mcp search <query> # search every source at once
257
+ /mcp install <name> # install by name — auto-connects, tools go live
258
+ /mcp installed # what's installed + live status
259
+ /mcp runtimes # which runtimes you have (node/python/docker)
260
+
261
+ /mcp # list configured servers and their tools
262
+ /mcp add <name> <cmd> # add a stdio server manually
251
263
  /mcp reload # reconnect all
252
264
  ```
253
265
 
@@ -68,6 +68,10 @@ SET /sticky_input ON since the first run for the best experience!
68
68
 
69
69
  > Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
70
70
 
71
+ - **🔄 Self-update — the fleet moves as one.** Dulus now keeps itself current: a quiet, cached, non-blocking check against PyPI at startup, and if there's a newer release it upgrades in place. `/update`, `/update now`, `/update status`, `/update on|off`. On by default (the organism heals fastest when every node runs the latest), handles PEP 668, and never blocks your boot. 🦅
72
+
73
+ - **🔌 MCP Marketplace — 2000+ servers, zero friction.** MCP was always powerful but nobody used it — too much friction: find a server, dig up its launch command, hand-edit JSON, hope it connects. Now: `/mcp search <anything>` browses **2000+ servers** (official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list, cached & offline-safe), and `/mcp install <name>` installs *and auto-connects* in one shot — tools go live in the same session. Plus a Windows fix so node servers (`npx`) actually launch. From "never touched MCP" to "connected in 5 seconds." 🦅
74
+
71
75
  - **🌍 dulus.work — THE HUB. The secret is out.** This is what Kevin had been hiding (even from Dulus itself 😂): **a global hub where every Dulus instance on the planet shares what it learns.** Your Dulus finds a fix, polishes a skill, adapts a plugin → it can publish it to the hub → every other Dulus in the world can pull it down. Not an update server — an **organism**. Every installation is a node. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Other agents have N devs improving one product (linear). Dulus makes every user a dev (exponential). The pieces have been quietly shipping for months — SelfImprove engine, Auto-Adapter, the skills format, cloudsave — and now they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
72
76
 
73
77
  - **Dulus Agent — Telegram communities.** Dulus evolved from a CLI tool into a live AI agent inside Telegram groups. The first fully autonomous AI moderating and conversing in real communities — not a bot, not a filter, a real agent. Groups pay in $DULUS to activate him. We host Dulus for them — that's the business model. The CLI stays free forever. This is the paid layer. 🦅🇩🇴
@@ -179,9 +183,17 @@ Answering `a` at any prompt upgrades the session to accept-all.
179
183
 
180
184
  Drop a `.mcp.json` in your project — every server registers instantly as `mcp__server__tool`. stdio, SSE, and HTTP transports.
181
185
 
186
+ **New: a built-in MCP marketplace.** Browse **2000+ servers** (the official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list) and install any of them by name — no hunting for the launch command, no hand-editing JSON. Zero friction.
187
+
182
188
  ```
183
- /mcp # list servers and their tools
184
- /mcp add <name> <cmd> # add a stdio server
189
+ /mcp list [query] # browse the 2000+ server catalog
190
+ /mcp search <query> # search every source at once
191
+ /mcp install <name> # install by name — auto-connects, tools go live
192
+ /mcp installed # what's installed + live status
193
+ /mcp runtimes # which runtimes you have (node/python/docker)
194
+
195
+ /mcp # list configured servers and their tools
196
+ /mcp add <name> <cmd> # add a stdio server manually
185
197
  /mcp reload # reconnect all
186
198
  ```
187
199
 
@@ -2,6 +2,10 @@
2
2
 
3
3
  ## 🔥🔥🔥 News (Pacific Time)
4
4
 
5
+ - July 11, 2026: **🔄 Self-update — every Dulus stays on the latest, automatically.** The whole point of the organism is that a fix found by one Dulus reaches every other Dulus fast. So now Dulus keeps *itself* current: on startup it does a quiet, cached (6h TTL), non-blocking check against PyPI, and if there's a newer release it upgrades in place — no `pip install -U` dance, no stale nodes drifting behind. New `/update` command with everything you'd expect: `/update` (check + upgrade if newer), `/update now` (force), `/update check` (look but don't touch), `/update status` (see installed vs latest), and `/update on|off` to toggle the automatic startup check (on by default). Handles PEP 668 externally-managed Pythons automatically, and the check *never* blocks or crashes your boot — if PyPI is down or you're offline, Dulus just carries on. The fleet moves as one. 🦅🇩🇴
6
+
7
+ - July 11, 2026: **🔌 MCP Marketplace — 2000+ servers, zero friction, install by name.** MCP (Model Context Protocol) was always one of Dulus's most powerful features and almost nobody used it — the friction killed it. You had to *find* a server somewhere, dig up its exact launch command, hand-edit a `.mcp.json`, and pray it connected. Dead on arrival. So we built a real marketplace on top of it. `/mcp list` and `/mcp search <anything>` now browse a live catalog of **2000+ servers** pulled from the official [registry.modelcontextprotocol.io](https://registry.modelcontextprotocol.io) (the metaregistry backed by Anthropic, GitHub, Microsoft & PulseMCP) plus the community awesome-mcp list — deduped, cached with a 6h TTL, and **offline-safe** (falls back to a curated set if you're on a plane). Then `/mcp install <name>` does the whole thing in one shot: resolves the launch command, writes the config, **connects, verifies the tools, and hot-reloads them live into your current session** — no restart, no JSON, no manual anything. `/mcp installed` shows what's live with a green/red status dot, `/mcp runtimes` tells you which runtimes (node/python/docker) you've got. Also shipped a Windows launcher fix so node-based servers (`npx`, which ships as `npx.cmd` on Windows) actually start instead of silently failing — probably the real reason MCP "never worked" for a lot of Windows users. From *"I've never touched MCP"* to *"connected in 5 seconds."* 🦅🇩🇴
8
+
5
9
  - July 10, 2026: **🎙️ Dulus has his own voice — Deepgram lands as a first-class voice provider.** Voice on Dulus just leveled up: **Deepgram nova-3** is now the default STT backend the moment a key exists — ~300ms cloud transcription with native keyterm boosting, so your coding vocabulary (`grep`, `MemPalace`, `TypeScript`) comes out spelled right instead of whatever Whisper hallucinates on a noisy mic. And on the speaking side, **Aura-2** joins the TTS chain as the `deepgram` provider with natural per-language voices (`aura-2-celeste-es` for Spanish, `aura-2-thalia-en` for English). Zero SDK, zero heavy model downloads — both talk plain HTTPS, which means voice finally works great on laptops that can't afford a 1GB local Whisper model warming up for 30 seconds. Getting in is stupid simple: create a free account at [deepgram.com](https://deepgram.com) (**$200 in credit, no credit card** — that's roughly 775 hours of transcription), drop the key in `DEEPGRAM_API_KEY` or `deepgram_api_key` in your config, and `/voice`, `/wake` and `/say` are instantly running on it. No key? Nothing changes — local Whisper keeps working as always, and `DULUS_WAKE_FORCE_LOCAL=1` brings it back anytime. The eagle speaks now. 🦅🇩🇴
6
10
 
7
11
  - July 3, 2026: **🌍 dulus.work — THE HUB, revealed.** The secret is out: dulus.work is a **global hub where every Dulus instance on the planet shares what it learns**. Your Dulus finds a fix, refines a skill, adapts a plugin locally → publishes it to the hub → every other Dulus in the world pulls it down. Not an update server — an **organism**: every installation is a node, every user effectively becomes a contributor. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Traditional agents scale linearly (N devs improving 1 product); Dulus scales with its network (every user is a dev). The building blocks have been quietly shipping for months — the SelfImprove engine, the Auto-Adapter, the skills format, cloudsave — and dulus.work is where they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dulus
3
- Version: 3.8.0
3
+ Version: 3.9.1
4
4
  Summary: Use IA as an agent without an API key. $0. Harvest your browser session for Gemini (guest, no login), Claude.ai, Claude Code, Kimi.com, Qwen, DeepSeek — drive them like an API, no per-token billing. Plus LiteLLM (100+ paid providers), local models via Ollama, /lang in 34 languages, Mesa Redonda, voice, OCR, MemPalace, embedded sandbox OS.
5
5
  Author: KevRojo
6
6
  License: GPL-3.0
@@ -134,6 +134,10 @@ SET /sticky_input ON since the first run for the best experience!
134
134
 
135
135
  > Full changelog: [`docs/news.md`](docs/news.md) · Inside the REPL: `/news`
136
136
 
137
+ - **🔄 Self-update — the fleet moves as one.** Dulus now keeps itself current: a quiet, cached, non-blocking check against PyPI at startup, and if there's a newer release it upgrades in place. `/update`, `/update now`, `/update status`, `/update on|off`. On by default (the organism heals fastest when every node runs the latest), handles PEP 668, and never blocks your boot. 🦅
138
+
139
+ - **🔌 MCP Marketplace — 2000+ servers, zero friction.** MCP was always powerful but nobody used it — too much friction: find a server, dig up its launch command, hand-edit JSON, hope it connects. Now: `/mcp search <anything>` browses **2000+ servers** (official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list, cached & offline-safe), and `/mcp install <name>` installs *and auto-connects* in one shot — tools go live in the same session. Plus a Windows fix so node servers (`npx`) actually launch. From "never touched MCP" to "connected in 5 seconds." 🦅
140
+
137
141
  - **🌍 dulus.work — THE HUB. The secret is out.** This is what Kevin had been hiding (even from Dulus itself 😂): **a global hub where every Dulus instance on the planet shares what it learns.** Your Dulus finds a fix, polishes a skill, adapts a plugin → it can publish it to the hub → every other Dulus in the world can pull it down. Not an update server — an **organism**. Every installation is a node. *The fix a Dulus in Tokyo finds at 3AM, the one in Santo Domingo has by sunrise.* Other agents have N devs improving one product (linear). Dulus makes every user a dev (exponential). The pieces have been quietly shipping for months — SelfImprove engine, Auto-Adapter, the skills format, cloudsave — and now they converge. Domain is live, execution underway. 🦅🇩🇴 → [dulus.work](https://dulus.work)
138
142
 
139
143
  - **Dulus Agent — Telegram communities.** Dulus evolved from a CLI tool into a live AI agent inside Telegram groups. The first fully autonomous AI moderating and conversing in real communities — not a bot, not a filter, a real agent. Groups pay in $DULUS to activate him. We host Dulus for them — that's the business model. The CLI stays free forever. This is the paid layer. 🦅🇩🇴
@@ -245,9 +249,17 @@ Answering `a` at any prompt upgrades the session to accept-all.
245
249
 
246
250
  Drop a `.mcp.json` in your project — every server registers instantly as `mcp__server__tool`. stdio, SSE, and HTTP transports.
247
251
 
252
+ **New: a built-in MCP marketplace.** Browse **2000+ servers** (the official [modelcontextprotocol.io](https://registry.modelcontextprotocol.io) registry + the awesome-mcp list) and install any of them by name — no hunting for the launch command, no hand-editing JSON. Zero friction.
253
+
248
254
  ```
249
- /mcp # list servers and their tools
250
- /mcp add <name> <cmd> # add a stdio server
255
+ /mcp list [query] # browse the 2000+ server catalog
256
+ /mcp search <query> # search every source at once
257
+ /mcp install <name> # install by name — auto-connects, tools go live
258
+ /mcp installed # what's installed + live status
259
+ /mcp runtimes # which runtimes you have (node/python/docker)
260
+
261
+ /mcp # list configured servers and their tools
262
+ /mcp add <name> <cmd> # add a stdio server manually
251
263
  /mcp reload # reconnect all
252
264
  ```
253
265
 
@@ -31,6 +31,7 @@ tmux_offloader.py
31
31
  tmux_tools.py
32
32
  tool_registry.py
33
33
  tools.py
34
+ updater.py
34
35
  webchat.py
35
36
  webchat_server.py
36
37
  welcome.py
@@ -119,7 +120,9 @@ dulus.egg-info/requires.txt
119
120
  dulus.egg-info/top_level.txt
120
121
  dulus_mcp/__init__.py
121
122
  dulus_mcp/client.py
123
+ dulus_mcp/commands.py
122
124
  dulus_mcp/config.py
125
+ dulus_mcp/hub.py
123
126
  dulus_mcp/tools.py
124
127
  dulus_mcp/types.py
125
128
  dulus_tools/__init__.py
@@ -42,6 +42,7 @@ tmux_tools
42
42
  tool_registry
43
43
  tools
44
44
  ui
45
+ updater
45
46
  voice
46
47
  webbridge
47
48
  webchat
@@ -82,6 +82,10 @@ Slash commands in REPL:
82
82
  /plugin enable/disable name Toggle plugin
83
83
  /plugin update name Update a plugin
84
84
  /plugin recommend [ctx] Recommend plugins for context
85
+ /update Check PyPI and update Dulus if a newer version exists
86
+ /update now Force update to the latest release
87
+ /update on|off Toggle the automatic update check at startup (default: on)
88
+ /update status Show installed version, latest, and auto-check setting
85
89
  /tasks List all tasks
86
90
  /tasks create <subject> Quick-create a task
87
91
  /tasks start/done/cancel <id> Update task status
@@ -5213,6 +5217,14 @@ def cmd_skill(args: str, state, config) -> bool:
5213
5217
  def cmd_mcp(args: str, _state, config) -> bool:
5214
5218
  """Show MCP server status, or manage servers.
5215
5219
 
5220
+ Marketplace (0-friction discovery of 2000+ servers):
5221
+ /mcp list [query] — browse the catalog (official registry + awesome list)
5222
+ /mcp search <q> — search every source for matching servers
5223
+ /mcp install <name>— install by name (auto-connects, no manual config)
5224
+ /mcp installed — show installed servers and their live status
5225
+ /mcp runtimes — show which runtimes (node/python/docker) are available
5226
+
5227
+ Manual management:
5216
5228
  /mcp — list all configured servers and their tools
5217
5229
  /mcp reload — reconnect all servers and refresh tools
5218
5230
  /mcp reload <name> — reconnect a single server
@@ -5227,6 +5239,33 @@ def cmd_mcp(args: str, _state, config) -> bool:
5227
5239
  parts = args.split() if args.strip() else []
5228
5240
  subcmd = parts[0].lower() if parts else ""
5229
5241
 
5242
+ # ── Marketplace subcommands (0-friction discovery + install) ──────────────
5243
+ # Wired to dulus_mcp.commands.handle_mcp_command so users can browse a
5244
+ # catalog of 2000+ servers and install by name without knowing the raw
5245
+ # command. Falls through to the classic manager for reload/add/remove.
5246
+ _MARKETPLACE_SUBCMDS = {"list", "ls", "install", "search", "find", "s",
5247
+ "installed", "runtimes", "browse", "catalog"}
5248
+ if subcmd in _MARKETPLACE_SUBCMDS:
5249
+ try:
5250
+ from dulus_mcp.commands import handle_mcp_command
5251
+ _mkt_args = list(parts)
5252
+ if subcmd in ("browse", "catalog"):
5253
+ _mkt_args[0] = "list"
5254
+ _out = handle_mcp_command(_mkt_args)
5255
+ print(_out)
5256
+ # After a successful install, hot-reload so the new MCP tools go
5257
+ # live in this same session (no restart needed).
5258
+ if subcmd == "install" and _out.strip().startswith("\u2705"):
5259
+ try:
5260
+ reload_mcp()
5261
+ ok("MCP tools reloaded — new server is live.")
5262
+ except Exception:
5263
+ pass
5264
+ return True
5265
+ except Exception as _mkt_e:
5266
+ err(f"MCP marketplace error: {_mkt_e}")
5267
+ return True
5268
+
5230
5269
  if subcmd == "reload":
5231
5270
  target = parts[1] if len(parts) > 1 else ""
5232
5271
  if target:
@@ -8451,6 +8490,89 @@ def cmd_profile(args: str, state, config) -> bool:
8451
8490
  return True
8452
8491
 
8453
8492
 
8493
+ def cmd_update(args: str, state, config) -> bool:
8494
+ """Self-update Dulus from PyPI — keep every node on the latest release.
8495
+
8496
+ /update — check PyPI and update now if a newer version exists
8497
+ /update now — force an update to the latest release
8498
+ /update check — just check, don't install
8499
+ /update on — enable the automatic startup check (default)
8500
+ /update off — disable the automatic startup check
8501
+ /update status — show current version, latest, and the auto-check setting
8502
+ """
8503
+ try:
8504
+ import updater
8505
+ except Exception as e:
8506
+ err(f"Updater unavailable: {e}")
8507
+ return True
8508
+
8509
+ sub = (args or "").strip().lower()
8510
+
8511
+ # Toggle the automatic startup check
8512
+ if sub in ("on", "enable"):
8513
+ config["auto_update"] = True
8514
+ try:
8515
+ from config import save_config
8516
+ save_config(config)
8517
+ except Exception:
8518
+ pass
8519
+ ok("Automatic update check enabled — Dulus will check PyPI at startup.")
8520
+ return True
8521
+ if sub in ("off", "disable"):
8522
+ config["auto_update"] = False
8523
+ try:
8524
+ from config import save_config
8525
+ save_config(config)
8526
+ except Exception:
8527
+ pass
8528
+ info("Automatic update check disabled. Run /update anytime to check manually.")
8529
+ return True
8530
+
8531
+ if sub == "status":
8532
+ current = updater.get_installed_version()
8533
+ latest = updater.get_latest_version() or "(unreachable)"
8534
+ auto = config.get("auto_update", True)
8535
+ info(f"Installed: {current}")
8536
+ info(f"Latest on PyPI: {latest}")
8537
+ info(f"Auto-check at startup: {'ON' if auto else 'OFF'}")
8538
+ return True
8539
+
8540
+ if sub == "check":
8541
+ available, current, latest = updater.is_update_available()
8542
+ if not latest:
8543
+ info(f"Couldn't reach PyPI. Installed: {current}")
8544
+ elif available:
8545
+ ok(f"Update available: {current} -> {latest}. Run /update now")
8546
+ else:
8547
+ ok(f"You're on the latest Dulus ({current}). 🦅")
8548
+ return True
8549
+
8550
+ # Default (no arg) and "now" → check + install if newer
8551
+ available, current, latest = updater.is_update_available()
8552
+ if sub != "now":
8553
+ if not latest:
8554
+ info(f"Couldn't reach PyPI. Installed: {current}")
8555
+ return True
8556
+ if not available:
8557
+ ok(f"You're already on the latest Dulus ({current}). 🦅")
8558
+ return True
8559
+ info(f"Updating {current} -> {latest} ...")
8560
+
8561
+ # Perform the upgrade (force for "now", or when an update is available)
8562
+ target = latest if latest else None
8563
+ if sub == "now" and not available and latest and current == latest:
8564
+ ok(f"Already on {current}. Nothing to do.")
8565
+ return True
8566
+ print(" ⏳ Running pip install --upgrade dulus ...")
8567
+ success, message = updater.perform_update(target if available else None)
8568
+ if success:
8569
+ ok(message)
8570
+ info("Restart Dulus to run the new version.")
8571
+ else:
8572
+ err(message)
8573
+ return True
8574
+
8575
+
8454
8576
  def cmd_news(args: str, state, config) -> bool:
8455
8577
  """Show the latest news from docs/news.md."""
8456
8578
  news_file = Path(__file__).parent / "docs" / "news.md"
@@ -9749,6 +9871,9 @@ COMMANDS = {
9749
9871
  "exit": cmd_exit,
9750
9872
  "quit": cmd_exit,
9751
9873
  "resume": cmd_resume,
9874
+ "update": cmd_update,
9875
+ "upgrade": cmd_update,
9876
+ "selfupdate": cmd_update,
9752
9877
  "news": cmd_news,
9753
9878
  "batch": cmd_batch,
9754
9879
  "claude_batch": cmd_claude_batch,
@@ -10317,6 +10442,34 @@ def repl(config: dict, initial_prompt: str = None):
10317
10442
  except Exception:
10318
10443
  pass
10319
10444
 
10445
+ # ── Auto-update check (on by default; /update off to disable) ─────────
10446
+ # Keep every Dulus in the world on the latest release: the organism
10447
+ # heals fastest when fixes propagate instantly. This is intentionally
10448
+ # quiet & fast — cached PyPI check (6h TTL), short timeout, never blocks
10449
+ # or crashes the boot. Set auto_update=False (or /update off) to skip.
10450
+ if config.get("auto_update", True):
10451
+ try:
10452
+ import updater as _updater
10453
+ _avail, _cur, _latest = _updater.is_update_available()
10454
+ if _avail:
10455
+ print()
10456
+ info(f" 🆕 A new Dulus is available: {_cur} → {_latest}")
10457
+ if config.get("auto_update_install", True):
10458
+ # Kevin's call: force the update at startup so we keep
10459
+ # the whole fleet in sync. Set auto_update_install=False
10460
+ # to only notify instead of installing.
10461
+ print(" ⏳ Updating automatically...")
10462
+ _ok, _msg = _updater.perform_update(_latest)
10463
+ if _ok:
10464
+ ok(f" {_msg} Restart to run {_latest}.")
10465
+ else:
10466
+ info(f" Auto-update skipped: {_msg}")
10467
+ info(" Run /update now to try manually.")
10468
+ else:
10469
+ info(" Run /update now to upgrade.")
10470
+ except Exception:
10471
+ pass # never let the update check break the boot
10472
+
10320
10473
  # First-run /harvest — wizard sets `pending_first_run_harvest` to
10321
10474
  # the provider name the user picked (claude / kimi / gemini / qwen /
10322
10475
  # deepseek). We auto-fire the matching command so they actually
@@ -36,13 +36,26 @@ class StdioTransport:
36
36
 
37
37
  def start(self) -> None:
38
38
  env = {**os.environ, **(self._config.env or {})}
39
- cmd = [self._config.command] + list(self._config.args or [])
39
+ # ── Windows launcher resolution ──────────────────────────────────
40
+ # On Windows, node tooling ships as .cmd shims (npx.cmd, npm.cmd) and
41
+ # Python's subprocess won't find a bare "npx" without the extension.
42
+ # shutil.which() resolves the real executable (respecting PATHEXT), so
43
+ # "npx"/"uvx"/"uv" all launch correctly. Falls back to the raw name.
44
+ import shutil as _shutil
45
+ _launcher = self._config.command
46
+ _resolved = _shutil.which(_launcher)
47
+ if _resolved:
48
+ _launcher = _resolved
49
+ cmd = [_launcher] + list(self._config.args or [])
50
+ # On Windows, .cmd/.bat shims must run through the shell layer.
51
+ _use_shell = os.name == "nt" and str(_launcher).lower().endswith((".cmd", ".bat"))
40
52
  self._process = subprocess.Popen(
41
53
  cmd,
42
54
  stdin=subprocess.PIPE,
43
55
  stdout=subprocess.PIPE,
44
56
  stderr=subprocess.PIPE,
45
57
  env=env,
58
+ shell=_use_shell,
46
59
  )
47
60
  self._running = True
48
61
  self._reader = threading.Thread(target=self._read_loop, daemon=True)
@@ -0,0 +1,182 @@
1
+ """MCP command handler for Dulus CLI.
2
+
3
+ Commands:
4
+ /mcp list [query] — List available MCP servers
5
+ /mcp installed — List installed MCP servers
6
+ /mcp install <name> — Install an MCP server (0-friction)
7
+ /mcp remove <name> — Uninstall an MCP server
8
+ /mcp status [name] — Check MCP server status
9
+ /mcp search <query> — Search for MCP servers
10
+ /mcp runtimes — Show available runtimes
11
+ """
12
+ from __future__ import annotations
13
+
14
+ from .hub import (
15
+ MCPServerEntry,
16
+ detect_available_runtimes,
17
+ get_server,
18
+ get_status,
19
+ install,
20
+ list_all,
21
+ list_installed,
22
+ search,
23
+ uninstall,
24
+ )
25
+
26
+
27
+ def handle_mcp_command(args: list[str]) -> str:
28
+ """Handle /mcp commands. Returns a formatted string response."""
29
+ if not args:
30
+ return _help_text()
31
+
32
+ cmd = args[0].lower()
33
+ rest = args[1:] if len(args) > 1 else []
34
+
35
+ if cmd in ("list", "ls"):
36
+ return _cmd_list(rest[0] if rest else None)
37
+ if cmd == "installed":
38
+ return _cmd_installed()
39
+ if cmd in ("install", "add"):
40
+ return _cmd_install(rest)
41
+ if cmd in ("remove", "rm", "uninstall"):
42
+ return _cmd_remove(rest[0] if rest else "")
43
+ if cmd == "status":
44
+ return _cmd_status(rest[0] if rest else None)
45
+ if cmd in ("search", "find", "s"):
46
+ return _cmd_search(rest[0] if rest else "")
47
+ if cmd == "runtimes":
48
+ return _cmd_runtimes()
49
+ if cmd in ("help", "h", "?"):
50
+ return _help_text()
51
+
52
+ return f"Unknown MCP command: '{cmd}'. Type /mcp help for usage."
53
+
54
+
55
+ def _help_text() -> str:
56
+ return """🛠️ MCP Marketplace Commands
57
+
58
+ /mcp list [query] — Browse available MCP servers
59
+ /mcp installed — List your installed MCP servers
60
+ /mcp install <name> — Install an MCP server (0-friction!)
61
+ /mcp remove <name> — Uninstall an MCP server
62
+ /mcp status [name] — Check connection status
63
+ /mcp search <query> — Search for MCP servers
64
+ /mcp runtimes — Check available runtimes
65
+
66
+ 💡 Popular: /mcp install filesystem | /mcp install github | /mcp install fetch
67
+ """
68
+
69
+
70
+ def _cmd_list(query: str | None) -> str:
71
+ servers = list_all(query)
72
+ if not servers:
73
+ return "No MCP servers found." + (f" (query: '{query}')" if query else "")
74
+
75
+ lines = [f"📦 Available MCP Servers{' (filtered)' if query else ''}:", ""]
76
+ for s in servers:
77
+ icon = "✅" if s.installed else "⬜"
78
+ source_icon = {"official": "🌟", "dulus": "🏠", "installed": "📌"}.get(s.source, "📦")
79
+ runtime = f" [{s.runtime}]" if s.runtime else ""
80
+ lines.append(f" {icon} {source_icon} {s.name}{runtime} — {s.description}")
81
+ lines.append("")
82
+ lines.append(f"Total: {len(servers)} servers. Install with /mcp install <name>")
83
+ return "\n".join(lines)
84
+
85
+
86
+ def _cmd_installed() -> str:
87
+ servers = list_installed()
88
+ if not servers:
89
+ return "📭 No MCP servers installed. Run /mcp list to browse available servers."
90
+
91
+ lines = ["📌 Installed MCP Servers:", ""]
92
+ for s in servers:
93
+ status = get_status(s.config_name)
94
+ state_icon = {"connected": "🟢", "error": "🔴", "not_configured": "⚪"}.get(status["state"], "⚪")
95
+ tools = f" ({status['tools']} tools)" if status["tools"] > 0 else ""
96
+ lines.append(f" {state_icon} {s.config_name}{tools} — {s.description}")
97
+ if status.get("error"):
98
+ lines.append(f" ⚠️ {status['error']}")
99
+ return "\n".join(lines)
100
+
101
+
102
+ def _cmd_install(args: list[str]) -> str:
103
+ if not args:
104
+ return "Usage: /mcp install <name>"
105
+
106
+ name = args[0]
107
+ user_args = args[1:] if len(args) > 1 else None
108
+
109
+ # Check if server exists
110
+ entry = get_server(name)
111
+ if entry is None:
112
+ available = [s.name for s in list_all()]
113
+ did_you_mean = _fuzzy_match(name, available)
114
+ msg = f"❌ MCP server '{name}' not found."
115
+ if did_you_mean:
116
+ msg += f" Did you mean: {', '.join(did_you_mean)}?"
117
+ msg += "\nRun /mcp list to see available servers."
118
+ return msg
119
+
120
+ # Check if it requires args
121
+ if entry.requires_args and not user_args:
122
+ return f"⚠️ '{name}' requires arguments: {entry.arg_prompt}\nUsage: /mcp install {name} <args>"
123
+
124
+ success, message = install(name, user_args=user_args)
125
+ icon = "✅" if success else "❌"
126
+ return f"{icon} {message}"
127
+
128
+
129
+ def _cmd_remove(name: str) -> str:
130
+ if not name:
131
+ return "Usage: /mcp remove <name>"
132
+ success, message = uninstall(name)
133
+ icon = "✅" if success else "❌"
134
+ return f"{icon} {message}"
135
+
136
+
137
+ def _cmd_status(name: str | None) -> str:
138
+ if name:
139
+ status = get_status(name)
140
+ state_icon = {"connected": "🟢", "error": "🔴", "not_configured": "⚪"}.get(status["state"], "⚪")
141
+ lines = [f"{state_icon} {status['name']}: {status['state']}"]
142
+ if status["tools"] > 0:
143
+ lines.append(f" Tools: {status['tools']}")
144
+ if status.get("description"):
145
+ lines.append(f" Server: {status['description']} v{status.get('version', '?')}")
146
+ if status.get("error"):
147
+ lines.append(f" ⚠️ Error: {status['error']}")
148
+ return "\n".join(lines)
149
+
150
+ # Status for all installed
151
+ return _cmd_installed()
152
+
153
+
154
+ def _cmd_search(query: str) -> str:
155
+ if not query:
156
+ return "Usage: /mcp search <query>"
157
+ results = search(query)
158
+ if not results:
159
+ return f"🔍 No results for '{query}'."
160
+
161
+ lines = [f"🔍 Search results for '{query}':", ""]
162
+ for s in results:
163
+ icon = "✅" if s.installed else "⬜"
164
+ lines.append(f" {icon} {s.name} — {s.description}")
165
+ return "\n".join(lines)
166
+
167
+
168
+ def _cmd_runtimes() -> str:
169
+ runtimes = detect_available_runtimes()
170
+ lines = ["🔧 Available Runtimes:", ""]
171
+ for name, available in runtimes.items():
172
+ icon = "✅" if available else "❌"
173
+ lines.append(f" {icon} {name}")
174
+ lines.append("")
175
+ lines.append("MCP servers need their runtime to be available.")
176
+ return "\n".join(lines)
177
+
178
+
179
+ def _fuzzy_match(query: str, candidates: list[str], max_results: int = 3) -> list[str]:
180
+ """Find close matches for a query string."""
181
+ from difflib import get_close_matches
182
+ return get_close_matches(query.lower(), [c.lower() for c in candidates], n=max_results, cutoff=0.5)