meredith-agent 0.2.6__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 (39) hide show
  1. coding_agent/__init__.py +38 -0
  2. coding_agent/acp/__init__.py +12 -0
  3. coding_agent/acp/server.py +269 -0
  4. coding_agent/agent/__init__.py +20 -0
  5. coding_agent/agent/core.py +545 -0
  6. coding_agent/agent/planner.py +388 -0
  7. coding_agent/agent/verifier.py +273 -0
  8. coding_agent/config.py +362 -0
  9. coding_agent/context/__init__.py +17 -0
  10. coding_agent/context/budget.py +184 -0
  11. coding_agent/context/compressor.py +323 -0
  12. coding_agent/context/manager.py +327 -0
  13. coding_agent/llm/__init__.py +26 -0
  14. coding_agent/llm/base.py +216 -0
  15. coding_agent/llm/local.py +527 -0
  16. coding_agent/llm/remote.py +304 -0
  17. coding_agent/main.py +212 -0
  18. coding_agent/memory/__init__.py +15 -0
  19. coding_agent/memory/store.py +358 -0
  20. coding_agent/rag/__init__.py +18 -0
  21. coding_agent/rag/chunker.py +484 -0
  22. coding_agent/rag/indexer.py +515 -0
  23. coding_agent/rag/retriever.py +390 -0
  24. coding_agent/recovery/__init__.py +14 -0
  25. coding_agent/recovery/detector.py +303 -0
  26. coding_agent/recovery/strategies.py +288 -0
  27. coding_agent/tools/__init__.py +18 -0
  28. coding_agent/tools/base.py +395 -0
  29. coding_agent/tools/fs.py +419 -0
  30. coding_agent/tools/git.py +201 -0
  31. coding_agent/tools/router.py +248 -0
  32. coding_agent/tools/search.py +487 -0
  33. coding_agent/tools/web.py +419 -0
  34. coding_agent/types.py +430 -0
  35. meredith_agent-0.2.6.dist-info/METADATA +327 -0
  36. meredith_agent-0.2.6.dist-info/RECORD +39 -0
  37. meredith_agent-0.2.6.dist-info/WHEEL +4 -0
  38. meredith_agent-0.2.6.dist-info/entry_points.txt +2 -0
  39. meredith_agent-0.2.6.dist-info/licenses/LICENSE +182 -0
