emdash-core 0.1.7__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 (187) hide show
  1. emdash_core/__init__.py +3 -0
  2. emdash_core/agent/__init__.py +37 -0
  3. emdash_core/agent/agents.py +225 -0
  4. emdash_core/agent/code_reviewer.py +476 -0
  5. emdash_core/agent/compaction.py +143 -0
  6. emdash_core/agent/context_manager.py +140 -0
  7. emdash_core/agent/events.py +338 -0
  8. emdash_core/agent/handlers.py +224 -0
  9. emdash_core/agent/inprocess_subagent.py +377 -0
  10. emdash_core/agent/mcp/__init__.py +50 -0
  11. emdash_core/agent/mcp/client.py +346 -0
  12. emdash_core/agent/mcp/config.py +302 -0
  13. emdash_core/agent/mcp/manager.py +496 -0
  14. emdash_core/agent/mcp/tool_factory.py +213 -0
  15. emdash_core/agent/prompts/__init__.py +38 -0
  16. emdash_core/agent/prompts/main_agent.py +104 -0
  17. emdash_core/agent/prompts/subagents.py +131 -0
  18. emdash_core/agent/prompts/workflow.py +136 -0
  19. emdash_core/agent/providers/__init__.py +34 -0
  20. emdash_core/agent/providers/base.py +143 -0
  21. emdash_core/agent/providers/factory.py +80 -0
  22. emdash_core/agent/providers/models.py +220 -0
  23. emdash_core/agent/providers/openai_provider.py +463 -0
  24. emdash_core/agent/providers/transformers_provider.py +217 -0
  25. emdash_core/agent/research/__init__.py +81 -0
  26. emdash_core/agent/research/agent.py +143 -0
  27. emdash_core/agent/research/controller.py +254 -0
  28. emdash_core/agent/research/critic.py +428 -0
  29. emdash_core/agent/research/macros.py +469 -0
  30. emdash_core/agent/research/planner.py +449 -0
  31. emdash_core/agent/research/researcher.py +436 -0
  32. emdash_core/agent/research/state.py +523 -0
  33. emdash_core/agent/research/synthesizer.py +594 -0
  34. emdash_core/agent/reviewer_profile.py +475 -0
  35. emdash_core/agent/rules.py +123 -0
  36. emdash_core/agent/runner.py +601 -0
  37. emdash_core/agent/session.py +262 -0
  38. emdash_core/agent/spec_schema.py +66 -0
  39. emdash_core/agent/specification.py +479 -0
  40. emdash_core/agent/subagent.py +397 -0
  41. emdash_core/agent/subagent_prompts.py +13 -0
  42. emdash_core/agent/toolkit.py +482 -0
  43. emdash_core/agent/toolkits/__init__.py +64 -0
  44. emdash_core/agent/toolkits/base.py +96 -0
  45. emdash_core/agent/toolkits/explore.py +47 -0
  46. emdash_core/agent/toolkits/plan.py +55 -0
  47. emdash_core/agent/tools/__init__.py +141 -0
  48. emdash_core/agent/tools/analytics.py +436 -0
  49. emdash_core/agent/tools/base.py +131 -0
  50. emdash_core/agent/tools/coding.py +484 -0
  51. emdash_core/agent/tools/github_mcp.py +592 -0
  52. emdash_core/agent/tools/history.py +13 -0
  53. emdash_core/agent/tools/modes.py +153 -0
  54. emdash_core/agent/tools/plan.py +206 -0
  55. emdash_core/agent/tools/plan_write.py +135 -0
  56. emdash_core/agent/tools/search.py +412 -0
  57. emdash_core/agent/tools/spec.py +341 -0
  58. emdash_core/agent/tools/task.py +262 -0
  59. emdash_core/agent/tools/task_output.py +204 -0
  60. emdash_core/agent/tools/tasks.py +454 -0
  61. emdash_core/agent/tools/traversal.py +588 -0
  62. emdash_core/agent/tools/web.py +179 -0
  63. emdash_core/analytics/__init__.py +5 -0
  64. emdash_core/analytics/engine.py +1286 -0
  65. emdash_core/api/__init__.py +5 -0
  66. emdash_core/api/agent.py +308 -0
  67. emdash_core/api/agents.py +154 -0
  68. emdash_core/api/analyze.py +264 -0
  69. emdash_core/api/auth.py +173 -0
  70. emdash_core/api/context.py +77 -0
  71. emdash_core/api/db.py +121 -0
  72. emdash_core/api/embed.py +131 -0
  73. emdash_core/api/feature.py +143 -0
  74. emdash_core/api/health.py +93 -0
  75. emdash_core/api/index.py +162 -0
  76. emdash_core/api/plan.py +110 -0
  77. emdash_core/api/projectmd.py +210 -0
  78. emdash_core/api/query.py +320 -0
  79. emdash_core/api/research.py +122 -0
  80. emdash_core/api/review.py +161 -0
  81. emdash_core/api/router.py +76 -0
  82. emdash_core/api/rules.py +116 -0
  83. emdash_core/api/search.py +119 -0
  84. emdash_core/api/spec.py +99 -0
  85. emdash_core/api/swarm.py +223 -0
  86. emdash_core/api/tasks.py +109 -0
  87. emdash_core/api/team.py +120 -0
  88. emdash_core/auth/__init__.py +17 -0
  89. emdash_core/auth/github.py +389 -0
  90. emdash_core/config.py +74 -0
  91. emdash_core/context/__init__.py +52 -0
  92. emdash_core/context/models.py +50 -0
  93. emdash_core/context/providers/__init__.py +11 -0
  94. emdash_core/context/providers/base.py +74 -0
  95. emdash_core/context/providers/explored_areas.py +183 -0
  96. emdash_core/context/providers/touched_areas.py +360 -0
  97. emdash_core/context/registry.py +73 -0
  98. emdash_core/context/reranker.py +199 -0
  99. emdash_core/context/service.py +260 -0
  100. emdash_core/context/session.py +352 -0
  101. emdash_core/core/__init__.py +104 -0
  102. emdash_core/core/config.py +454 -0
  103. emdash_core/core/exceptions.py +55 -0
  104. emdash_core/core/models.py +265 -0
  105. emdash_core/core/review_config.py +57 -0
  106. emdash_core/db/__init__.py +67 -0
  107. emdash_core/db/auth.py +134 -0
  108. emdash_core/db/models.py +91 -0
  109. emdash_core/db/provider.py +222 -0
  110. emdash_core/db/providers/__init__.py +5 -0
  111. emdash_core/db/providers/supabase.py +452 -0
  112. emdash_core/embeddings/__init__.py +24 -0
  113. emdash_core/embeddings/indexer.py +534 -0
  114. emdash_core/embeddings/models.py +192 -0
  115. emdash_core/embeddings/providers/__init__.py +7 -0
  116. emdash_core/embeddings/providers/base.py +112 -0
  117. emdash_core/embeddings/providers/fireworks.py +141 -0
  118. emdash_core/embeddings/providers/openai.py +104 -0
  119. emdash_core/embeddings/registry.py +146 -0
  120. emdash_core/embeddings/service.py +215 -0
  121. emdash_core/graph/__init__.py +26 -0
  122. emdash_core/graph/builder.py +134 -0
  123. emdash_core/graph/connection.py +692 -0
  124. emdash_core/graph/schema.py +416 -0
  125. emdash_core/graph/writer.py +667 -0
  126. emdash_core/ingestion/__init__.py +7 -0
  127. emdash_core/ingestion/change_detector.py +150 -0
  128. emdash_core/ingestion/git/__init__.py +5 -0
  129. emdash_core/ingestion/git/commit_analyzer.py +196 -0
  130. emdash_core/ingestion/github/__init__.py +6 -0
  131. emdash_core/ingestion/github/pr_fetcher.py +296 -0
  132. emdash_core/ingestion/github/task_extractor.py +100 -0
  133. emdash_core/ingestion/orchestrator.py +540 -0
  134. emdash_core/ingestion/parsers/__init__.py +10 -0
  135. emdash_core/ingestion/parsers/base_parser.py +66 -0
  136. emdash_core/ingestion/parsers/call_graph_builder.py +121 -0
  137. emdash_core/ingestion/parsers/class_extractor.py +154 -0
  138. emdash_core/ingestion/parsers/function_extractor.py +202 -0
  139. emdash_core/ingestion/parsers/import_analyzer.py +119 -0
  140. emdash_core/ingestion/parsers/python_parser.py +123 -0
  141. emdash_core/ingestion/parsers/registry.py +72 -0
  142. emdash_core/ingestion/parsers/ts_ast_parser.js +313 -0
  143. emdash_core/ingestion/parsers/typescript_parser.py +278 -0
  144. emdash_core/ingestion/repository.py +346 -0
  145. emdash_core/models/__init__.py +38 -0
  146. emdash_core/models/agent.py +68 -0
  147. emdash_core/models/index.py +77 -0
  148. emdash_core/models/query.py +113 -0
  149. emdash_core/planning/__init__.py +7 -0
  150. emdash_core/planning/agent_api.py +413 -0
  151. emdash_core/planning/context_builder.py +265 -0
  152. emdash_core/planning/feature_context.py +232 -0
  153. emdash_core/planning/feature_expander.py +646 -0
  154. emdash_core/planning/llm_explainer.py +198 -0
  155. emdash_core/planning/similarity.py +509 -0
  156. emdash_core/planning/team_focus.py +821 -0
  157. emdash_core/server.py +153 -0
  158. emdash_core/sse/__init__.py +5 -0
  159. emdash_core/sse/stream.py +196 -0
  160. emdash_core/swarm/__init__.py +17 -0
  161. emdash_core/swarm/merge_agent.py +383 -0
  162. emdash_core/swarm/session_manager.py +274 -0
  163. emdash_core/swarm/swarm_runner.py +226 -0
  164. emdash_core/swarm/task_definition.py +137 -0
  165. emdash_core/swarm/worker_spawner.py +319 -0
  166. emdash_core/swarm/worktree_manager.py +278 -0
  167. emdash_core/templates/__init__.py +10 -0
  168. emdash_core/templates/defaults/agent-builder.md.template +82 -0
  169. emdash_core/templates/defaults/focus.md.template +115 -0
  170. emdash_core/templates/defaults/pr-review-enhanced.md.template +309 -0
  171. emdash_core/templates/defaults/pr-review.md.template +80 -0
  172. emdash_core/templates/defaults/project.md.template +85 -0
  173. emdash_core/templates/defaults/research_critic.md.template +112 -0
  174. emdash_core/templates/defaults/research_planner.md.template +85 -0
  175. emdash_core/templates/defaults/research_synthesizer.md.template +128 -0
  176. emdash_core/templates/defaults/reviewer.md.template +81 -0
  177. emdash_core/templates/defaults/spec.md.template +41 -0
  178. emdash_core/templates/defaults/tasks.md.template +78 -0
  179. emdash_core/templates/loader.py +296 -0
  180. emdash_core/utils/__init__.py +45 -0
  181. emdash_core/utils/git.py +84 -0
  182. emdash_core/utils/image.py +502 -0
  183. emdash_core/utils/logger.py +51 -0
  184. emdash_core-0.1.7.dist-info/METADATA +35 -0
  185. emdash_core-0.1.7.dist-info/RECORD +187 -0
  186. emdash_core-0.1.7.dist-info/WHEEL +4 -0
  187. emdash_core-0.1.7.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,3 @@
