dulus 0.2.51__tar.gz → 0.2.53__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-0.2.51/dulus.egg-info → dulus-0.2.53}/PKG-INFO +3 -2
- {dulus-0.2.51 → dulus-0.2.53}/README.md +2 -1
- {dulus-0.2.51 → dulus-0.2.53}/data/context.json +18 -26
- {dulus-0.2.51 → dulus-0.2.53/dulus.egg-info}/PKG-INFO +3 -2
- {dulus-0.2.51 → dulus-0.2.53}/dulus.py +92 -26
- {dulus-0.2.51 → dulus-0.2.53}/gui/session_utils.py +2 -1
- {dulus-0.2.51 → dulus-0.2.53}/pyproject.toml +1 -1
- {dulus-0.2.51 → dulus-0.2.53}/skill/clawhub.py +89 -1
- {dulus-0.2.51 → dulus-0.2.53}/LICENSE +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/MANIFEST.in +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/agent.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/agents_bridge.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/compressor.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/context.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/githook.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/marketplace.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/mempalace_bridge.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/personas.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/plugins.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/server.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/backend/tasks.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/batch_api.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/checkpoint/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/checkpoint/hooks.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/checkpoint/store.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/checkpoint/types.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/claude_code_watcher.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/clipboard_utils.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/cloudsave.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/common.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/compaction.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/config.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/context.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/active_persona.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/marketplace.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/personas.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/composio/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/composio/composio_plugin/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/composio/composio_plugin/session_manager.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/composio/composio_plugin/tool_generator.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/composio/plugin.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/plugins/composio/plugin_tool.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/data/tasks.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/README.md +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/api.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/architecture.md +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/azure-speech-template.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/dashboard/index.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/divider.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/generate.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/hero.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/index.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/news.md +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/nvidia-models.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/particle-playground.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/personas/index.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/poetry-banner.png +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/preview.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-agents.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-brainstorm.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-bridges.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-features.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-freetier.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-memory.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-models.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-perms.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-plugins.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-quickstart.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/sec-ssj.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/spinners.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/split-pane.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/terminal-boot.svg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/docs/uploads/particle-playground.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus.egg-info/SOURCES.txt +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus.egg-info/dependency_links.txt +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus.egg-info/entry_points.txt +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus.egg-info/requires.txt +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus.egg-info/top_level.txt +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus_gui.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus_mcp/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus_mcp/client.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus_mcp/config.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus_mcp/tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/dulus_mcp/types.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/agent_bridge.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/chat_widget.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/main_window.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/personas.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/settings_dialog.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/sidebar.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/tasks_view.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/themes.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/gui/tool_panel.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/input.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/license_manager.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/audit.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/consolidator.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/context.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/offload.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/palace.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/scan.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/sessions.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/store.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/types.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/memory/vector_search.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/multi_agent/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/multi_agent/subagent.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/multi_agent/tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/offload_helper.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/plugin/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/plugin/autoadapter.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/plugin/loader.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/plugin/recommend.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/plugin/store.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/plugin/types.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/providers.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/README.md +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/components.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/assets/index-DE51D6wI.css +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/assets/index-DMCCNE9Y.js +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/index.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/wallpaper-default.jpg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/wallpapers/default.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/wallpapers/light.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/wallpapers/nature.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/dist/wallpapers/tech.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/eslint.config.js +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/index.html +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/info.md +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/package-lock.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/package.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/postcss.config.js +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/public/wallpaper-default.jpg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/public/wallpapers/default.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/public/wallpapers/light.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/public/wallpapers/nature.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/public/wallpapers/tech.jpeg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/App.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/AgentMonitor.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ApiTester.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/AppRouter.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ArchiveManager.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/AsciiArt.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Base64Tool.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Browser.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Calculator.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Calendar.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Chat.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Chess.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Clock.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/CodeEditor.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ColorPalette.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ColorPicker.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Contacts.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/DocumentViewer.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Drawing.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Email.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/FileManager.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/FlappyBird.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/FtpClient.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Game2048.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/GitClient.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ImageGallery.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ImageViewer.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/JsonFormatter.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/MarkdownPreview.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/MatrixRain.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/MediaConverter.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Memory.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/MemoryManager.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Minesweeper.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/MusicPlayer.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/NetworkTools.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Notes.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/PasswordManager.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/PhotoEditor.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Pong.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/RegexTester.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Reminders.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/RssReader.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/ScreenRecorder.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Settings.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/SkillsLauncher.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Snake.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Solitaire.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Spreadsheet.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Sudoku.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/SystemMonitor.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/TaskManager.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Terminal.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Tetris.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/TextEditor.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/TicTacToe.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Todo.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/VideoPlayer.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/VoiceRecorder.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Weather.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/Whiteboard.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/apps/registry.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/AppContainer.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/AppLauncher.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/BootSequence.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ContextMenu.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/Desktop.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/Dock.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/LoginScreen.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/NotImplemented.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/NotificationCenter.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/NotificationSystem.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/TopPanel.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/WindowFrame.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/WindowManager.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/accordion.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/alert-dialog.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/alert.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/aspect-ratio.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/avatar.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/badge.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/breadcrumb.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/button-group.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/button.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/calendar.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/card.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/carousel.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/chart.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/checkbox.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/collapsible.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/command.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/context-menu.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/dialog.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/drawer.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/dropdown-menu.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/empty.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/field.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/form.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/hover-card.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/input-group.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/input-otp.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/input.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/item.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/kbd.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/label.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/menubar.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/navigation-menu.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/pagination.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/popover.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/progress.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/radio-group.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/resizable.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/scroll-area.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/select.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/separator.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/sheet.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/sidebar.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/skeleton.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/slider.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/sonner.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/spinner.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/switch.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/table.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/tabs.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/textarea.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/toggle-group.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/toggle.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/components/ui/tooltip.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/index.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/use-mobile.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useAutoOpenChat.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusAgents.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusChat.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusEvents.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusHealth.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusMemory.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusSkills.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useDulusTasks.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useFileSystem.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useOSStore.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useSkillBridge.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useSystemBattery.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useSystemNetwork.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/hooks/useSystemVolume.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/index.css +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/lib/dulus-api.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/lib/utils.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/main.tsx +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/types/index.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/src/utils/assets.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/tailwind.config.js +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/tsconfig.app.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/tsconfig.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/tsconfig.node.json +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/sandbox/vite.config.ts +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/setup.cfg +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/skill/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/skill/builtin.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/skill/executor.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/skill/loader.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/skill/tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/skills.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/spinner.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/string_utils.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/subagent.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/task/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/task/store.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/task/tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/task/types.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_afk_yolo.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_approval_runtime.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_background_task_tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_background_tasks.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_checkpoint.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_clipboard_utils.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_compaction.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_diff_view.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_diff_visualization.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_display_blocks.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_export_import.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_hook_engine.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_injection_fix.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_license.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_mcp.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_memory.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_notification_manager.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_plugin.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_session_fork.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_shell_mode.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_skills.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_steer_input.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_subagent.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_task.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_telegram_buffer.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_think_tool.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_todo_tool.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_todo_visualization.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_tool_registry.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_voice.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tests/test_wire_events.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tmux_offloader.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tmux_tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tool_registry.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/tools.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/ui/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/ui/input.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/ui/render.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/voice/__init__.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/voice/keyterms.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/voice/recorder.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/voice/stt.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/voice/tts.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/webchat.py +0 -0
- {dulus-0.2.51 → dulus-0.2.53}/webchat_server.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.53
|
|
4
4
|
Summary: Spanish-first multi-provider AI CLI — 14 NVIDIA models free, Mesa Redonda, voice, TTS, RTK token reducer, MemPalace
|
|
5
5
|
Author: KevRojo
|
|
6
6
|
License: GPL-3.0
|
|
@@ -69,7 +69,7 @@ SET /sticky_input ON since the first run for the best experience!
|
|
|
69
69
|
<a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
|
|
70
70
|
<img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
|
|
71
71
|
<img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
|
|
72
|
-
<img src="https://img.shields.io/badge/version-v0.2.
|
|
72
|
+
<img src="https://img.shields.io/badge/version-v0.2.52-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
|
|
73
73
|
<img src="https://img.shields.io/badge/providers-11-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
|
|
74
74
|
<img src="https://img.shields.io/badge/tools-27-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
|
|
75
75
|
<img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
|
|
@@ -125,6 +125,7 @@ Dulus adapt any python repository <3
|
|
|
125
125
|
|
|
126
126
|
Dulus is a **lightweight Python reimplementation of Claude Code** that isn't locked to Claude. It ships the whole loop — REPL, tool dispatch, streaming, context compaction, checkpoints, sub-agents, voice, Telegram bridge, MCP, plugins — in roughly **12K lines you can actually read**. Fork it. Bend it. Run it offline against Qwen on your M2.
|
|
127
127
|
|
|
128
|
+
> **v0.2.52 — May 12, 2026** — Session system overhaul: removed 50-session scan limit, metadata now includes message counts, eliminated daily prune from save path. **Recommendation:** wipe old sessions and start clean — only sessions, your memories stay intact.
|
|
128
129
|
> **v0.2.22 — May 9, 2026** — `/bg start` spawns one detached Dulus daemon that serves CLI (IPC), Web (browser at `127.0.0.1:5000`), and Telegram simultaneously — all sharing the same session. WebChat now defaults to **loopback-only** (opt-in to LAN exposure with `/webchat lan on`).
|
|
129
130
|
> **v0.2.21 — May 9, 2026** — Console-freeze fix: system prompt now tells the model SleepTimer is for user reminders only — pauses inside command pipelines must use `sleep N` inside the Bash command itself.
|
|
130
131
|
> **v0.2.20 — May 9, 2026** — Windows IPC port-collision fix: switched to `SO_EXCLUSIVEADDRUSE` so a second Dulus instance correctly cedes the port and acts as client. End-to-end tested.
|
|
@@ -22,7 +22,7 @@ SET /sticky_input ON since the first run for the best experience!
|
|
|
22
22
|
<a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
|
|
23
23
|
<img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
|
|
24
24
|
<img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
|
|
25
|
-
<img src="https://img.shields.io/badge/version-v0.2.
|
|
25
|
+
<img src="https://img.shields.io/badge/version-v0.2.52-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
|
|
26
26
|
<img src="https://img.shields.io/badge/providers-11-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
|
|
27
27
|
<img src="https://img.shields.io/badge/tools-27-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
|
|
28
28
|
<img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
|
|
@@ -78,6 +78,7 @@ Dulus adapt any python repository <3
|
|
|
78
78
|
|
|
79
79
|
Dulus is a **lightweight Python reimplementation of Claude Code** that isn't locked to Claude. It ships the whole loop — REPL, tool dispatch, streaming, context compaction, checkpoints, sub-agents, voice, Telegram bridge, MCP, plugins — in roughly **12K lines you can actually read**. Fork it. Bend it. Run it offline against Qwen on your M2.
|
|
80
80
|
|
|
81
|
+
> **v0.2.52 — May 12, 2026** — Session system overhaul: removed 50-session scan limit, metadata now includes message counts, eliminated daily prune from save path. **Recommendation:** wipe old sessions and start clean — only sessions, your memories stay intact.
|
|
81
82
|
> **v0.2.22 — May 9, 2026** — `/bg start` spawns one detached Dulus daemon that serves CLI (IPC), Web (browser at `127.0.0.1:5000`), and Telegram simultaneously — all sharing the same session. WebChat now defaults to **loopback-only** (opt-in to LAN exposure with `/webchat lan on`).
|
|
82
83
|
> **v0.2.21 — May 9, 2026** — Console-freeze fix: system prompt now tells the model SleepTimer is for user reminders only — pauses inside command pipelines must use `sleep N` inside the Bash command itself.
|
|
83
84
|
> **v0.2.20 — May 9, 2026** — Windows IPC port-collision fix: switched to `SO_EXCLUSIVEADDRUSE` so a second Dulus instance correctly cedes the port and acts as client. End-to-end tested.
|
|
@@ -10,20 +10,20 @@
|
|
|
10
10
|
"name": "Dulus Command Center",
|
|
11
11
|
"repo_stats": {
|
|
12
12
|
"files": 432,
|
|
13
|
-
"lines":
|
|
13
|
+
"lines": 205188,
|
|
14
14
|
"languages": {
|
|
15
15
|
".example": 5,
|
|
16
16
|
"no_ext": 1424,
|
|
17
|
-
".py":
|
|
17
|
+
".py": 60473,
|
|
18
18
|
".html": 14944,
|
|
19
19
|
".in": 3,
|
|
20
20
|
".toml": 129,
|
|
21
21
|
".md": 2161,
|
|
22
22
|
".txt": 463,
|
|
23
23
|
".lock": 1,
|
|
24
|
-
".json":
|
|
25
|
-
".whl":
|
|
26
|
-
".gz":
|
|
24
|
+
".json": 9513,
|
|
25
|
+
".whl": 21729,
|
|
26
|
+
".gz": 20908,
|
|
27
27
|
".svg": 1123,
|
|
28
28
|
".png": 31639,
|
|
29
29
|
".sh": 144,
|
|
@@ -38,49 +38,41 @@
|
|
|
38
38
|
},
|
|
39
39
|
"recent_commits": [
|
|
40
40
|
{
|
|
41
|
-
"hash": "
|
|
42
|
-
"subject": "
|
|
41
|
+
"hash": "7fc805e",
|
|
42
|
+
"subject": "chore: bump version 0.2.50 -> 0.2.51",
|
|
43
43
|
"author": "KevRojo",
|
|
44
44
|
"date": "2026-05-12"
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
|
-
"hash": "
|
|
48
|
-
"subject": "
|
|
47
|
+
"hash": "360badc",
|
|
48
|
+
"subject": "fix: GUI session responsiveness, sidebar incremental diff, async scan, default session IDs",
|
|
49
49
|
"author": "KevRojo",
|
|
50
50
|
"date": "2026-05-12"
|
|
51
51
|
},
|
|
52
52
|
{
|
|
53
|
-
"hash": "
|
|
54
|
-
"subject": "
|
|
53
|
+
"hash": "fae4455",
|
|
54
|
+
"subject": "feat: MemoryManager overhaul, skill bridge polish, server sync",
|
|
55
55
|
"author": "KevRojo",
|
|
56
56
|
"date": "2026-05-12"
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
|
-
"hash": "
|
|
60
|
-
"subject": "
|
|
59
|
+
"hash": "bf6455f",
|
|
60
|
+
"subject": "feat: skill-to-chat bridge, MemPalace search API, sandbox UX polish",
|
|
61
61
|
"author": "KevRojo",
|
|
62
62
|
"date": "2026-05-12"
|
|
63
63
|
},
|
|
64
64
|
{
|
|
65
|
-
"hash": "
|
|
66
|
-
"subject": "
|
|
65
|
+
"hash": "9803f97",
|
|
66
|
+
"subject": "fix: include sandbox/src and sandbox/public in wheel",
|
|
67
67
|
"author": "KevRojo",
|
|
68
68
|
"date": "2026-05-12"
|
|
69
69
|
}
|
|
70
70
|
],
|
|
71
71
|
"recent_changes": [
|
|
72
|
-
"backend/server.py",
|
|
73
72
|
"data/context.json",
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"sandbox/dist/assets/index-DMCCNE9Y.js",
|
|
78
|
-
"sandbox/dist/index.html",
|
|
79
|
-
"sandbox/src/apps/Chat.tsx",
|
|
80
|
-
"sandbox/src/apps/MemoryManager.tsx",
|
|
81
|
-
"sandbox/src/hooks/useSkillBridge.ts",
|
|
82
|
-
"sandbox/src/lib/dulus-api.ts",
|
|
83
|
-
"webchat_server.py"
|
|
73
|
+
"dulus.py",
|
|
74
|
+
"gui/session_utils.py",
|
|
75
|
+
"pyproject.toml"
|
|
84
76
|
]
|
|
85
77
|
},
|
|
86
78
|
"tasks": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dulus
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.53
|
|
4
4
|
Summary: Spanish-first multi-provider AI CLI — 14 NVIDIA models free, Mesa Redonda, voice, TTS, RTK token reducer, MemPalace
|
|
5
5
|
Author: KevRojo
|
|
6
6
|
License: GPL-3.0
|
|
@@ -69,7 +69,7 @@ SET /sticky_input ON since the first run for the best experience!
|
|
|
69
69
|
<a href="https://pypi.org/project/dulus/"><img src="https://static.pepy.tech/badge/dulus?style=flat-square" alt="downloads"/></a>
|
|
70
70
|
<img src="https://img.shields.io/badge/python-3.11+-ff6b1f?style=flat-square&labelColor=07070a" alt="python"/>
|
|
71
71
|
<img src="https://img.shields.io/badge/license-GPLv3-ff6b1f?style=flat-square&labelColor=07070a" alt="license"/>
|
|
72
|
-
<img src="https://img.shields.io/badge/version-v0.2.
|
|
72
|
+
<img src="https://img.shields.io/badge/version-v0.2.52-ff6b1f?style=flat-square&labelColor=07070a" alt="version"/>
|
|
73
73
|
<img src="https://img.shields.io/badge/providers-11-ff6b1f?style=flat-square&labelColor=07070a" alt="providers"/>
|
|
74
74
|
<img src="https://img.shields.io/badge/tools-27-ff6b1f?style=flat-square&labelColor=07070a" alt="tools"/>
|
|
75
75
|
<img src="https://img.shields.io/badge/tests-263+-ff6b1f?style=flat-square&labelColor=07070a" alt="tests"/>
|
|
@@ -125,6 +125,7 @@ Dulus adapt any python repository <3
|
|
|
125
125
|
|
|
126
126
|
Dulus is a **lightweight Python reimplementation of Claude Code** that isn't locked to Claude. It ships the whole loop — REPL, tool dispatch, streaming, context compaction, checkpoints, sub-agents, voice, Telegram bridge, MCP, plugins — in roughly **12K lines you can actually read**. Fork it. Bend it. Run it offline against Qwen on your M2.
|
|
127
127
|
|
|
128
|
+
> **v0.2.52 — May 12, 2026** — Session system overhaul: removed 50-session scan limit, metadata now includes message counts, eliminated daily prune from save path. **Recommendation:** wipe old sessions and start clean — only sessions, your memories stay intact.
|
|
128
129
|
> **v0.2.22 — May 9, 2026** — `/bg start` spawns one detached Dulus daemon that serves CLI (IPC), Web (browser at `127.0.0.1:5000`), and Telegram simultaneously — all sharing the same session. WebChat now defaults to **loopback-only** (opt-in to LAN exposure with `/webchat lan on`).
|
|
129
130
|
> **v0.2.21 — May 9, 2026** — Console-freeze fix: system prompt now tells the model SleepTimer is for user reminders only — pauses inside command pipelines must use `sleep N` inside the Bash command itself.
|
|
130
131
|
> **v0.2.20 — May 9, 2026** — Windows IPC port-collision fix: switched to `SO_EXCLUSIVEADDRUSE` so a second Dulus instance correctly cedes the port and acts as client. End-to-end tested.
|
|
@@ -1239,11 +1239,6 @@ def save_latest(args: str, state, config=None, mode: str = "full") -> bool:
|
|
|
1239
1239
|
daily_path = day_dir / f"session_{ts}_{sid}.json"
|
|
1240
1240
|
daily_path.write_text(payload)
|
|
1241
1241
|
|
|
1242
|
-
# Prune daily folder: keep only the latest `daily_limit` files
|
|
1243
|
-
daily_files = sorted(day_dir.glob("session_*.json"))
|
|
1244
|
-
for old in daily_files[:-daily_limit]:
|
|
1245
|
-
old.unlink(missing_ok=True)
|
|
1246
|
-
|
|
1247
1242
|
# 3. Append to history.json (master file)
|
|
1248
1243
|
if SESSION_HIST_FILE.exists():
|
|
1249
1244
|
try:
|
|
@@ -4331,6 +4326,7 @@ def cmd_skill(args: str, state, config) -> bool:
|
|
|
4331
4326
|
list_local, list_installed, install_local, install_clawhub,
|
|
4332
4327
|
search_clawhub, read_skill,
|
|
4333
4328
|
list_awesome_remote, list_composio_toolkits,
|
|
4329
|
+
install_awesome_remote,
|
|
4334
4330
|
)
|
|
4335
4331
|
from pathlib import Path as _Path
|
|
4336
4332
|
|
|
@@ -4376,17 +4372,16 @@ def cmd_skill(args: str, state, config) -> bool:
|
|
|
4376
4372
|
|
|
4377
4373
|
if rest.startswith("awesome"):
|
|
4378
4374
|
query = rest[7:].strip()
|
|
4379
|
-
# `--
|
|
4380
|
-
|
|
4381
|
-
|
|
4382
|
-
|
|
4383
|
-
|
|
4384
|
-
|
|
4385
|
-
|
|
4386
|
-
info("Fetching awesome skills + descriptions from GitHub (parallel, ~5s)...")
|
|
4375
|
+
# `--fast` skips descriptions for an instant list; default pulls them in parallel.
|
|
4376
|
+
fast = False
|
|
4377
|
+
if "--fast" in query.split():
|
|
4378
|
+
fast = True
|
|
4379
|
+
query = " ".join(t for t in query.split() if t != "--fast").strip()
|
|
4380
|
+
if fast:
|
|
4381
|
+
info("Fetching awesome skill list from GitHub (instant, no descriptions)...")
|
|
4387
4382
|
else:
|
|
4388
|
-
info("Fetching awesome
|
|
4389
|
-
skills = list_awesome_remote(query, with_descriptions=
|
|
4383
|
+
info("Fetching awesome skills + descriptions from GitHub (parallel, ~5s)...")
|
|
4384
|
+
skills = list_awesome_remote(query, with_descriptions=not fast)
|
|
4390
4385
|
if not skills:
|
|
4391
4386
|
err("Could not fetch awesome skills (network or rate-limit).")
|
|
4392
4387
|
return True
|
|
@@ -4395,7 +4390,7 @@ def cmd_skill(args: str, state, config) -> bool:
|
|
|
4395
4390
|
for s in skills
|
|
4396
4391
|
]
|
|
4397
4392
|
header = f"Awesome skills ({len(skills)})" + (f" matching '{query}'" if query else "")
|
|
4398
|
-
hint = "" if
|
|
4393
|
+
hint = "" if not fast else " — remove `--fast` for descriptions"
|
|
4399
4394
|
_pager(f"{header}{hint} — n=next q=quit", lines)
|
|
4400
4395
|
return True
|
|
4401
4396
|
|
|
@@ -4460,6 +4455,21 @@ def cmd_skill(args: str, state, config) -> bool:
|
|
|
4460
4455
|
print(f" {clr(r['slug'], 'cyan'):30s} {r.get('description','')[:60]}")
|
|
4461
4456
|
return True
|
|
4462
4457
|
|
|
4458
|
+
if rest.startswith("installed"):
|
|
4459
|
+
query = rest[9:].strip()
|
|
4460
|
+
skills = list_installed(query)
|
|
4461
|
+
if not skills:
|
|
4462
|
+
if query:
|
|
4463
|
+
info(f"No installed skills matching '{query}'.")
|
|
4464
|
+
else:
|
|
4465
|
+
info("No skills installed yet.")
|
|
4466
|
+
return True
|
|
4467
|
+
header = f"Installed skills ({len(skills)})" + (f" matching '{query}'" if query else "")
|
|
4468
|
+
info(header + ":")
|
|
4469
|
+
for s in skills:
|
|
4470
|
+
print(f" • {clr(s['name'], 'cyan'):22s} [{s['source']}] {s['description']}")
|
|
4471
|
+
return True
|
|
4472
|
+
|
|
4463
4473
|
# /skill info <name>
|
|
4464
4474
|
if subcmd == "info":
|
|
4465
4475
|
if not rest:
|
|
@@ -4499,27 +4509,83 @@ def cmd_skill(args: str, state, config) -> bool:
|
|
|
4499
4509
|
if rest.startswith("clawhub:"):
|
|
4500
4510
|
slug = rest[8:]
|
|
4501
4511
|
success, msg = install_clawhub(slug)
|
|
4512
|
+
elif rest.startswith("awesome/"):
|
|
4513
|
+
success, msg = install_awesome_remote(rest)
|
|
4502
4514
|
else:
|
|
4503
4515
|
success, msg = install_local(rest)
|
|
4516
|
+
# Fallback to awesome-remote if not found locally
|
|
4517
|
+
if not success:
|
|
4518
|
+
success, msg = install_awesome_remote(rest)
|
|
4504
4519
|
(ok if success else err)(msg)
|
|
4505
4520
|
return True
|
|
4506
4521
|
|
|
4507
4522
|
# ── /skill use ─────────────────────────────────────────────────────────
|
|
4508
4523
|
if subcmd == "use":
|
|
4524
|
+
from skill.clawhub import DULUS_SKILLS_DIR
|
|
4525
|
+
installed = list_installed()
|
|
4526
|
+
if not installed:
|
|
4527
|
+
err("No skills installed yet. Run /skill list to browse and install skills.")
|
|
4528
|
+
return True
|
|
4529
|
+
|
|
4530
|
+
# Interactive picker when no target is given
|
|
4509
4531
|
if not rest:
|
|
4510
|
-
|
|
4532
|
+
print(clr(" Select skill(s) to inject (active for this turn):", "cyan", "bold"))
|
|
4533
|
+
menu_buf = clr(" Select skill(s) to inject:", "cyan", "bold")
|
|
4534
|
+
for i, s in enumerate(installed):
|
|
4535
|
+
line = f" {clr(f'[{i+1:2d}]', 'yellow')} {clr(s['name'], 'white', 'bold'):<24} [{clr(s['source'], 'dim')}] {s['description'][:55]}"
|
|
4536
|
+
print(line)
|
|
4537
|
+
menu_buf += "\n" + line
|
|
4538
|
+
print()
|
|
4539
|
+
ans = ask_input_interactive(
|
|
4540
|
+
clr(" Enter number(s) (e.g. 1 or 1,2,3), name, or Enter to cancel > ", "cyan"),
|
|
4541
|
+
config, menu_buf,
|
|
4542
|
+
).strip()
|
|
4543
|
+
if not ans:
|
|
4544
|
+
info(" Cancelled.")
|
|
4545
|
+
return True
|
|
4546
|
+
rest = ans
|
|
4547
|
+
|
|
4548
|
+
# Resolve rest → list of skill names
|
|
4549
|
+
selected_names: list[str] = []
|
|
4550
|
+
tokens = [t.strip() for t in rest.replace(",", " ").split() if t.strip()]
|
|
4551
|
+
for tok in tokens:
|
|
4552
|
+
if tok.isdigit():
|
|
4553
|
+
idx = int(tok) - 1
|
|
4554
|
+
if 0 <= idx < len(installed):
|
|
4555
|
+
selected_names.append(installed[idx]["name"])
|
|
4556
|
+
else:
|
|
4557
|
+
warn(f"Index {tok} out of range (1-{len(installed)}). Skipping.")
|
|
4558
|
+
else:
|
|
4559
|
+
match = next((s["name"] for s in installed if s["name"].lower() == tok.lower()), None)
|
|
4560
|
+
if match is None:
|
|
4561
|
+
warn(f"No skill named '{tok}'. Skipping.")
|
|
4562
|
+
else:
|
|
4563
|
+
selected_names.append(match)
|
|
4564
|
+
|
|
4565
|
+
if not selected_names:
|
|
4566
|
+
err("No valid skill selected.")
|
|
4511
4567
|
return True
|
|
4512
|
-
|
|
4513
|
-
|
|
4514
|
-
|
|
4515
|
-
|
|
4568
|
+
|
|
4569
|
+
# Inject selected skills into context
|
|
4570
|
+
blocks = []
|
|
4571
|
+
for name in selected_names:
|
|
4572
|
+
body = read_skill(name)
|
|
4573
|
+
if not body:
|
|
4574
|
+
warn(f"Skill '{name}' could not be read. Skipping.")
|
|
4575
|
+
continue
|
|
4576
|
+
skill_dir = DULUS_SKILLS_DIR / name
|
|
4577
|
+
path_hint = f"\n\n# NOTE: Skill '{name}' files are located at: {skill_dir}" if skill_dir.exists() else ""
|
|
4578
|
+
blocks.append(f"## Skill: {name}\n\n{body}{path_hint}")
|
|
4579
|
+
|
|
4580
|
+
if not blocks:
|
|
4581
|
+
err("No skills could be injected.")
|
|
4516
4582
|
return True
|
|
4517
|
-
|
|
4518
|
-
skill_dir = DULUS_SKILLS_DIR / rest
|
|
4519
|
-
path_hint = f"\n\n# NOTE: Skill '{rest}' files are located at: {skill_dir}" if skill_dir.exists() else ""
|
|
4583
|
+
|
|
4520
4584
|
existing = config.get("_skill_inject", "")
|
|
4521
|
-
|
|
4522
|
-
|
|
4585
|
+
new_inject = "\n\n---\n\n".join(blocks)
|
|
4586
|
+
config["_skill_inject"] = (existing + "\n\n" + new_inject).strip() if existing else new_inject
|
|
4587
|
+
names_txt = ", ".join(f"'{n}'" for n in selected_names)
|
|
4588
|
+
ok(f"Injected {len(blocks)} skill(s) — active for this turn: {names_txt}")
|
|
4523
4589
|
return True
|
|
4524
4590
|
|
|
4525
4591
|
# ── /skill remove ──────────────────────────────────────────────────────
|
|
@@ -51,6 +51,7 @@ def _read_session_meta(path: Path) -> dict | None:
|
|
|
51
51
|
"path": str(path),
|
|
52
52
|
"saved_at": saved_at,
|
|
53
53
|
"turn_count": data.get("turn_count", len(messages) // 2),
|
|
54
|
+
"messages": messages,
|
|
54
55
|
}
|
|
55
56
|
_scan_cache[key] = (mtime, result)
|
|
56
57
|
return result
|
|
@@ -87,7 +88,7 @@ def scan_sessions() -> list[dict]:
|
|
|
87
88
|
|
|
88
89
|
# Sort all found sessions by saved_at DESC
|
|
89
90
|
sessions.sort(key=lambda x: x.get("saved_at", ""), reverse=True)
|
|
90
|
-
return sessions
|
|
91
|
+
return sessions
|
|
91
92
|
|
|
92
93
|
def save_session(state, config: dict, session_id: str | None = None) -> str:
|
|
93
94
|
"""Save AgentState to disk in standard Dulus format. Returns the session_id."""
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dulus"
|
|
7
|
-
version = "0.2.
|
|
7
|
+
version = "0.2.53"
|
|
8
8
|
description = "Spanish-first multi-provider AI CLI — 14 NVIDIA models free, Mesa Redonda, voice, TTS, RTK token reducer, MemPalace"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -198,10 +198,14 @@ def _fetch_awesome_remote(with_descriptions: bool = False) -> list[dict]:
|
|
|
198
198
|
if not path.endswith("/SKILL.md"):
|
|
199
199
|
continue
|
|
200
200
|
parts = path.split("/")
|
|
201
|
-
|
|
201
|
+
# Skip actual hidden scaffolding (.git, .github, .gitignore) but keep .gemini/skills/
|
|
202
|
+
if any(p in (".git", ".github", ".gitignore", ".gitattributes") for p in parts):
|
|
202
203
|
continue
|
|
203
204
|
if _AWESOME_EXCLUDE_REMOTE.intersection(parts):
|
|
204
205
|
continue
|
|
206
|
+
# Skip non-skill scaffold folders that happen to contain SKILL.md
|
|
207
|
+
if any(p.upper() in ("README", "TEMPLATE") for p in parts):
|
|
208
|
+
continue
|
|
205
209
|
skill_paths.append(path)
|
|
206
210
|
|
|
207
211
|
# Build the skill list from paths alone — instant, no per-file fetch.
|
|
@@ -287,6 +291,90 @@ def list_awesome_remote(query: Optional[str] = None, force_refresh: bool = False
|
|
|
287
291
|
return skills
|
|
288
292
|
|
|
289
293
|
|
|
294
|
+
def get_awesome_remote(slug: str) -> Optional[dict]:
|
|
295
|
+
"""Find an awesome-remote skill by its id (awesome/<dir>/<skill>) or skill name."""
|
|
296
|
+
for s in list_awesome_remote():
|
|
297
|
+
if s.get("id") == slug or s.get("skill") == slug:
|
|
298
|
+
return s
|
|
299
|
+
return None
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def install_awesome_remote(slug: str) -> tuple[bool, str]:
|
|
303
|
+
"""Download a skill from the awesome GitHub repo and install into ~/.dulus/skills/."""
|
|
304
|
+
import shutil
|
|
305
|
+
import urllib.request
|
|
306
|
+
|
|
307
|
+
entry = get_awesome_remote(slug)
|
|
308
|
+
if entry:
|
|
309
|
+
rel_dir = entry.get("_remote_dir", "")
|
|
310
|
+
skill_name = entry["skill"]
|
|
311
|
+
else:
|
|
312
|
+
# Derive from slug: awesome/<dir>/<skill> or <dir>/<skill>
|
|
313
|
+
parts = slug.replace("awesome/", "").strip("/").split("/")
|
|
314
|
+
if len(parts) < 1:
|
|
315
|
+
return False, f"Invalid awesome slug: '{slug}'. Expected awesome/<category>/<skill> or <category>/<skill>."
|
|
316
|
+
skill_name = parts[-1]
|
|
317
|
+
rel_dir = "/".join(parts)
|
|
318
|
+
|
|
319
|
+
dest_dir = DULUS_SKILLS_DIR / skill_name
|
|
320
|
+
dest_dir.mkdir(parents=True, exist_ok=True)
|
|
321
|
+
|
|
322
|
+
# Try to discover sibling files via GitHub tree API for this directory.
|
|
323
|
+
tree_url = (
|
|
324
|
+
f"https://api.github.com/repos/{_AWESOME_REPO}/git/trees/"
|
|
325
|
+
f"{_AWESOME_BRANCH}?recursive=1"
|
|
326
|
+
)
|
|
327
|
+
sibling_paths: list[str] = []
|
|
328
|
+
try:
|
|
329
|
+
with urllib.request.urlopen(tree_url, timeout=15) as resp:
|
|
330
|
+
tree = json.loads(resp.read())
|
|
331
|
+
prefix = rel_dir.rstrip("/") + "/"
|
|
332
|
+
for item in tree.get("tree", []):
|
|
333
|
+
p = item.get("path", "")
|
|
334
|
+
if p.startswith(prefix) and item.get("type") == "blob":
|
|
335
|
+
sibling_paths.append(p)
|
|
336
|
+
except Exception:
|
|
337
|
+
sibling_paths = []
|
|
338
|
+
|
|
339
|
+
# If tree API failed or returned nothing, fall back to just SKILL.md
|
|
340
|
+
if not sibling_paths:
|
|
341
|
+
sibling_paths = [f"{rel_dir}/SKILL.md"]
|
|
342
|
+
|
|
343
|
+
raw_base = f"https://raw.githubusercontent.com/{_AWESOME_REPO}/{_AWESOME_BRANCH}"
|
|
344
|
+
downloaded = []
|
|
345
|
+
for p in sibling_paths:
|
|
346
|
+
url = f"{raw_base}/{p}"
|
|
347
|
+
rel = p[len(prefix):] if p.startswith(prefix) else p.split("/")[-1]
|
|
348
|
+
dst = dest_dir / rel
|
|
349
|
+
dst.parent.mkdir(parents=True, exist_ok=True)
|
|
350
|
+
try:
|
|
351
|
+
with urllib.request.urlopen(url, timeout=15) as r:
|
|
352
|
+
data = r.read()
|
|
353
|
+
dst.write_bytes(data)
|
|
354
|
+
downloaded.append(rel)
|
|
355
|
+
except Exception:
|
|
356
|
+
pass # skip files we can't fetch
|
|
357
|
+
|
|
358
|
+
skill_md = dest_dir / "SKILL.md"
|
|
359
|
+
if not skill_md.exists():
|
|
360
|
+
return False, f"Could not download SKILL.md for '{skill_name}' from awesome repo."
|
|
361
|
+
|
|
362
|
+
# Rewrite SKILL.md frontmatter for Dulus
|
|
363
|
+
raw = skill_md.read_text(encoding="utf-8")
|
|
364
|
+
body = _strip_frontmatter(raw)
|
|
365
|
+
frontmatter = (
|
|
366
|
+
f"---\n"
|
|
367
|
+
f"name: {skill_name}\n"
|
|
368
|
+
f"description: {(entry or {}).get('description', '')}\n"
|
|
369
|
+
f"source: awesome-remote\n"
|
|
370
|
+
f"triggers: [/{skill_name}]\n"
|
|
371
|
+
f"---\n\n"
|
|
372
|
+
)
|
|
373
|
+
skill_md.write_text(frontmatter + body, encoding="utf-8")
|
|
374
|
+
|
|
375
|
+
return True, f"Installed '{skill_name}' → {dest_dir} ({len(downloaded)} files: {', '.join(downloaded[:5])}{'...' if len(downloaded)>5 else ''})"
|
|
376
|
+
|
|
377
|
+
|
|
290
378
|
# ── COMPOSIO (live API listing of toolkits) ───────────────────────────────
|
|
291
379
|
# The composio backend exposes a public toolkit list — we surface it as
|
|
292
380
|
# pseudo-skills so users can browse `gmail`, `slack`, etc. and create a
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|