dulus 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. agent.py +363 -0
  2. backend/__init__.py +63 -0
  3. backend/compressor.py +261 -0
  4. backend/context.py +329 -0
  5. backend/githook.py +166 -0
  6. backend/marketplace.py +141 -0
  7. backend/mempalace_bridge.py +182 -0
  8. backend/personas.py +297 -0
  9. backend/plugins.py +222 -0
  10. backend/server.py +411 -0
  11. backend/tasks.py +213 -0
  12. batch_api.py +307 -0
  13. checkpoint/__init__.py +27 -0
  14. checkpoint/hooks.py +90 -0
  15. checkpoint/store.py +314 -0
  16. checkpoint/types.py +80 -0
  17. claude_code_watcher.py +214 -0
  18. clipboard_utils.py +246 -0
  19. cloudsave.py +159 -0
  20. common.py +177 -0
  21. compaction.py +378 -0
  22. config.py +180 -0
  23. context.py +241 -0
  24. dulus-0.2.0.dist-info/METADATA +600 -0
  25. dulus-0.2.0.dist-info/RECORD +101 -0
  26. dulus-0.2.0.dist-info/WHEEL +5 -0
  27. dulus-0.2.0.dist-info/entry_points.txt +2 -0
  28. dulus-0.2.0.dist-info/licenses/LICENSE +674 -0
  29. dulus-0.2.0.dist-info/licenses/license_manager.py +187 -0
  30. dulus-0.2.0.dist-info/top_level.txt +36 -0
  31. dulus.py +8455 -0
  32. dulus_gui.py +331 -0
  33. dulus_mcp/__init__.py +43 -0
  34. dulus_mcp/client.py +546 -0
  35. dulus_mcp/config.py +133 -0
  36. dulus_mcp/tools.py +131 -0
  37. dulus_mcp/types.py +124 -0
  38. gui/__init__.py +18 -0
  39. gui/agent_bridge.py +283 -0
  40. gui/chat_widget.py +448 -0
  41. gui/main_window.py +485 -0
  42. gui/personas.py +230 -0
  43. gui/session_utils.py +189 -0
  44. gui/settings_dialog.py +146 -0
  45. gui/sidebar.py +515 -0
  46. gui/tasks_view.py +499 -0
  47. gui/themes.py +256 -0
  48. gui/tool_panel.py +94 -0
  49. input.py +1030 -0
  50. license_manager.py +187 -0
  51. memory/__init__.py +93 -0
  52. memory/audit.py +51 -0
  53. memory/consolidator.py +312 -0
  54. memory/context.py +270 -0
  55. memory/offload.py +148 -0
  56. memory/palace.py +127 -0
  57. memory/scan.py +146 -0
  58. memory/sessions.py +100 -0
  59. memory/store.py +395 -0
  60. memory/tools.py +408 -0
  61. memory/types.py +114 -0
  62. memory/vector_search.py +92 -0
  63. multi_agent/__init__.py +23 -0
  64. multi_agent/subagent.py +501 -0
  65. multi_agent/tools.py +393 -0
  66. offload_helper.py +183 -0
  67. plugin/__init__.py +22 -0
  68. plugin/autoadapter.py +1641 -0
  69. plugin/loader.py +156 -0
  70. plugin/recommend.py +211 -0
  71. plugin/store.py +387 -0
  72. plugin/types.py +147 -0
  73. providers.py +3750 -0
  74. skill/__init__.py +14 -0
  75. skill/builtin.py +100 -0
  76. skill/clawhub.py +270 -0
  77. skill/executor.py +66 -0
  78. skill/loader.py +199 -0
  79. skill/tools.py +110 -0
  80. skills.py +14 -0
  81. spinner.py +42 -0
  82. string_utils.py +42 -0
  83. subagent.py +11 -0
  84. task/__init__.py +12 -0
  85. task/store.py +199 -0
  86. task/tools.py +265 -0
  87. task/types.py +92 -0
  88. tmux_offloader.py +177 -0
  89. tmux_tools.py +410 -0
  90. tool_registry.py +214 -0
  91. tools.py +2694 -0
  92. ui/__init__.py +1 -0
  93. ui/input.py +464 -0
  94. ui/render.py +272 -0
  95. voice/__init__.py +56 -0
  96. voice/keyterms.py +179 -0
  97. voice/recorder.py +263 -0
  98. voice/stt.py +408 -0
  99. voice/tts.py +570 -0
  100. webchat.py +432 -0
  101. webchat_server.py +1761 -0
