mita-code 0.1.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 (67) hide show
  1. mita/__init__.py +5 -0
  2. mita/__main__.py +5 -0
  3. mita/agent/__init__.py +1 -0
  4. mita/agent/context.py +43 -0
  5. mita/agent/conversation.py +101 -0
  6. mita/agent/loop.py +594 -0
  7. mita/agent/system_prompt.py +75 -0
  8. mita/cli.py +940 -0
  9. mita/config/__init__.py +6 -0
  10. mita/config/defaults.py +41 -0
  11. mita/config/loader.py +53 -0
  12. mita/config/schema.py +131 -0
  13. mita/hooks/__init__.py +1 -0
  14. mita/hooks/manager.py +94 -0
  15. mita/hooks/runner.py +145 -0
  16. mita/index/__init__.py +1 -0
  17. mita/index/embeddings.py +49 -0
  18. mita/index/manager.py +170 -0
  19. mita/index/parser.py +331 -0
  20. mita/index/retriever.py +53 -0
  21. mita/index/store.py +143 -0
  22. mita/llm/__init__.py +1 -0
  23. mita/llm/client.py +86 -0
  24. mita/llm/instructor.py +80 -0
  25. mita/llm/streaming.py +58 -0
  26. mita/memory/__init__.py +6 -0
  27. mita/memory/discovery.py +61 -0
  28. mita/memory/loader.py +76 -0
  29. mita/memory/manager.py +117 -0
  30. mita/models/__init__.py +13 -0
  31. mita/models/hardware.py +289 -0
  32. mita/models/manager.py +268 -0
  33. mita/models/ollama_client.py +104 -0
  34. mita/models/recommender.py +88 -0
  35. mita/models/registry.py +167 -0
  36. mita/models/server.py +262 -0
  37. mita/plugins/__init__.py +1 -0
  38. mita/plugins/client.py +152 -0
  39. mita/plugins/manager.py +210 -0
  40. mita/py.typed +0 -0
  41. mita/skills/__init__.py +1 -0
  42. mita/skills/executor.py +84 -0
  43. mita/skills/loader.py +117 -0
  44. mita/skills/manager.py +129 -0
  45. mita/tools/__init__.py +1 -0
  46. mita/tools/builtins/__init__.py +28 -0
  47. mita/tools/builtins/file_edit.py +71 -0
  48. mita/tools/builtins/file_read.py +74 -0
  49. mita/tools/builtins/file_write.py +42 -0
  50. mita/tools/builtins/git.py +112 -0
  51. mita/tools/builtins/glob_tool.py +67 -0
  52. mita/tools/builtins/grep_tool.py +93 -0
  53. mita/tools/builtins/shell.py +83 -0
  54. mita/tools/executor.py +80 -0
  55. mita/tools/registry.py +69 -0
  56. mita/tools/safety.py +91 -0
  57. mita/tools/schema.py +87 -0
  58. mita/ui/__init__.py +1 -0
  59. mita/ui/display.py +139 -0
  60. mita/ui/repl.py +88 -0
  61. mita/ui/spinner.py +48 -0
  62. mita/ui/theme.py +23 -0
  63. mita_code-0.1.0.dist-info/METADATA +227 -0
  64. mita_code-0.1.0.dist-info/RECORD +67 -0
  65. mita_code-0.1.0.dist-info/WHEEL +4 -0
  66. mita_code-0.1.0.dist-info/entry_points.txt +3 -0
  67. mita_code-0.1.0.dist-info/licenses/LICENSE +201 -0