1
+ """EmDash Core - FastAPI server for code intelligence."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,37 @@
1
+ """Agent module for LLM-powered graph exploration.
2
+
3
+ This module provides tools and infrastructure for LLM agents to explore
4
+ and understand code graphs.
5
+
6
+ Imports are lazy to allow submodules (like subagent) to be imported
7
+ without requiring all dependencies (like kuzu).
8
+ """
9
+
10
+
11
+ def __getattr__(name: str):
12
+ """Lazy import to avoid loading kuzu when not needed."""
13
+ if name == "AgentToolkit":
14
+ from .toolkit import AgentToolkit
15
+ return AgentToolkit
16
+ elif name == "AgentSession":
17
+ from .session import AgentSession
18
+ return AgentSession
19
+ elif name == "AgentRunner":
20
+ from .runner import AgentRunner
21
+ return AgentRunner
22
+ elif name == "ToolResult":
23
+ from .tools.base import ToolResult
24
+ return ToolResult
25
+ elif name == "ToolCategory":
26
+ from .tools.base import ToolCategory
27
+ return ToolCategory
28
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
29
+
30
+
31
+ __all__ = [
32
+ "AgentToolkit",
33
+ "AgentSession",
34
+ "AgentRunner",
35
+ "ToolResult",
36
+ "ToolCategory",
37
+ ]
@@ -0,0 +1,225 @@
1
+ """Custom agent loader from .emdash/agents/*.md files.
2
+
3
+ Allows users to define custom agent configurations with
4
+ specialized system prompts and tool selections.
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from pathlib import Path
9
+ from typing import Optional
10
+ import re
11
+
12
+ from ..utils.logger import log
13
+
14
+
15
+ @dataclass
16
+ class CustomAgent:
17
+ """A custom agent configuration loaded from markdown.
18
+
19
+ Attributes:
20
+ name: Agent name (from filename)
21
+ description: Brief description
22
+ system_prompt: Custom system prompt
23
+ tools: List of tools to enable
24
+ examples: Example interactions
25
+ file_path: Source file path
26
+ """
27
+
28
+ name: str
29
+ description: str = ""
30
+ system_prompt: str = ""
31
+ tools: list[str] = field(default_factory=list)
32
+ examples: list[dict] = field(default_factory=list)
33
+ file_path: Optional[Path] = None
34
+
35
+
36
+ def load_agents(
37
+ agents_dir: Optional[Path] = None,
38
+ ) -> dict[str, CustomAgent]:
39
+ """Load custom agents from .emdash/agents/ directory.
40
+
41
+ Agent files are markdown with frontmatter-style metadata:
42
+
43
+ ```markdown
44
+ ---
45
+ description: Security analysis agent
46
+ tools: [semantic_search, get_callers, get_impact_analysis]
47
+ ---
48
+
49
+ # System Prompt
50
+
51
+ You are a security-focused code analyst...
52
+
53
+ # Examples
54
+
55
+ ## Example 1
56
+ User: Find SQL injection vulnerabilities
57
+ Agent: I'll search for database query patterns...
58
+ ```
59
+
60
+ Args:
61
+ agents_dir: Directory containing agent .md files.
62
+ Defaults to .emdash/agents/ in cwd.
63
+
64
+ Returns:
65
+ Dict mapping agent name to CustomAgent
66
+ """
67
+ if agents_dir is None:
68
+ agents_dir = Path.cwd() / ".emdash" / "agents"
69
+
70
+ if not agents_dir.exists():
71
+ return {}
72
+
73
+ agents = {}
74
+
75
+ for md_file in agents_dir.glob("*.md"):
76
+ try:
77
+ agent = _parse_agent_file(md_file)
78
+ if agent:
79
+ agents[agent.name] = agent
80
+ log.debug(f"Loaded custom agent: {agent.name}")
81
+ except Exception as e:
82
+ log.warning(f"Failed to load agent from {md_file}: {e}")
83
+
84
+ if agents:
85
+ log.info(f"Loaded {len(agents)} custom agents")
86
+
87
+ return agents
88
+
89
+
90
+ def _parse_agent_file(file_path: Path) -> Optional[CustomAgent]:
91
+ """Parse a single agent markdown file.
92
+
93
+ Args:
94
+ file_path: Path to the .md file
95
+
96
+ Returns:
97
+ CustomAgent or None if parsing fails
98
+ """
99
+ content = file_path.read_text()
100
+
101
+ # Extract frontmatter
102
+ frontmatter = {}
103
+ body = content
104
+
105
+ if content.startswith("---"):
106
+ parts = content.split("---", 2)
107
+ if len(parts) >= 3:
108
+ frontmatter = _parse_frontmatter(parts[1])
109
+ body = parts[2].strip()
110
+
111
+ # Extract system prompt (content before # Examples)
112
+ system_prompt = body
113
+ examples = []
114
+
115
+ if "# Examples" in body:
116
+ prompt_part, examples_part = body.split("# Examples", 1)
117
+ system_prompt = prompt_part.strip()
118
+ examples = _parse_examples(examples_part)
119
+
120
+ # Remove "# System Prompt" header if present
121
+ if system_prompt.startswith("# System Prompt"):
122
+ system_prompt = system_prompt[len("# System Prompt") :].strip()
123
+
124
+ return CustomAgent(
125
+ name=file_path.stem,
126
+ description=frontmatter.get("description", ""),
127
+ system_prompt=system_prompt,
128
+ tools=frontmatter.get("tools", []),
129
+ examples=examples,
130
+ file_path=file_path,
131
+ )
132
+
133
+
134
+ def _parse_frontmatter(frontmatter_str: str) -> dict:
135
+ """Parse YAML-like frontmatter.
136
+
137
+ Simple parser for key: value pairs.
138
+
139
+ Args:
140
+ frontmatter_str: Frontmatter string
141
+
142
+ Returns:
143
+ Dict of parsed values
144
+ """
145
+ result = {}
146
+
147
+ for line in frontmatter_str.strip().split("\n"):
148
+ if ":" not in line:
149
+ continue
150
+
151
+ key, value = line.split(":", 1)
152
+ key = key.strip()
153
+ value = value.strip()
154
+
155
+ # Parse list values
156
+ if value.startswith("[") and value.endswith("]"):
157
+ # Simple list parsing
158
+ items = value[1:-1].split(",")
159
+ result[key] = [item.strip().strip("'\"") for item in items if item.strip()]
160
+ else:
161
+ result[key] = value.strip("'\"")
162
+
163
+ return result
164
+
165
+
166
+ def _parse_examples(examples_str: str) -> list[dict]:
167
+ """Parse example interactions from markdown.
168
+
169
+ Args:
170
+ examples_str: Examples section content
171
+
172
+ Returns:
173
+ List of example dicts with user/agent keys
174
+ """
175
+ examples = []
176
+
177
+ # Split by ## Example headers
178
+ example_blocks = re.split(r"##\s+Example\s*\d*", examples_str)
179
+
180
+ for block in example_blocks:
181
+ if not block.strip():
182
+ continue
183
+
184
+ example = {"user": "", "agent": ""}
185
+
186
+ # Find User: and Agent: sections
187
+ user_match = re.search(r"User:\s*(.+?)(?=Agent:|$)", block, re.DOTALL)
188
+ agent_match = re.search(r"Agent:\s*(.+?)(?=User:|$)", block, re.DOTALL)
189
+
190
+ if user_match:
191
+ example["user"] = user_match.group(1).strip()
192
+ if agent_match:
193
+ example["agent"] = agent_match.group(1).strip()
194
+
195
+ if example["user"] or example["agent"]:
196
+ examples.append(example)
197
+
198
+ return examples
199
+
200
+
201
+ def get_agent(name: str, agents_dir: Optional[Path] = None) -> Optional[CustomAgent]:
202
+ """Get a specific custom agent by name.
203
+
204
+ Args:
205
+ name: Agent name (filename without .md)
206
+ agents_dir: Optional agents directory
207
+
208
+ Returns:
209
+ CustomAgent or None if not found
210
+ """
211
+ agents = load_agents(agents_dir)
212
+ return agents.get(name)
213
+
214
+
215
+ def list_agents(agents_dir: Optional[Path] = None) -> list[str]:
216
+ """List available custom agent names.
217
+
218
+ Args:
219
+ agents_dir: Optional agents directory
220
+
221
+ Returns:
222
+ List of agent names
223
+ """
224
+ agents = load_agents(agents_dir)
225
+ return list(agents.keys())