dulus 3.7.1__tar.gz → 3.9.0__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.
- {dulus-3.7.1/dulus.egg-info → dulus-3.9.0}/PKG-INFO +15 -4
- {dulus-3.7.1 → dulus-3.9.0}/README.md +14 -3
- {dulus-3.7.1 → dulus-3.9.0}/common.py +21 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/news.md +4 -0
- {dulus-3.7.1 → dulus-3.9.0/dulus.egg-info}/PKG-INFO +15 -4
- {dulus-3.7.1 → dulus-3.9.0}/dulus.egg-info/SOURCES.txt +2 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus.py +41 -6
- {dulus-3.7.1 → dulus-3.9.0}/dulus_mcp/client.py +14 -1
- dulus-3.9.0/dulus_mcp/commands.py +182 -0
- dulus-3.9.0/dulus_mcp/hub.py +845 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/tree_ls.py +156 -156
- {dulus-3.7.1 → dulus-3.9.0}/plugin/store.py +2 -1
- {dulus-3.7.1 → dulus-3.9.0}/pyproject.toml +1 -1
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_tree_ls.py +86 -86
- {dulus-3.7.1 → dulus-3.9.0}/voice/stt.py +80 -5
- {dulus-3.7.1 → dulus-3.9.0}/voice/tts.py +101 -11
- {dulus-3.7.1 → dulus-3.9.0}/welcome.py +1 -1
- {dulus-3.7.1 → dulus-3.9.0}/LICENSE +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/MANIFEST.in +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/agent.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/analytics.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/agents_bridge.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/compressor.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/context.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/githook.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/marketplace.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/mempalace_bridge.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/personas.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/plugins.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/server.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/backend/tasks.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/batch_api.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/checkpoint/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/checkpoint/hooks.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/checkpoint/store.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/checkpoint/types.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/claude_code_watcher.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/clipboard_utils.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/cloudsave.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/compaction.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/config.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/context.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/active_persona.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/marketplace.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/personas.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/composio/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/composio/composio_plugin/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/composio/plugin.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/plugins/composio/plugin_tool.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/data/tasks.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/API.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/BRAND_GUIDE.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/CHANGELOG.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/CONTRIBUTING.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/DEPLOYMENT.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/Dulus_AI_Whitepaper_(v2.0).pdf +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/FAQ.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/GETTING_STARTED.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/INVESTOR_BRIEF.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_AR.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_EN.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_ES.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_FR.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_JA.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_KO.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_PT.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_RU.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/README_ZH.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/SECURITY.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/api.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/architecture.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/azure-speech-template.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/dashboard/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/divider.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/dulus-bird.png +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/generate.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/hero.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/nvidia-models.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/particle-playground.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/personas/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/poetry-banner.png +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/preview.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-agents.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-brainstorm.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-bridges.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-features.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-freetier.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-memory.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-models.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-perms.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-plugins.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-quickstart.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/sec-ssj.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/spinners.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/split-pane.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/docs/terminal-boot.svg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus.egg-info/dependency_links.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus.egg-info/entry_points.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus.egg-info/requires.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus.egg-info/top_level.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_gui.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_gui_web.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_mcp/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_mcp/config.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_mcp/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_mcp/types.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/add_dir_manager.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/afk_mode.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/approval_runtime.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/background_tasks.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/clipboard_utils.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/diff_visualizer.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/export_import.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/hook_engine.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/notification_manager.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/session_fork.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/shell_mode.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/todo_visualizer.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/wire_events.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/dulus_tools/yolo_mode.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/governance.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/agent_bridge.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/chat_widget.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/App.tsx +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/THIRD_PARTY_NOTICES.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/dist/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/dist/standalone.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/index.tsx +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/metadata.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/package-lock.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/package.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/types.ts +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/utils.ts +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/vertex-ai-proxy-interceptor.js +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/dulus_orchestrator/vite.config.ts +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/main_window.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/personas.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/sandbox_server.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/session_utils.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/settings_dialog.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/sidebar.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/tasks_view.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/themes.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/gui/tool_panel.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/input.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/license_manager.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/audit.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/consolidator.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/context.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/offload.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/palace.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/scan.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/sessions.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/store.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/types.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/memory/vector_search.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/multi_agent/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/multi_agent/subagent.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/multi_agent/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/offload_helper.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/personality_config.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/plugin/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/plugin/autoadapter.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/plugin/loader.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/plugin/recommend.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/plugin/types.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/providers.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/README.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/components.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/wallpaper-default.jpg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/wallpapers/default.jpeg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/wallpapers/light.jpeg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/wallpapers/nature.jpeg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/dist/wallpapers/tech.jpeg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/eslint.config.js +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/index.html +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/info.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/package-lock.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/package.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/postcss.config.js +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/tailwind.config.js +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/tsconfig.app.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/tsconfig.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/tsconfig.node.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox/vite.config.ts +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/sandbox_bootstrap.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/setup.cfg +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/builtin.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/LICENSE-obsidian-skills +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/README.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/defuddle/SKILL.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/json-canvas/SKILL.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/json-canvas/references/EXAMPLES.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-bases/SKILL.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-bases/references/FUNCTIONS_REFERENCE.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-cli/SKILL.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-markdown/SKILL.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-markdown/references/CALLOUTS.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-markdown/references/EMBEDS.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/obsidian-markdown/references/PROPERTIES.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/README.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/SKILL.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/addresses.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/advancedbackgroundchecks.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/beenverified.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/clustal.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/clustrmaps.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/cyberbackgroundchecks.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/familytreenow.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/fastpeoplesearch.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/intelius.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/mylife.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/nuwber.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/peekyou.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/peoplefinders.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/radaris.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/rehold.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/searchpeoplefree.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/socialcatfish.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/spokeo.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/thatsthem.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/truepeoplesearch.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/usphonebook.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/brokers/whitepages.json +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/legal/ccpa.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/legal/drop.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/legal/gdpr.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/methods.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/site-playbooks.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/references/state-machine.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/autopilot.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/badbool.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/brokers.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/cdp.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/config.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/crypto.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/dossier.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/email_modes.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/emailer.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/ledger.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/legal.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/paths.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/pdd.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/registry.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/report.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/scan.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/storage.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/tiers.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/scripts/vectors.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/templates/consent/authorization.md +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/templates/emails/ccpa-authorized-agent.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/templates/emails/ccpa-deletion.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/templates/emails/ccpa-indirect-deletion.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/templates/emails/gdpr-erasure.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/bundled/unbroker/templates/emails/generic-optout.txt +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/clawhub.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/executor.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/loader.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skill/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/skills.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/soul.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/spinner.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/string_utils.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/subagent.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/task/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/task/store.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/task/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/task/types.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_afk_yolo.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_approval_runtime.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_background_task_tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_background_tasks.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_checkpoint.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_clipboard_utils.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_compaction.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_diff_view.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_diff_visualization.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_display_blocks.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_export_import.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_hook_engine.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_injection_fix.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_license.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_mcp.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_memory.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_notification_manager.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_plugin.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_session_fork.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_shell_mode.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_skills.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_steer_input.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_subagent.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_task.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_telegram_buffer.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_think_tool.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_todo_tool.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_todo_visualization.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_tool_registry.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_voice.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tests/test_wire_events.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tmux_offloader.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tmux_tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tool_registry.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/ui/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/ui/input.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/ui/render.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/voice/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/voice/audio_utils.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/voice/keyterms.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/voice/recorder.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/voice/wake_word.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/webbridge/__init__.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/webbridge/core.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/webbridge/tools.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/webchat.py +0 -0
- {dulus-3.7.1 → dulus-3.9.0}/webchat_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.9.0
|
|
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,8 @@ 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
|
+
- **🔌 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." 🦅
|
|
138
|
+
|
|
137
139
|
- **🌍 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
140
|
|
|
139
141
|
- **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 +247,17 @@ Answering `a` at any prompt upgrades the session to accept-all.
|
|
|
245
247
|
|
|
246
248
|
Drop a `.mcp.json` in your project — every server registers instantly as `mcp__server__tool`. stdio, SSE, and HTTP transports.
|
|
247
249
|
|
|
250
|
+
**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.
|
|
251
|
+
|
|
248
252
|
```
|
|
249
|
-
/mcp
|
|
250
|
-
/mcp
|
|
253
|
+
/mcp list [query] # browse the 2000+ server catalog
|
|
254
|
+
/mcp search <query> # search every source at once
|
|
255
|
+
/mcp install <name> # install by name — auto-connects, tools go live
|
|
256
|
+
/mcp installed # what's installed + live status
|
|
257
|
+
/mcp runtimes # which runtimes you have (node/python/docker)
|
|
258
|
+
|
|
259
|
+
/mcp # list configured servers and their tools
|
|
260
|
+
/mcp add <name> <cmd> # add a stdio server manually
|
|
251
261
|
/mcp reload # reconnect all
|
|
252
262
|
```
|
|
253
263
|
|
|
@@ -305,7 +315,8 @@ Dulus is proudly supported by these startup programs — powering our infrastruc
|
|
|
305
315
|
<a href="https://amplitude.com/startups"><img src="https://www.google.com/s2/favicons?domain=amplitude.com&sz=128" height="38" alt="Amplitude" title="Amplitude Startup Scholarship"></a>
|
|
306
316
|
<a href="https://www.digitalocean.com/hatch"><img src="https://cdn.simpleicons.org/digitalocean" height="38" alt="DigitalOcean" title="DigitalOcean Hatch"></a>
|
|
307
317
|
<a href="https://www.notion.com/startups"><img src="https://cdn.simpleicons.org/notion" height="38" alt="Notion" title="Notion for Startups"></a>
|
|
308
|
-
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a
|
|
318
|
+
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a>
|
|
319
|
+
<a href="https://deepgram.com/startups"><img src="https://www.google.com/s2/favicons?domain=deepgram.com&sz=128" height="38" alt="Deepgram" title="Deepgram Startup Program — powering Dulus's voice"></a>
|
|
309
320
|
</p>
|
|
310
321
|
|
|
311
322
|
<p align="center"><i>Want to support Dulus? → <a href="https://github.com/sponsors/KevRojo">GitHub Sponsors</a> 🦅</i></p>
|
|
@@ -68,6 +68,8 @@ 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
|
+
- **🔌 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." 🦅
|
|
72
|
+
|
|
71
73
|
- **🌍 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
74
|
|
|
73
75
|
- **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 +181,17 @@ Answering `a` at any prompt upgrades the session to accept-all.
|
|
|
179
181
|
|
|
180
182
|
Drop a `.mcp.json` in your project — every server registers instantly as `mcp__server__tool`. stdio, SSE, and HTTP transports.
|
|
181
183
|
|
|
184
|
+
**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.
|
|
185
|
+
|
|
182
186
|
```
|
|
183
|
-
/mcp
|
|
184
|
-
/mcp
|
|
187
|
+
/mcp list [query] # browse the 2000+ server catalog
|
|
188
|
+
/mcp search <query> # search every source at once
|
|
189
|
+
/mcp install <name> # install by name — auto-connects, tools go live
|
|
190
|
+
/mcp installed # what's installed + live status
|
|
191
|
+
/mcp runtimes # which runtimes you have (node/python/docker)
|
|
192
|
+
|
|
193
|
+
/mcp # list configured servers and their tools
|
|
194
|
+
/mcp add <name> <cmd> # add a stdio server manually
|
|
185
195
|
/mcp reload # reconnect all
|
|
186
196
|
```
|
|
187
197
|
|
|
@@ -239,7 +249,8 @@ Dulus is proudly supported by these startup programs — powering our infrastruc
|
|
|
239
249
|
<a href="https://amplitude.com/startups"><img src="https://www.google.com/s2/favicons?domain=amplitude.com&sz=128" height="38" alt="Amplitude" title="Amplitude Startup Scholarship"></a>
|
|
240
250
|
<a href="https://www.digitalocean.com/hatch"><img src="https://cdn.simpleicons.org/digitalocean" height="38" alt="DigitalOcean" title="DigitalOcean Hatch"></a>
|
|
241
251
|
<a href="https://www.notion.com/startups"><img src="https://cdn.simpleicons.org/notion" height="38" alt="Notion" title="Notion for Startups"></a>
|
|
242
|
-
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a
|
|
252
|
+
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a>
|
|
253
|
+
<a href="https://deepgram.com/startups"><img src="https://www.google.com/s2/favicons?domain=deepgram.com&sz=128" height="38" alt="Deepgram" title="Deepgram Startup Program — powering Dulus's voice"></a>
|
|
243
254
|
</p>
|
|
244
255
|
|
|
245
256
|
<p align="center"><i>Want to support Dulus? → <a href="https://github.com/sponsors/KevRojo">GitHub Sponsors</a> 🦅</i></p>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import json
|
|
3
|
+
from pathlib import Path
|
|
3
4
|
|
|
4
5
|
# ── Import slash completer helpers ──
|
|
5
6
|
try:
|
|
@@ -131,6 +132,26 @@ def ok(msg: str): print(clr(msg, "green"))
|
|
|
131
132
|
def warn(msg: str): print(clr(f"Warning: {msg}", "yellow"))
|
|
132
133
|
def err(msg: str): print(clr(f"Error: {msg}", "red"), file=sys.stderr)
|
|
133
134
|
|
|
135
|
+
def pip_install_cmd(*packages: str) -> list[str]:
|
|
136
|
+
"""Build a pip-install command that works on externally-managed Pythons.
|
|
137
|
+
|
|
138
|
+
Debian 12+ / Ubuntu 23.04+ mark the system Python as externally managed
|
|
139
|
+
(PEP 668): any bare `pip install` dies with `externally-managed-environment`
|
|
140
|
+
unless `--break-system-packages` is passed. That flag only exists on
|
|
141
|
+
pip >= 23, so we add it exactly when the marker file is present (which
|
|
142
|
+
implies a modern pip) instead of always.
|
|
143
|
+
"""
|
|
144
|
+
import sysconfig
|
|
145
|
+
cmd = [sys.executable, "-m", "pip", "install"]
|
|
146
|
+
try:
|
|
147
|
+
marker = Path(sysconfig.get_path("stdlib")) / "EXTERNALLY-MANAGED"
|
|
148
|
+
if marker.exists():
|
|
149
|
+
cmd.append("--break-system-packages")
|
|
150
|
+
except Exception:
|
|
151
|
+
pass
|
|
152
|
+
return cmd + list(packages)
|
|
153
|
+
|
|
154
|
+
|
|
134
155
|
def stream_thinking(chunk: str, verbose: bool):
|
|
135
156
|
if verbose:
|
|
136
157
|
clean_chunk = chunk.replace("\n", " ")
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
## 🔥🔥🔥 News (Pacific Time)
|
|
4
4
|
|
|
5
|
+
- 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."* 🦅🇩🇴
|
|
6
|
+
|
|
7
|
+
- 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. 🦅🇩🇴
|
|
8
|
+
|
|
5
9
|
- 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)
|
|
6
10
|
|
|
7
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 3.
|
|
3
|
+
Version: 3.9.0
|
|
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,8 @@ 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
|
+
- **🔌 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." 🦅
|
|
138
|
+
|
|
137
139
|
- **🌍 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
140
|
|
|
139
141
|
- **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 +247,17 @@ Answering `a` at any prompt upgrades the session to accept-all.
|
|
|
245
247
|
|
|
246
248
|
Drop a `.mcp.json` in your project — every server registers instantly as `mcp__server__tool`. stdio, SSE, and HTTP transports.
|
|
247
249
|
|
|
250
|
+
**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.
|
|
251
|
+
|
|
248
252
|
```
|
|
249
|
-
/mcp
|
|
250
|
-
/mcp
|
|
253
|
+
/mcp list [query] # browse the 2000+ server catalog
|
|
254
|
+
/mcp search <query> # search every source at once
|
|
255
|
+
/mcp install <name> # install by name — auto-connects, tools go live
|
|
256
|
+
/mcp installed # what's installed + live status
|
|
257
|
+
/mcp runtimes # which runtimes you have (node/python/docker)
|
|
258
|
+
|
|
259
|
+
/mcp # list configured servers and their tools
|
|
260
|
+
/mcp add <name> <cmd> # add a stdio server manually
|
|
251
261
|
/mcp reload # reconnect all
|
|
252
262
|
```
|
|
253
263
|
|
|
@@ -305,7 +315,8 @@ Dulus is proudly supported by these startup programs — powering our infrastruc
|
|
|
305
315
|
<a href="https://amplitude.com/startups"><img src="https://www.google.com/s2/favicons?domain=amplitude.com&sz=128" height="38" alt="Amplitude" title="Amplitude Startup Scholarship"></a>
|
|
306
316
|
<a href="https://www.digitalocean.com/hatch"><img src="https://cdn.simpleicons.org/digitalocean" height="38" alt="DigitalOcean" title="DigitalOcean Hatch"></a>
|
|
307
317
|
<a href="https://www.notion.com/startups"><img src="https://cdn.simpleicons.org/notion" height="38" alt="Notion" title="Notion for Startups"></a>
|
|
308
|
-
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a
|
|
318
|
+
<a href="https://www.zendesk.com/campaign/startups/"><img src="https://cdn.simpleicons.org/zendesk" height="38" alt="Zendesk" title="Zendesk for Startups"></a>
|
|
319
|
+
<a href="https://deepgram.com/startups"><img src="https://www.google.com/s2/favicons?domain=deepgram.com&sz=128" height="38" alt="Deepgram" title="Deepgram Startup Program — powering Dulus's voice"></a>
|
|
309
320
|
</p>
|
|
310
321
|
|
|
311
322
|
<p align="center"><i>Want to support Dulus? → <a href="https://github.com/sponsors/KevRojo">GitHub Sponsors</a> 🦅</i></p>
|
|
@@ -119,7 +119,9 @@ dulus.egg-info/requires.txt
|
|
|
119
119
|
dulus.egg-info/top_level.txt
|
|
120
120
|
dulus_mcp/__init__.py
|
|
121
121
|
dulus_mcp/client.py
|
|
122
|
+
dulus_mcp/commands.py
|
|
122
123
|
dulus_mcp/config.py
|
|
124
|
+
dulus_mcp/hub.py
|
|
123
125
|
dulus_mcp/tools.py
|
|
124
126
|
dulus_mcp/types.py
|
|
125
127
|
dulus_tools/__init__.py
|
|
@@ -2743,7 +2743,7 @@ def cmd_harvest(_args: str, _state, config) -> bool:
|
|
|
2743
2743
|
except ImportError:
|
|
2744
2744
|
import os
|
|
2745
2745
|
info("Installing playwright...")
|
|
2746
|
-
|
|
2746
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
2747
2747
|
os.system("playwright install chromium")
|
|
2748
2748
|
from playwright.sync_api import sync_playwright
|
|
2749
2749
|
|
|
@@ -2888,7 +2888,7 @@ def cmd_harvest_kimi(_args: str, _state, config) -> bool:
|
|
|
2888
2888
|
from playwright.sync_api import sync_playwright
|
|
2889
2889
|
except ImportError:
|
|
2890
2890
|
info("Installing playwright...")
|
|
2891
|
-
|
|
2891
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
2892
2892
|
os.system("playwright install chromium")
|
|
2893
2893
|
from playwright.sync_api import sync_playwright
|
|
2894
2894
|
|
|
@@ -2999,7 +2999,7 @@ def cmd_harvest_gemini(_args: str, _state, config) -> bool:
|
|
|
2999
2999
|
from playwright.sync_api import sync_playwright
|
|
3000
3000
|
except ImportError:
|
|
3001
3001
|
info("Installing playwright...")
|
|
3002
|
-
|
|
3002
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
3003
3003
|
os.system("playwright install chromium")
|
|
3004
3004
|
from playwright.sync_api import sync_playwright
|
|
3005
3005
|
|
|
@@ -3169,7 +3169,7 @@ def cmd_harvest_deepseek(_args: str, _state, config) -> bool:
|
|
|
3169
3169
|
from playwright.sync_api import sync_playwright
|
|
3170
3170
|
except ImportError:
|
|
3171
3171
|
info("Installing playwright...")
|
|
3172
|
-
|
|
3172
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
3173
3173
|
os.system("playwright install chromium")
|
|
3174
3174
|
from playwright.sync_api import sync_playwright
|
|
3175
3175
|
|
|
@@ -3317,7 +3317,7 @@ def cmd_harvest_qwen(_args: str, _state, config) -> bool:
|
|
|
3317
3317
|
from playwright.sync_api import sync_playwright
|
|
3318
3318
|
except ImportError:
|
|
3319
3319
|
info("Installing playwright...")
|
|
3320
|
-
|
|
3320
|
+
__import__("subprocess").run(__import__("common").pip_install_cmd("playwright"))
|
|
3321
3321
|
os.system("playwright install chromium")
|
|
3322
3322
|
from playwright.sync_api import sync_playwright
|
|
3323
3323
|
|
|
@@ -5213,6 +5213,14 @@ def cmd_skill(args: str, state, config) -> bool:
|
|
|
5213
5213
|
def cmd_mcp(args: str, _state, config) -> bool:
|
|
5214
5214
|
"""Show MCP server status, or manage servers.
|
|
5215
5215
|
|
|
5216
|
+
Marketplace (0-friction discovery of 2000+ servers):
|
|
5217
|
+
/mcp list [query] — browse the catalog (official registry + awesome list)
|
|
5218
|
+
/mcp search <q> — search every source for matching servers
|
|
5219
|
+
/mcp install <name>— install by name (auto-connects, no manual config)
|
|
5220
|
+
/mcp installed — show installed servers and their live status
|
|
5221
|
+
/mcp runtimes — show which runtimes (node/python/docker) are available
|
|
5222
|
+
|
|
5223
|
+
Manual management:
|
|
5216
5224
|
/mcp — list all configured servers and their tools
|
|
5217
5225
|
/mcp reload — reconnect all servers and refresh tools
|
|
5218
5226
|
/mcp reload <name> — reconnect a single server
|
|
@@ -5227,6 +5235,33 @@ def cmd_mcp(args: str, _state, config) -> bool:
|
|
|
5227
5235
|
parts = args.split() if args.strip() else []
|
|
5228
5236
|
subcmd = parts[0].lower() if parts else ""
|
|
5229
5237
|
|
|
5238
|
+
# ── Marketplace subcommands (0-friction discovery + install) ──────────────
|
|
5239
|
+
# Wired to dulus_mcp.commands.handle_mcp_command so users can browse a
|
|
5240
|
+
# catalog of 2000+ servers and install by name without knowing the raw
|
|
5241
|
+
# command. Falls through to the classic manager for reload/add/remove.
|
|
5242
|
+
_MARKETPLACE_SUBCMDS = {"list", "ls", "install", "search", "find", "s",
|
|
5243
|
+
"installed", "runtimes", "browse", "catalog"}
|
|
5244
|
+
if subcmd in _MARKETPLACE_SUBCMDS:
|
|
5245
|
+
try:
|
|
5246
|
+
from dulus_mcp.commands import handle_mcp_command
|
|
5247
|
+
_mkt_args = list(parts)
|
|
5248
|
+
if subcmd in ("browse", "catalog"):
|
|
5249
|
+
_mkt_args[0] = "list"
|
|
5250
|
+
_out = handle_mcp_command(_mkt_args)
|
|
5251
|
+
print(_out)
|
|
5252
|
+
# After a successful install, hot-reload so the new MCP tools go
|
|
5253
|
+
# live in this same session (no restart needed).
|
|
5254
|
+
if subcmd == "install" and _out.strip().startswith("\u2705"):
|
|
5255
|
+
try:
|
|
5256
|
+
reload_mcp()
|
|
5257
|
+
ok("MCP tools reloaded — new server is live.")
|
|
5258
|
+
except Exception:
|
|
5259
|
+
pass
|
|
5260
|
+
return True
|
|
5261
|
+
except Exception as _mkt_e:
|
|
5262
|
+
err(f"MCP marketplace error: {_mkt_e}")
|
|
5263
|
+
return True
|
|
5264
|
+
|
|
5230
5265
|
if subcmd == "reload":
|
|
5231
5266
|
target = parts[1] if len(parts) > 1 else ""
|
|
5232
5267
|
if target:
|
|
@@ -6991,7 +7026,7 @@ def cmd_tts(args: str, state, config) -> bool:
|
|
|
6991
7026
|
|
|
6992
7027
|
if parts and parts[0].lower() == "provider":
|
|
6993
7028
|
name = parts[1].strip().lower() if len(parts) > 1 else ""
|
|
6994
|
-
valid = ("auto", "azure", "riva", "openai", "gtts", "pyttsx3", "edge", "elevenlabs")
|
|
7029
|
+
valid = ("auto", "azure", "riva", "openai", "gtts", "pyttsx3", "edge", "elevenlabs", "deepgram")
|
|
6995
7030
|
if not name:
|
|
6996
7031
|
current = config.get("tts_provider", "auto")
|
|
6997
7032
|
info(f"TTS provider: {current}")
|
|
@@ -36,13 +36,26 @@ class StdioTransport:
|
|
|
36
36
|
|
|
37
37
|
def start(self) -> None:
|
|
38
38
|
env = {**os.environ, **(self._config.env or {})}
|
|
39
|
-
|
|
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)
|