devobin 1.0.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 (47) hide show
  1. devobin/__init__.py +4 -0
  2. devobin/app.py +42 -0
  3. devobin/cli/__init__.py +1 -0
  4. devobin/cli/chat.py +831 -0
  5. devobin/cli/commands.py +362 -0
  6. devobin/cli/slash_menu.py +74 -0
  7. devobin/cli/terminal.py +155 -0
  8. devobin/cli/ui.py +129 -0
  9. devobin/config/__init__.py +1 -0
  10. devobin/config/settings.py +124 -0
  11. devobin/core/__init__.py +1 -0
  12. devobin/core/analyzer.py +166 -0
  13. devobin/core/executor.py +244 -0
  14. devobin/core/memory.py +78 -0
  15. devobin/core/optimizer.py +106 -0
  16. devobin/core/prompt_builder.py +513 -0
  17. devobin/core/researcher.py +283 -0
  18. devobin/core/rtl.py +93 -0
  19. devobin/core/scanner.py +206 -0
  20. devobin/core/tools.py +283 -0
  21. devobin/core/validator.py +235 -0
  22. devobin/main.py +33 -0
  23. devobin/output/__init__.py +1 -0
  24. devobin/providers/__init__.py +85 -0
  25. devobin/providers/anthropic_provider.py +72 -0
  26. devobin/providers/google_provider.py +90 -0
  27. devobin/providers/ollama_provider.py +81 -0
  28. devobin/providers/openai_provider.py +102 -0
  29. devobin/storage/__init__.py +1 -0
  30. devobin/storage/database.py +207 -0
  31. devobin/ui/__init__.py +1 -0
  32. devobin/ui/ask_modal.py +96 -0
  33. devobin/ui/chat_screen.py +1029 -0
  34. devobin/ui/command_palette.py +131 -0
  35. devobin/ui/connect_modal.py +166 -0
  36. devobin/ui/export_modal.py +159 -0
  37. devobin/ui/history_modal.py +137 -0
  38. devobin/ui/memory_modal.py +88 -0
  39. devobin/ui/model_modal.py +143 -0
  40. devobin/ui/permission_modal.py +92 -0
  41. devobin/ui/project_modal.py +133 -0
  42. devobin/ui/settings_modal.py +89 -0
  43. devobin-1.0.0.dist-info/METADATA +364 -0
  44. devobin-1.0.0.dist-info/RECORD +47 -0
  45. devobin-1.0.0.dist-info/WHEEL +4 -0
  46. devobin-1.0.0.dist-info/entry_points.txt +2 -0
  47. devobin-1.0.0.dist-info/licenses/LICENSE +21 -0