mita/__init__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Mita Code — Local-first agentic coding assistant powered by Ollama."""
2
+
3
+ from importlib.metadata import version
4
+
5
+ __version__ = version("mita-code")
mita/__main__.py ADDED
@@ -0,0 +1,5 @@
1
+ """Allow running mita as `python -m mita`."""
2
+
3
+ from mita.cli import app
4
+
5
+ app()
mita/agent/__init__.py ADDED
@@ -0,0 +1 @@
1
+ """Core agent loop: prompt -> LLM -> tool calls -> execute -> loop."""
mita/agent/context.py ADDED
@@ -0,0 +1,43 @@
1
+ """Context assembly: memory + tool definitions → system prompt."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ from mita.agent.conversation import Conversation, Message, Role
8
+ from mita.agent.system_prompt import build_system_prompt
9
+ from mita.config.schema import MitaConfig
10
+ from mita.memory.loader import load_memory
11
+ from mita.tools.registry import ToolRegistry
12
+
13
+
14
+ def assemble_context(
15
+ conversation: Conversation,
16
+ config: MitaConfig,
17
+ registry: ToolRegistry,
18
+ cwd: Path | None = None,
19
+ ) -> None:
20
+ """Assemble the initial context for the agent loop.
21
+
22
+ Builds the system prompt from config, memory, and tool definitions,
23
+ and adds it to the conversation.
24
+
25
+ Args:
26
+ conversation: The conversation to add the system message to.
27
+ config: Application configuration.
28
+ registry: Tool registry with all available tools.
29
+ cwd: Working directory for memory discovery. Defaults to Path.cwd().
30
+ """
31
+ working_dir = cwd or Path.cwd()
32
+
33
+ # Load memory from MITA.md files
34
+ memory_content = load_memory(cwd=working_dir)
35
+
36
+ # Build system prompt
37
+ system_prompt = build_system_prompt(
38
+ config=config,
39
+ memory=memory_content,
40
+ tools=registry.get_definitions(),
41
+ )
42
+
43
+ conversation.add(Message(role=Role.SYSTEM, content=system_prompt))
@@ -0,0 +1,101 @@
1
+ """Message history management with token counting and truncation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from enum import StrEnum
6
+ from typing import Any
7
+
8
+ from pydantic import BaseModel, Field
9
+
10
+
11
+ class Role(StrEnum):
12
+ """Message roles in the conversation."""
13
+
14
+ SYSTEM = "system"
15
+ USER = "user"
16
+ ASSISTANT = "assistant"
17
+ TOOL = "tool"
18
+
19
+
20
+ class Message(BaseModel):
21
+ """A single message in the conversation."""
22
+
23
+ role: Role
24
+ content: str
25
+ tool_calls: list[dict[str, Any]] = Field(default_factory=list)
26
+ tool_call_id: str | None = None # for tool-result messages
27
+ name: str | None = None # tool name for tool-result messages
28
+
29
+ def to_api_dict(self) -> dict[str, Any]:
30
+ """Convert to LiteLLM/OpenAI API format."""
31
+ msg: dict[str, Any] = {"role": self.role.value, "content": self.content}
32
+ if self.tool_calls:
33
+ msg["tool_calls"] = self.tool_calls
34
+ if self.tool_call_id is not None:
35
+ msg["tool_call_id"] = self.tool_call_id
36
+ if self.name is not None:
37
+ msg["name"] = self.name
38
+ return msg
39
+
40
+
41
+ class Conversation(BaseModel):
42
+ """Conversation history with token estimation and truncation."""
43
+
44
+ messages: list[Message] = Field(default_factory=list)
45
+ total_tokens: int = 0
46
+
47
+ def add(self, msg: Message) -> None:
48
+ """Add a message to the conversation."""
49
+ self.messages.append(msg)
50
+ # Rough token estimate: ~4 chars per token
51
+ self.total_tokens += _estimate_tokens(msg.content)
52
+
53
+ def get_messages_for_api(self) -> list[dict[str, Any]]:
54
+ """Convert all messages to API format."""
55
+ return [m.to_api_dict() for m in self.messages]
56
+
57
+ def truncate_to_fit(self, max_tokens: int) -> None:
58
+ """Truncate conversation to fit within the token budget.
59
+
60
+ Strategy: Keep the system message(s) and last N messages.
61
+ Remove oldest non-system messages first.
62
+ """
63
+ if self.total_tokens <= max_tokens:
64
+ return
65
+
66
+ # Separate system messages from the rest
67
+ system_msgs = [m for m in self.messages if m.role == Role.SYSTEM]
68
+ other_msgs = [m for m in self.messages if m.role != Role.SYSTEM]
69
+
70
+ system_tokens = sum(_estimate_tokens(m.content) for m in system_msgs)
71
+ budget = max_tokens - system_tokens
72
+
73
+ if budget <= 0:
74
+ # System prompt alone exceeds budget — keep just the last system msg
75
+ self.messages = system_msgs[-1:] if system_msgs else []
76
+ self.total_tokens = sum(_estimate_tokens(m.content) for m in self.messages)
77
+ return
78
+
79
+ # Keep messages from the end until we hit the budget
80
+ kept: list[Message] = []
81
+ used = 0
82
+ for msg in reversed(other_msgs):
83
+ msg_tokens = _estimate_tokens(msg.content)
84
+ if used + msg_tokens > budget:
85
+ break
86
+ kept.append(msg)
87
+ used += msg_tokens
88
+
89
+ kept.reverse()
90
+ self.messages = system_msgs + kept
91
+ self.total_tokens = system_tokens + used
92
+
93
+ def clear_non_system(self) -> None:
94
+ """Clear all non-system messages (for /clear command)."""
95
+ self.messages = [m for m in self.messages if m.role == Role.SYSTEM]
96
+ self.total_tokens = sum(_estimate_tokens(m.content) for m in self.messages)
97
+
98
+
99
+ def _estimate_tokens(text: str) -> int:
100
+ """Rough token estimate: ~4 characters per token."""
101
+ return max(1, len(text) // 4)