@@ -0,0 +1,38 @@
1
+ """
2
+ meredith: A modern AI coding agent with RAG, ACP integration,
3
+ and smart context management.
4
+
5
+ Supports any OpenAI-compatible remote API (Claude, GPT, Opencode, etc.)
6
+ and local models via Ollama (Linux, macOS, Windows) or MLX (Apple Silicon).
7
+ """
8
+
9
+ __version__ = "0.2.6"
10
+
11
+ from coding_agent.types import (
12
+ AgentState,
13
+ LoopDetection,
14
+ LoopType,
15
+ Message,
16
+ Plan,
17
+ RecoveryAction,
18
+ Role,
19
+ Step,
20
+ SubTask,
21
+ ToolCall,
22
+ ToolResult,
23
+ )
24
+
25
+ __all__ = [
26
+ "__version__",
27
+ "AgentState",
28
+ "LoopDetection",
29
+ "LoopType",
30
+ "Message",
31
+ "Plan",
32
+ "RecoveryAction",
33
+ "Role",
34
+ "Step",
35
+ "SubTask",
36
+ "ToolCall",
37
+ "ToolResult",
38
+ ]
@@ -0,0 +1,12 @@
1
+ """
2
+ ACP server: exposes the coding agent over the Agent Client Protocol.
3
+
4
+ The ACP server communicates with ACP-aware editors
5
+ (Zed, JetBrains, Neovim, Emacs, etc.) via the stdio transport.
6
+ """
7
+
8
+ from coding_agent.acp.server import CodingAgentServer
9
+
10
+ __all__ = [
11
+ "CodingAgentServer",
12
+ ]
@@ -0,0 +1,269 @@
1
+ """
2
+ ACP server: exposes the coding agent over the Agent Client Protocol.
3
+
4
+ Replaces the previous MCP server. Uses the official `agent-client-protocol`
5
+ Python SDK to communicate with ACP-aware editors (Zed, JetBrains, Neovim, etc.).
6
+
7
+ Usage (standalone):
8
+ python -m coding_agent.acp.server --profile local_model
9
+
10
+ The server registers as an ACP agent. When the editor sends a prompt,
11
+ the coding agent's ReAct loop runs and streams status updates back
12
+ to the editor via `session/update` notifications.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import asyncio
18
+ import logging
19
+ from typing import Any
20
+ from uuid import uuid4
21
+
22
+ from acp import (
23
+ PROTOCOL_VERSION,
24
+ Agent,
25
+ InitializeResponse,
26
+ NewSessionResponse,
27
+ PromptResponse,
28
+ run_agent,
29
+ text_block,
30
+ update_agent_message,
31
+ )
32
+ from acp.interfaces import Client
33
+ from acp.schema import (
34
+ AgentCapabilities,
35
+ AudioContentBlock,
36
+ AuthenticateResponse,
37
+ ClientCapabilities,
38
+ CloseSessionResponse,
39
+ EmbeddedResourceContentBlock,
40
+ ForkSessionResponse,
41
+ HttpMcpServer,
42
+ ImageContentBlock,
43
+ Implementation,
44
+ ListSessionsResponse,
45
+ LoadSessionResponse,
46
+ McpServerStdio,
47
+ ResourceContentBlock,
48
+ ResumeSessionResponse,
49
+ SetSessionConfigOptionResponse,
50
+ SetSessionModelResponse,
51
+ SetSessionModeResponse,
52
+ SseMcpServer,
53
+ TextContentBlock,
54
+ )
55
+
56
+ from coding_agent.agent.core import AgentCore
57
+ from coding_agent.config import load_config
58
+ from coding_agent.main import create_llm_client
59
+
60
+ logger = logging.getLogger(__name__)
61
+
62
+
63
+ class CodingAgentServer(Agent):
64
+ """
65
+ ACP-compatible agent server wrapping the coding agent's AgentCore.
66
+
67
+ Each `prompt` call spawns a fresh AgentCore with the user's text
68
+ as the task, executes the full ReAct loop, and streams a summary
69
+ back to the editor.
70
+ """
71
+
72
+ _conn: Client
73
+
74
+ def __init__(self, profile: str = "large_model") -> None:
75
+ self._profile = profile
76
+ self._config = load_config(profile)
77
+
78
+ def on_connect(self, conn: Client) -> None:
79
+ self._conn = conn
80
+
81
+ async def initialize(
82
+ self,
83
+ protocol_version: int,
84
+ client_capabilities: ClientCapabilities | None = None,
85
+ client_info: Implementation | None = None,
86
+ **kwargs: Any,
87
+ ) -> InitializeResponse:
88
+ return InitializeResponse(
89
+ protocol_version=PROTOCOL_VERSION,
90
+ agent_capabilities=AgentCapabilities(),
91
+ agent_info=Implementation(
92
+ name="meredith",
93
+ title="meredith",
94
+ version="0.2.6",
95
+ ),
96
+ )
97
+
98
+ async def new_session(
99
+ self,
100
+ cwd: str,
101
+ additional_directories: list[str] | None = None,
102
+ mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio] | None = None,
103
+ **kwargs: Any,
104
+ ) -> NewSessionResponse:
105
+ session_id = uuid4().hex
106
+ logger.info("New ACP session: %s (cwd=%s)", session_id, cwd)
107
+ return NewSessionResponse(session_id=session_id)
108
+
109
+ async def prompt(
110
+ self,
111
+ prompt: list[
112
+ TextContentBlock
113
+ | ImageContentBlock
114
+ | AudioContentBlock
115
+ | ResourceContentBlock
116
+ | EmbeddedResourceContentBlock
117
+ ],
118
+ session_id: str,
119
+ message_id: str | None = None,
120
+ **kwargs: Any,
121
+ ) -> PromptResponse:
122
+ user_text = self._extract_text(prompt)
123
+ if not user_text:
124
+ user_text = "(no text provided)"
125
+
126
+ logger.info("ACP prompt on session %s: %.80s", session_id, user_text)
127
+
128
+ llm = create_llm_client(self._config)
129
+ async with AgentCore(self._config, llm, user_text) as agent:
130
+ success = await agent.run()
131
+
132
+ summary = self._build_summary(agent, success)
133
+ if self._conn:
134
+ chunk = update_agent_message(text_block(summary))
135
+ await self._conn.session_update(
136
+ session_id=session_id,
137
+ update=chunk,
138
+ source="coding_agent",
139
+ )
140
+
141
+ return PromptResponse(stop_reason="end_turn", user_message_id=message_id)
142
+
143
+ async def cancel(self, session_id: str, **kwargs: Any) -> None:
144
+ logger.info("Cancel requested for session %s", session_id)
145
+
146
+ async def load_session(
147
+ self,
148
+ cwd: str,
149
+ session_id: str,
150
+ additional_directories: list[str] | None = None,
151
+ mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio] | None = None,
152
+ **kwargs: Any,
153
+ ) -> LoadSessionResponse | None:
154
+ return None
155
+
156
+ async def list_sessions(
157
+ self,
158
+ additional_directories: list[str] | None = None,
159
+ cursor: str | None = None,
160
+ cwd: str | None = None,
161
+ **kwargs: Any,
162
+ ) -> ListSessionsResponse:
163
+ return ListSessionsResponse(sessions=[])
164
+
165
+ async def set_session_mode(
166
+ self, mode_id: str, session_id: str, **kwargs: Any
167
+ ) -> SetSessionModeResponse | None:
168
+ return None
169
+
170
+ async def set_session_model(
171
+ self, model_id: str, session_id: str, **kwargs: Any
172
+ ) -> SetSessionModelResponse | None:
173
+ return None
174
+
175
+ async def set_config_option(
176
+ self, config_id: str, session_id: str, value: str | bool, **kwargs: Any
177
+ ) -> SetSessionConfigOptionResponse | None:
178
+ return None
179
+
180
+ async def authenticate(self, method_id: str, **kwargs: Any) -> AuthenticateResponse | None:
181
+ return None
182
+
183
+ async def fork_session(
184
+ self,
185
+ cwd: str,
186
+ session_id: str,
187
+ additional_directories: list[str] | None = None,
188
+ mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio] | None = None,
189
+ **kwargs: Any,
190
+ ) -> ForkSessionResponse:
191
+ return ForkSessionResponse(session_id=session_id)
192
+
193
+ async def resume_session(
194
+ self,
195
+ cwd: str,
196
+ session_id: str,
197
+ additional_directories: list[str] | None = None,
198
+ mcp_servers: list[HttpMcpServer | SseMcpServer | McpServerStdio] | None = None,
199
+ **kwargs: Any,
200
+ ) -> ResumeSessionResponse:
201
+ return ResumeSessionResponse()
202
+
203
+ async def close_session(self, session_id: str, **kwargs: Any) -> CloseSessionResponse | None:
204
+ return None
205
+
206
+ async def ext_method(self, method: str, params: dict[str, Any]) -> dict[str, Any]:
207
+ return {}
208
+
209
+ async def ext_notification(self, method: str, params: dict[str, Any]) -> None:
210
+ pass
211
+
212
+ # ── Helpers ────────────────────────────────────────────────
213
+
214
+ @staticmethod
215
+ def _extract_text(
216
+ prompt: list[
217
+ TextContentBlock
218
+ | ImageContentBlock
219
+ | AudioContentBlock
220
+ | ResourceContentBlock
221
+ | EmbeddedResourceContentBlock
222
+ ],
223
+ ) -> str:
224
+ parts: list[str] = []
225
+ for block in prompt:
226
+ if isinstance(block, dict):
227
+ text = block.get("text", "")
228
+ else:
229
+ text = getattr(block, "text", "") or ""
230
+ if text:
231
+ parts.append(text)
232
+ return "\n".join(parts)
233
+
234
+ @staticmethod
235
+ def _build_summary(agent: AgentCore, success: bool) -> str:
236
+ steps = agent.state.step_count
237
+ files = agent.state.files_modified
238
+ status = "completed" if success else "max steps reached"
239
+ parts = [
240
+ f"Agent finished (status: {status}, steps: {steps})",
241
+ ]
242
+ if files:
243
+ parts.append(f"Files modified: {', '.join(sorted(files)[:10])}")
244
+ if steps > 0:
245
+ parts.append(f"Tokens used: {agent.state.total_tokens_used}")
246
+ return "\n".join(parts)
247
+
248
+
249
+ def main() -> None:
250
+ """Run the ACP agent server as a standalone process."""
251
+ import argparse
252
+
253
+ parser = argparse.ArgumentParser(description="Coding Agent (ACP Server)")
254
+ parser.add_argument(
255
+ "--profile", "-p",
256
+ default="large_model",
257
+ choices=["large_model", "local_model"],
258
+ help="Configuration profile",
259
+ )
260
+ args = parser.parse_args()
261
+
262
+ logging.basicConfig(level=logging.WARNING)
263
+
264
+ server = CodingAgentServer(profile=args.profile)
265
+ asyncio.run(run_agent(server))
266
+
267
+
268
+ if __name__ == "__main__":
269
+ main()
@@ -0,0 +1,20 @@
1
+ """
2
+ Agent subpackage: the core ReAct loop, planner, and verifier.
3
+
4
+ AgentCore orchestrates the full think → act → observe cycle,
5
+ delegating to Planner for task decomposition and Verifier for
6
+ post-step quality checks.
7
+ """
8
+
9
+ from coding_agent.agent.core import AgentCore
10
+ from coding_agent.agent.planner import FlatPlanner, Planner, TreeOfThoughtPlanner
11
+ from coding_agent.agent.verifier import VerificationResult, Verifier
12
+
13
+ __all__ = [
14
+ "AgentCore",
15
+ "FlatPlanner",
16
+ "Planner",
17
+ "TreeOfThoughtPlanner",
18
+ "VerificationResult",
19
+ "Verifier",
20
+ ]