gui/themes.py ADDED
@@ -0,0 +1,256 @@
1
+ """Theme system for Dulus GUI.
2
+
3
+ Provides multiple color presets that can be switched at runtime.
4
+ """
5
+ from __future__ import annotations
6
+
7
+ # ── Theme presets ───────────────────────────────────────────────────────────
8
+
9
+ THEMES: dict[str, dict[str, str]] = {
10
+ "dulus": {
11
+ "bg": "#0a0a0a",
12
+ "card": "#0f0f12",
13
+ "code_bg": "#15151a",
14
+ "accent": "#ff6b1f",
15
+ "accent_hover": "#ffb347",
16
+ "text": "#f0e8df",
17
+ "dim": "#6a6470",
18
+ "border": "#3a3840",
19
+ "user_bubble": "#15151a",
20
+ "assistant_bubble": "#0f0f12",
21
+ "error": "#ff5555",
22
+ "success": "#50fa7b",
23
+ "warning": "#f1fa8c",
24
+ },
25
+ "midnight": {
26
+ "bg": "#1a1a2e",
27
+ "card": "#16213e",
28
+ "accent": "#00BCD4",
29
+ "accent_hover": "#00acc1",
30
+ "text": "#eaeaea",
31
+ "dim": "#a0a0a0",
32
+ "border": "#2a2a4a",
33
+ "user_bubble": "#0d3b4c",
34
+ "assistant_bubble": "#1e293b",
35
+ "code_bg": "#0d1117",
36
+ "error": "#ff6b6b",
37
+ "success": "#4caf50",
38
+ "warning": "#FFC107",
39
+ },
40
+ "ocean": {
41
+ "bg": "#0f172a",
42
+ "card": "#1e293b",
43
+ "accent": "#38bdf8",
44
+ "accent_hover": "#0ea5e9",
45
+ "text": "#f1f5f9",
46
+ "dim": "#94a3b8",
47
+ "border": "#334155",
48
+ "user_bubble": "#064e3b",
49
+ "assistant_bubble": "#1e293b",
50
+ "code_bg": "#020617",
51
+ "error": "#f87171",
52
+ "success": "#4ade80",
53
+ "warning": "#fbbf24",
54
+ },
55
+ "dracula": {
56
+ "bg": "#282a36",
57
+ "card": "#44475a",
58
+ "accent": "#bd93f9",
59
+ "accent_hover": "#d6acff",
60
+ "text": "#f8f8f2",
61
+ "dim": "#6272a4",
62
+ "border": "#6272a4",
63
+ "user_bubble": "#6272a4",
64
+ "assistant_bubble": "#44475a",
65
+ "code_bg": "#191a21",
66
+ "error": "#ff5555",
67
+ "success": "#50fa7b",
68
+ "warning": "#f1fa8c",
69
+ },
70
+ "monokai": {
71
+ "bg": "#272822",
72
+ "card": "#3e3d32",
73
+ "accent": "#a6e22e",
74
+ "accent_hover": "#c4f279",
75
+ "text": "#f8f8f2",
76
+ "dim": "#75715e",
77
+ "border": "#75715e",
78
+ "user_bubble": "#49483e",
79
+ "assistant_bubble": "#3e3d32",
80
+ "code_bg": "#1e1f1c",
81
+ "error": "#f92672",
82
+ "success": "#a6e22e",
83
+ "warning": "#e6db74",
84
+ },
85
+ "nord": {
86
+ "bg": "#2e3440",
87
+ "card": "#3b4252",
88
+ "accent": "#88c0d0",
89
+ "accent_hover": "#81a1c1",
90
+ "text": "#eceff4",
91
+ "dim": "#4c566a",
92
+ "border": "#4c566a",
93
+ "user_bubble": "#434c5e",
94
+ "assistant_bubble": "#3b4252",
95
+ "code_bg": "#242933",
96
+ "error": "#bf616a",
97
+ "success": "#a3be8c",
98
+ "warning": "#ebcb8b",
99
+ },
100
+ "solarized": {
101
+ "bg": "#002b36",
102
+ "card": "#073642",
103
+ "accent": "#2aa198",
104
+ "accent_hover": "#1bafa3",
105
+ "text": "#eee8d5",
106
+ "dim": "#586e75",
107
+ "border": "#586e75",
108
+ "user_bubble": "#586e75",
109
+ "assistant_bubble": "#073642",
110
+ "code_bg": "#001f27",
111
+ "error": "#dc322f",
112
+ "success": "#859900",
113
+ "warning": "#b58900",
114
+ },
115
+ "gruvbox": {
116
+ "bg": "#282828",
117
+ "card": "#3c3836",
118
+ "accent": "#fabd2f",
119
+ "accent_hover": "#d79921",
120
+ "text": "#ebdbb2",
121
+ "dim": "#928374",
122
+ "border": "#504945",
123
+ "user_bubble": "#504945",
124
+ "assistant_bubble": "#3c3836",
125
+ "code_bg": "#1d2021",
126
+ "error": "#fb4934",
127
+ "success": "#b8bb26",
128
+ "warning": "#fe8019",
129
+ },
130
+ "tokyo-night": {
131
+ "bg": "#1a1b26",
132
+ "card": "#24283b",
133
+ "accent": "#7aa2f7",
134
+ "accent_hover": "#89ddff",
135
+ "text": "#c0caf5",
136
+ "dim": "#565f89",
137
+ "border": "#414868",
138
+ "user_bubble": "#364a82",
139
+ "assistant_bubble": "#24283b",
140
+ "code_bg": "#16161e",
141
+ "error": "#f7768e",
142
+ "success": "#9ece6a",
143
+ "warning": "#e0af68",
144
+ },
145
+ "catppuccin": {
146
+ "bg": "#1e1e2e",
147
+ "card": "#181825",
148
+ "accent": "#f5c2e7",
149
+ "accent_hover": "#f2cdcd",
150
+ "text": "#cdd6f4",
151
+ "dim": "#6c7086",
152
+ "border": "#45475a",
153
+ "user_bubble": "#313244",
154
+ "assistant_bubble": "#181825",
155
+ "code_bg": "#11111b",
156
+ "error": "#f38ba8",
157
+ "success": "#a6e3a1",
158
+ "warning": "#fab387",
159
+ },
160
+ "matrix": {
161
+ "bg": "#000000",
162
+ "card": "#0a0a0a",
163
+ "accent": "#00ff41",
164
+ "accent_hover": "#33ff66",
165
+ "text": "#00ff41",
166
+ "dim": "#008f11",
167
+ "border": "#003b00",
168
+ "user_bubble": "#003b00",
169
+ "assistant_bubble": "#0a0a0a",
170
+ "code_bg": "#000000",
171
+ "error": "#ff0000",
172
+ "success": "#00ff41",
173
+ "warning": "#ccff00",
174
+ },
175
+ "synthwave": {
176
+ "bg": "#2b213a",
177
+ "card": "#241b2f",
178
+ "accent": "#ff00ff",
179
+ "accent_hover": "#ff71ce",
180
+ "text": "#ffffff",
181
+ "dim": "#b31183",
182
+ "border": "#b31183",
183
+ "user_bubble": "#720c4f",
184
+ "assistant_bubble": "#241b2f",
185
+ "code_bg": "#1a1221",
186
+ "error": "#ff0000",
187
+ "success": "#33ff00",
188
+ "warning": "#fffb00",
189
+ },
190
+ "mono": {
191
+ "bg": "#121212",
192
+ "card": "#1e1e1e",
193
+ "accent": "#e0e0e0",
194
+ "accent_hover": "#ffffff",
195
+ "text": "#e0e0e0",
196
+ "dim": "#808080",
197
+ "border": "#333333",
198
+ "user_bubble": "#333333",
199
+ "assistant_bubble": "#1e1e1e",
200
+ "code_bg": "#000000",
201
+ "error": "#ffffff",
202
+ "success": "#ffffff",
203
+ "warning": "#ffffff",
204
+ },
205
+ "none": {
206
+ "bg": "#ffffff",
207
+ "card": "#f0f0f0",
208
+ "accent": "#000000",
209
+ "accent_hover": "#333333",
210
+ "text": "#000000",
211
+ "dim": "#666666",
212
+ "border": "#cccccc",
213
+ "user_bubble": "#e0e0e0",
214
+ "assistant_bubble": "#f0f0f0",
215
+ "code_bg": "#ffffff",
216
+ "error": "#ff0000",
217
+ "success": "#00aa00",
218
+ "warning": "#aa5500",
219
+ },
220
+ }
221
+
222
+ # Curated model list (shared between topbar and sidebar)
223
+ CURATED_MODELS = [
224
+ "claude-opus-4-6",
225
+ "claude-sonnet-4-6",
226
+ "gpt-4o",
227
+ "gpt-4o-mini",
228
+ "gemini-2.5-pro",
229
+ "kimi-for-coding",
230
+ "kimi-k2.5",
231
+ "deepseek-chat",
232
+ "deepseek-r1",
233
+ "ollama/gemma4",
234
+ ]
235
+
236
+ _ACTIVE_THEME: dict[str, str] = THEMES["midnight"].copy()
237
+
238
+
239
+ def get_theme() -> dict[str, str]:
240
+ """Return the currently active theme colors."""
241
+ return _ACTIVE_THEME.copy()
242
+
243
+
244
+ def set_theme(name: str) -> dict[str, str] | None:
245
+ """Activate a theme by name. Returns the theme dict or None if unknown."""
246
+ global _ACTIVE_THEME
247
+ t = THEMES.get(name)
248
+ if t:
249
+ _ACTIVE_THEME = t.copy()
250
+ return _ACTIVE_THEME
251
+ return None
252
+
253
+
254
+ def list_themes() -> list[str]:
255
+ """Return available theme names."""
256
+ return list(THEMES.keys())
gui/tool_panel.py ADDED
@@ -0,0 +1,94 @@
1
+ """Side panel showing active tool executions."""
2
+ from __future__ import annotations
3
+
4
+ import customtkinter as ctk
5
+
6
+ THEME = {
7
+ "bg": "#1a1a2e",
8
+ "card": "#16213e",
9
+ "accent": "#00BCD4",
10
+ "text": "#eaeaea",
11
+ "dim": "#888888",
12
+ "tool": "#e94560",
13
+ "success": "#4CAF50",
14
+ }
15
+
16
+ FONT_FAMILY = "Segoe UI"
17
+
18
+
19
+ class ToolPanel(ctk.CTkFrame):
20
+ """Panel that displays running and completed tools."""
21
+
22
+ def __init__(self, master, **kwargs):
23
+ super().__init__(master, fg_color=THEME["card"], corner_radius=10, **kwargs)
24
+
25
+ self.header = ctk.CTkLabel(
26
+ self,
27
+ text="🔧 Tools",
28
+ font=(FONT_FAMILY, 14, "bold"),
29
+ text_color=THEME["accent"],
30
+ )
31
+ self.header.pack(anchor="w", padx=10, pady=(10, 5))
32
+
33
+ self.container = ctk.CTkScrollableFrame(self, fg_color="transparent", height=300)
34
+ self.container.pack(fill="both", expand=True, padx=5, pady=5)
35
+
36
+ self._tools: dict[str, ctk.CTkFrame] = {}
37
+
38
+ def add_tool(self, name: str, status: str = "running") -> None:
39
+ if name in self._tools:
40
+ return
41
+
42
+ frame = ctk.CTkFrame(self.container, fg_color=THEME["bg"], corner_radius=8)
43
+ frame.pack(fill="x", padx=5, pady=3)
44
+
45
+ color = THEME["tool"] if status == "running" else THEME["success"]
46
+ symbol = "⚙" if status == "running" else "✓"
47
+
48
+ lbl = ctk.CTkLabel(
49
+ frame,
50
+ text=f"{symbol} {name}",
51
+ font=(FONT_FAMILY, 11),
52
+ text_color=color,
53
+ anchor="w",
54
+ )
55
+ lbl.pack(fill="x", padx=8, pady=5)
56
+
57
+ self._tools[name] = frame
58
+
59
+ def update_tool(self, name: str, status: str = "done", result: str = "") -> None:
60
+ frame = self._tools.get(name)
61
+ if frame is None:
62
+ return
63
+
64
+ for child in frame.winfo_children():
65
+ child.destroy()
66
+
67
+ color = THEME["success"] if status == "done" else THEME["tool"]
68
+ symbol = "✓" if status == "done" else "⚙"
69
+
70
+ lbl = ctk.CTkLabel(
71
+ frame,
72
+ text=f"{symbol} {name}",
73
+ font=(FONT_FAMILY, 11),
74
+ text_color=color,
75
+ anchor="w",
76
+ )
77
+ lbl.pack(fill="x", padx=8, pady=(5, 2))
78
+
79
+ if result:
80
+ preview = result[:120] + "..." if len(result) > 120 else result
81
+ res_lbl = ctk.CTkLabel(
82
+ frame,
83
+ text=preview,
84
+ font=("Consolas", 10),
85
+ text_color=THEME["dim"],
86
+ anchor="w",
87
+ wraplength=180,
88
+ )
89
+ res_lbl.pack(fill="x", padx=8, pady=(0, 5))
90
+
91
+ def clear_tools(self) -> None:
92
+ for frame in self._tools.values():
93
+ frame.destroy()
94
+ self._tools.clear()