devobin/core/memory.py ADDED
@@ -0,0 +1,78 @@
1
+ """Memory manager - manages long-term memory across sessions."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from devobin.storage.database import GlobalStorage, ProjectStorage
6
+
7
+
8
+ class MemoryManager:
9
+ """Manages both global and project-specific memory."""
10
+
11
+ def __init__(self, project_name: str | None = None) -> None:
12
+ self._global = GlobalStorage()
13
+ self._project = ProjectStorage(project_name) if project_name else None
14
+
15
+ # ── Global Memory ─────────────────────────────────────────────
16
+
17
+ def add_global_memory(self, content: str, category: str = "preference") -> dict:
18
+ return self._global.add_memory(content, category)
19
+
20
+ def get_global_memories(self, category: str | None = None) -> list[dict]:
21
+ return self._global.get_memories(category)
22
+
23
+ def search_global_memories(self, query: str) -> list[dict]:
24
+ return self._global.search_memories(query)
25
+
26
+ # ── Project Memory ────────────────────────────────────────────
27
+
28
+ def add_project_memory(self, content: str, category: str = "general") -> dict | None:
29
+ if self._project:
30
+ return self._project.add_memory(content, category)
31
+ return None
32
+
33
+ def get_project_memories(self, category: str | None = None) -> list[dict]:
34
+ if self._project:
35
+ return self._project.get_memories(category)
36
+ return []
37
+
38
+ def search_project_memories(self, query: str) -> list[dict]:
39
+ if self._project:
40
+ return self._project.search_memories(query)
41
+ return []
42
+
43
+ # ── Combined Context ──────────────────────────────────────────
44
+
45
+ def get_memory_context(self) -> str:
46
+ """Build a formatted string of all relevant memory for AI context."""
47
+ lines: list[str] = []
48
+
49
+ global_prefs = self.get_global_memories("preference")
50
+ if global_prefs:
51
+ lines.append("**User Preferences:**")
52
+ for m in global_prefs[-10:]: # Last 10 preferences
53
+ lines.append(f"- {m['content']}")
54
+
55
+ project_mem = self.get_project_memories()
56
+ if project_mem:
57
+ lines.append("\n**Project Context:**")
58
+ for m in project_mem[-10:]:
59
+ lines.append(f"- {m['content']}")
60
+
61
+ return "\n".join(lines)
62
+
63
+ # ── Convenience ───────────────────────────────────────────────
64
+
65
+ def remember(self, content: str, scope: str = "project", category: str = "general") -> dict | None:
66
+ """Remember something. scope: 'global' or 'project'."""
67
+ if scope == "global":
68
+ return self.add_global_memory(content, category)
69
+ return self.add_project_memory(content, category)
70
+
71
+ def recall(self, query: str, scope: str = "both") -> list[dict]:
72
+ """Recall memories matching query."""
73
+ results: list[dict] = []
74
+ if scope in ("global", "both"):
75
+ results.extend(self.search_global_memories(query))
76
+ if scope in ("project", "both") and self._project:
77
+ results.extend(self.search_project_memories(query))
78
+ return results
@@ -0,0 +1,106 @@
1
+ """Token optimizer - compacts and optimizes prompts for minimal token usage."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+
7
+
8
+ def optimize_prompt(text: str) -> str:
9
+ """Optimize a prompt for token efficiency while preserving meaning."""
10
+ lines = text.split("\n")
11
+ optimized: list[str] = []
12
+ prev_blank = False
13
+
14
+ for line in lines:
15
+ stripped = line.strip()
16
+
17
+ # Collapse multiple blank lines
18
+ if not stripped:
19
+ if not prev_blank:
20
+ optimized.append("")
21
+ prev_blank = True
22
+ continue
23
+
24
+ prev_blank = False
25
+
26
+ # Skip redundant phrases
27
+ if _is_redundant(stripped):
28
+ continue
29
+
30
+ # Compact verbose phrases
31
+ line = _compact_phrases(stripped)
32
+ optimized.append(line)
33
+
34
+ return "\n".join(optimized).strip()
35
+
36
+
37
+ def _is_redundant(line: str) -> bool:
38
+ """Check if a line is redundant and can be removed."""
39
+ redundant_patterns = [
40
+ r"^(it is important to|it is essential to|make sure to|be sure to|don't forget to)\s",
41
+ r"^(please|kindly|ensure that you|remember to)\s",
42
+ r"^(always remember that|note that|keep in mind that)\s",
43
+ ]
44
+ for pattern in redundant_patterns:
45
+ if re.match(pattern, line, re.IGNORECASE):
46
+ return True
47
+ return False
48
+
49
+
50
+ def _compact_phrases(text: str) -> str:
51
+ """Replace verbose phrases with concise equivalents."""
52
+ replacements = [
53
+ (r"in order to", "to"),
54
+ (r"for the purpose of", "for"),
55
+ (r"due to the fact that", "because"),
56
+ (r"at this point in time", "now"),
57
+ (r"in the event that", "if"),
58
+ (r"on a regular basis", "regularly"),
59
+ (r"with regard to", "regarding"),
60
+ (r"in the process of", "while"),
61
+ (r"has the ability to", "can"),
62
+ (r"is able to", "can"),
63
+ ]
64
+ for pattern, replacement in replacements:
65
+ text = re.sub(pattern, replacement, text, flags=re.IGNORECASE)
66
+ return text
67
+
68
+
69
+ def estimate_tokens(text: str) -> int:
70
+ """Rough token count estimate (1 token ≈ 4 chars for English)."""
71
+ return len(text) // 4
72
+
73
+
74
+ def compress_context(messages: list[dict[str, str]], max_tokens: int = 8000) -> list[dict[str, str]]:
75
+ """Compress chat context to fit within token budget."""
76
+ total = sum(estimate_tokens(m["content"]) for m in messages)
77
+
78
+ if total <= max_tokens:
79
+ return messages
80
+
81
+ # Keep the system message and recent messages
82
+ result: list[dict[str, str]] = []
83
+ budget = max_tokens
84
+
85
+ # Always keep first message if it's a system
86
+ if messages and messages[0]["role"] == "system":
87
+ system_msg = messages[0]
88
+ system_tokens = estimate_tokens(system_msg["content"])
89
+ result.append(system_msg)
90
+ budget -= system_tokens
91
+ messages = messages[1:]
92
+
93
+ # Keep recent messages, summarize old ones
94
+ kept: list[dict[str, str]] = []
95
+ for msg in reversed(messages):
96
+ msg_tokens = estimate_tokens(msg["content"])
97
+ if budget - msg_tokens >= 0:
98
+ kept.insert(0, msg)
99
+ budget -= msg_tokens
100
+ else:
101
+ break
102
+
103
+ if kept:
104
+ result.extend(kept)
105
+
106
+ return result