emdash-core 0.1.16__tar.gz
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.
- emdash_core-0.1.16/PKG-INFO +35 -0
- emdash_core-0.1.16/emdash_core/__init__.py +8 -0
- emdash_core-0.1.16/emdash_core/agent/__init__.py +37 -0
- emdash_core-0.1.16/emdash_core/agent/agents.py +225 -0
- emdash_core-0.1.16/emdash_core/agent/code_reviewer.py +476 -0
- emdash_core-0.1.16/emdash_core/agent/compaction.py +143 -0
- emdash_core-0.1.16/emdash_core/agent/context_manager.py +140 -0
- emdash_core-0.1.16/emdash_core/agent/events.py +338 -0
- emdash_core-0.1.16/emdash_core/agent/handlers.py +224 -0
- emdash_core-0.1.16/emdash_core/agent/inprocess_subagent.py +377 -0
- emdash_core-0.1.16/emdash_core/agent/mcp/__init__.py +50 -0
- emdash_core-0.1.16/emdash_core/agent/mcp/client.py +346 -0
- emdash_core-0.1.16/emdash_core/agent/mcp/config.py +302 -0
- emdash_core-0.1.16/emdash_core/agent/mcp/manager.py +496 -0
- emdash_core-0.1.16/emdash_core/agent/mcp/tool_factory.py +213 -0
- emdash_core-0.1.16/emdash_core/agent/prompts/__init__.py +38 -0
- emdash_core-0.1.16/emdash_core/agent/prompts/main_agent.py +104 -0
- emdash_core-0.1.16/emdash_core/agent/prompts/subagents.py +131 -0
- emdash_core-0.1.16/emdash_core/agent/prompts/workflow.py +136 -0
- emdash_core-0.1.16/emdash_core/agent/providers/__init__.py +34 -0
- emdash_core-0.1.16/emdash_core/agent/providers/base.py +143 -0
- emdash_core-0.1.16/emdash_core/agent/providers/factory.py +80 -0
- emdash_core-0.1.16/emdash_core/agent/providers/models.py +220 -0
- emdash_core-0.1.16/emdash_core/agent/providers/openai_provider.py +463 -0
- emdash_core-0.1.16/emdash_core/agent/providers/transformers_provider.py +217 -0
- emdash_core-0.1.16/emdash_core/agent/research/__init__.py +81 -0
- emdash_core-0.1.16/emdash_core/agent/research/agent.py +143 -0
- emdash_core-0.1.16/emdash_core/agent/research/controller.py +254 -0
- emdash_core-0.1.16/emdash_core/agent/research/critic.py +428 -0
- emdash_core-0.1.16/emdash_core/agent/research/macros.py +469 -0
- emdash_core-0.1.16/emdash_core/agent/research/planner.py +449 -0
- emdash_core-0.1.16/emdash_core/agent/research/researcher.py +436 -0
- emdash_core-0.1.16/emdash_core/agent/research/state.py +523 -0
- emdash_core-0.1.16/emdash_core/agent/research/synthesizer.py +594 -0
- emdash_core-0.1.16/emdash_core/agent/reviewer_profile.py +475 -0
- emdash_core-0.1.16/emdash_core/agent/rules.py +123 -0
- emdash_core-0.1.16/emdash_core/agent/runner.py +775 -0
- emdash_core-0.1.16/emdash_core/agent/session.py +262 -0
- emdash_core-0.1.16/emdash_core/agent/spec_schema.py +66 -0
- emdash_core-0.1.16/emdash_core/agent/specification.py +479 -0
- emdash_core-0.1.16/emdash_core/agent/subagent.py +397 -0
- emdash_core-0.1.16/emdash_core/agent/subagent_prompts.py +13 -0
- emdash_core-0.1.16/emdash_core/agent/toolkit.py +482 -0
- emdash_core-0.1.16/emdash_core/agent/toolkits/__init__.py +64 -0
- emdash_core-0.1.16/emdash_core/agent/toolkits/base.py +96 -0
- emdash_core-0.1.16/emdash_core/agent/toolkits/explore.py +47 -0
- emdash_core-0.1.16/emdash_core/agent/toolkits/plan.py +55 -0
- emdash_core-0.1.16/emdash_core/agent/tools/__init__.py +141 -0
- emdash_core-0.1.16/emdash_core/agent/tools/analytics.py +436 -0
- emdash_core-0.1.16/emdash_core/agent/tools/base.py +131 -0
- emdash_core-0.1.16/emdash_core/agent/tools/coding.py +484 -0
- emdash_core-0.1.16/emdash_core/agent/tools/github_mcp.py +592 -0
- emdash_core-0.1.16/emdash_core/agent/tools/history.py +13 -0
- emdash_core-0.1.16/emdash_core/agent/tools/modes.py +153 -0
- emdash_core-0.1.16/emdash_core/agent/tools/plan.py +206 -0
- emdash_core-0.1.16/emdash_core/agent/tools/plan_write.py +135 -0
- emdash_core-0.1.16/emdash_core/agent/tools/search.py +412 -0
- emdash_core-0.1.16/emdash_core/agent/tools/spec.py +341 -0
- emdash_core-0.1.16/emdash_core/agent/tools/task.py +262 -0
- emdash_core-0.1.16/emdash_core/agent/tools/task_output.py +204 -0
- emdash_core-0.1.16/emdash_core/agent/tools/tasks.py +454 -0
- emdash_core-0.1.16/emdash_core/agent/tools/traversal.py +588 -0
- emdash_core-0.1.16/emdash_core/agent/tools/web.py +179 -0
- emdash_core-0.1.16/emdash_core/analytics/__init__.py +5 -0
- emdash_core-0.1.16/emdash_core/analytics/engine.py +1286 -0
- emdash_core-0.1.16/emdash_core/api/__init__.py +5 -0
- emdash_core-0.1.16/emdash_core/api/agent.py +308 -0
- emdash_core-0.1.16/emdash_core/api/agents.py +154 -0
- emdash_core-0.1.16/emdash_core/api/analyze.py +264 -0
- emdash_core-0.1.16/emdash_core/api/auth.py +173 -0
- emdash_core-0.1.16/emdash_core/api/context.py +77 -0
- emdash_core-0.1.16/emdash_core/api/db.py +121 -0
- emdash_core-0.1.16/emdash_core/api/embed.py +131 -0
- emdash_core-0.1.16/emdash_core/api/feature.py +143 -0
- emdash_core-0.1.16/emdash_core/api/health.py +93 -0
- emdash_core-0.1.16/emdash_core/api/index.py +162 -0
- emdash_core-0.1.16/emdash_core/api/plan.py +110 -0
- emdash_core-0.1.16/emdash_core/api/projectmd.py +212 -0
- emdash_core-0.1.16/emdash_core/api/query.py +320 -0
- emdash_core-0.1.16/emdash_core/api/research.py +122 -0
- emdash_core-0.1.16/emdash_core/api/review.py +161 -0
- emdash_core-0.1.16/emdash_core/api/router.py +76 -0
- emdash_core-0.1.16/emdash_core/api/rules.py +116 -0
- emdash_core-0.1.16/emdash_core/api/search.py +119 -0
- emdash_core-0.1.16/emdash_core/api/spec.py +99 -0
- emdash_core-0.1.16/emdash_core/api/swarm.py +223 -0
- emdash_core-0.1.16/emdash_core/api/tasks.py +109 -0
- emdash_core-0.1.16/emdash_core/api/team.py +120 -0
- emdash_core-0.1.16/emdash_core/auth/__init__.py +17 -0
- emdash_core-0.1.16/emdash_core/auth/github.py +389 -0
- emdash_core-0.1.16/emdash_core/config.py +74 -0
- emdash_core-0.1.16/emdash_core/context/__init__.py +52 -0
- emdash_core-0.1.16/emdash_core/context/models.py +50 -0
- emdash_core-0.1.16/emdash_core/context/providers/__init__.py +11 -0
- emdash_core-0.1.16/emdash_core/context/providers/base.py +74 -0
- emdash_core-0.1.16/emdash_core/context/providers/explored_areas.py +183 -0
- emdash_core-0.1.16/emdash_core/context/providers/touched_areas.py +360 -0
- emdash_core-0.1.16/emdash_core/context/registry.py +73 -0
- emdash_core-0.1.16/emdash_core/context/reranker.py +199 -0
- emdash_core-0.1.16/emdash_core/context/service.py +260 -0
- emdash_core-0.1.16/emdash_core/context/session.py +352 -0
- emdash_core-0.1.16/emdash_core/core/__init__.py +104 -0
- emdash_core-0.1.16/emdash_core/core/config.py +470 -0
- emdash_core-0.1.16/emdash_core/core/exceptions.py +55 -0
- emdash_core-0.1.16/emdash_core/core/models.py +265 -0
- emdash_core-0.1.16/emdash_core/core/review_config.py +57 -0
- emdash_core-0.1.16/emdash_core/db/__init__.py +67 -0
- emdash_core-0.1.16/emdash_core/db/auth.py +134 -0
- emdash_core-0.1.16/emdash_core/db/models.py +91 -0
- emdash_core-0.1.16/emdash_core/db/provider.py +222 -0
- emdash_core-0.1.16/emdash_core/db/providers/__init__.py +5 -0
- emdash_core-0.1.16/emdash_core/db/providers/supabase.py +452 -0
- emdash_core-0.1.16/emdash_core/embeddings/__init__.py +24 -0
- emdash_core-0.1.16/emdash_core/embeddings/indexer.py +534 -0
- emdash_core-0.1.16/emdash_core/embeddings/models.py +192 -0
- emdash_core-0.1.16/emdash_core/embeddings/providers/__init__.py +7 -0
- emdash_core-0.1.16/emdash_core/embeddings/providers/base.py +112 -0
- emdash_core-0.1.16/emdash_core/embeddings/providers/fireworks.py +141 -0
- emdash_core-0.1.16/emdash_core/embeddings/providers/openai.py +104 -0
- emdash_core-0.1.16/emdash_core/embeddings/registry.py +146 -0
- emdash_core-0.1.16/emdash_core/embeddings/service.py +215 -0
- emdash_core-0.1.16/emdash_core/graph/__init__.py +26 -0
- emdash_core-0.1.16/emdash_core/graph/builder.py +134 -0
- emdash_core-0.1.16/emdash_core/graph/connection.py +692 -0
- emdash_core-0.1.16/emdash_core/graph/schema.py +416 -0
- emdash_core-0.1.16/emdash_core/graph/writer.py +667 -0
- emdash_core-0.1.16/emdash_core/ingestion/__init__.py +7 -0
- emdash_core-0.1.16/emdash_core/ingestion/change_detector.py +150 -0
- emdash_core-0.1.16/emdash_core/ingestion/git/__init__.py +5 -0
- emdash_core-0.1.16/emdash_core/ingestion/git/commit_analyzer.py +196 -0
- emdash_core-0.1.16/emdash_core/ingestion/github/__init__.py +6 -0
- emdash_core-0.1.16/emdash_core/ingestion/github/pr_fetcher.py +296 -0
- emdash_core-0.1.16/emdash_core/ingestion/github/task_extractor.py +100 -0
- emdash_core-0.1.16/emdash_core/ingestion/orchestrator.py +549 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/__init__.py +10 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/base_parser.py +66 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/call_graph_builder.py +121 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/class_extractor.py +154 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/function_extractor.py +202 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/import_analyzer.py +119 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/python_parser.py +123 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/registry.py +72 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/ts_ast_parser.js +313 -0
- emdash_core-0.1.16/emdash_core/ingestion/parsers/typescript_parser.py +278 -0
- emdash_core-0.1.16/emdash_core/ingestion/repository.py +346 -0
- emdash_core-0.1.16/emdash_core/models/__init__.py +38 -0
- emdash_core-0.1.16/emdash_core/models/agent.py +68 -0
- emdash_core-0.1.16/emdash_core/models/index.py +77 -0
- emdash_core-0.1.16/emdash_core/models/query.py +113 -0
- emdash_core-0.1.16/emdash_core/planning/__init__.py +7 -0
- emdash_core-0.1.16/emdash_core/planning/agent_api.py +413 -0
- emdash_core-0.1.16/emdash_core/planning/context_builder.py +265 -0
- emdash_core-0.1.16/emdash_core/planning/feature_context.py +232 -0
- emdash_core-0.1.16/emdash_core/planning/feature_expander.py +646 -0
- emdash_core-0.1.16/emdash_core/planning/llm_explainer.py +198 -0
- emdash_core-0.1.16/emdash_core/planning/similarity.py +509 -0
- emdash_core-0.1.16/emdash_core/planning/team_focus.py +821 -0
- emdash_core-0.1.16/emdash_core/server.py +195 -0
- emdash_core-0.1.16/emdash_core/sse/__init__.py +5 -0
- emdash_core-0.1.16/emdash_core/sse/stream.py +196 -0
- emdash_core-0.1.16/emdash_core/swarm/__init__.py +17 -0
- emdash_core-0.1.16/emdash_core/swarm/merge_agent.py +383 -0
- emdash_core-0.1.16/emdash_core/swarm/session_manager.py +274 -0
- emdash_core-0.1.16/emdash_core/swarm/swarm_runner.py +226 -0
- emdash_core-0.1.16/emdash_core/swarm/task_definition.py +137 -0
- emdash_core-0.1.16/emdash_core/swarm/worker_spawner.py +319 -0
- emdash_core-0.1.16/emdash_core/swarm/worktree_manager.py +278 -0
- emdash_core-0.1.16/emdash_core/templates/__init__.py +10 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/agent-builder.md.template +82 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/focus.md.template +115 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/pr-review-enhanced.md.template +309 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/pr-review.md.template +80 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/project.md.template +85 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/research_critic.md.template +112 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/research_planner.md.template +85 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/research_synthesizer.md.template +128 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/reviewer.md.template +81 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/spec.md.template +41 -0
- emdash_core-0.1.16/emdash_core/templates/defaults/tasks.md.template +78 -0
- emdash_core-0.1.16/emdash_core/templates/loader.py +296 -0
- emdash_core-0.1.16/emdash_core/utils/__init__.py +45 -0
- emdash_core-0.1.16/emdash_core/utils/git.py +84 -0
- emdash_core-0.1.16/emdash_core/utils/image.py +502 -0
- emdash_core-0.1.16/emdash_core/utils/logger.py +51 -0
- emdash_core-0.1.16/pyproject.toml +64 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: emdash-core
|
|
3
|
+
Version: 0.1.16
|
|
4
|
+
Summary: EmDash Core - FastAPI server for code intelligence
|
|
5
|
+
Author: Em Dash Team
|
|
6
|
+
Requires-Python: >=3.10,<4.0
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: astroid (>=3.0.1,<4.0.0)
|
|
14
|
+
Requires-Dist: beautifulsoup4 (>=4.12.0)
|
|
15
|
+
Requires-Dist: duckduckgo-search (>=6.0.0)
|
|
16
|
+
Requires-Dist: fastapi (>=0.109.0)
|
|
17
|
+
Requires-Dist: gitpython (>=3.1.40,<4.0.0)
|
|
18
|
+
Requires-Dist: httpx (>=0.25.0)
|
|
19
|
+
Requires-Dist: kuzu (>=0.4.0)
|
|
20
|
+
Requires-Dist: loguru (>=0.7.2,<0.8.0)
|
|
21
|
+
Requires-Dist: networkx (>=3.2.1,<4.0.0)
|
|
22
|
+
Requires-Dist: numpy (>=1.26.0)
|
|
23
|
+
Requires-Dist: openai (>=1.0.0)
|
|
24
|
+
Requires-Dist: pillow (>=10.0.0,<11.0.0)
|
|
25
|
+
Requires-Dist: pydantic (>=2.5.0,<3.0.0)
|
|
26
|
+
Requires-Dist: pydantic-settings (>=2.0.0,<3.0.0)
|
|
27
|
+
Requires-Dist: pygithub (>=2.1.1,<3.0.0)
|
|
28
|
+
Requires-Dist: python-dotenv (>=1.0.0,<2.0.0)
|
|
29
|
+
Requires-Dist: python-louvain (>=0.16,<0.17)
|
|
30
|
+
Requires-Dist: scipy (>=1.11.4,<2.0.0)
|
|
31
|
+
Requires-Dist: sentence-transformers (>=2.2.0)
|
|
32
|
+
Requires-Dist: sse-starlette (>=2.0.0)
|
|
33
|
+
Requires-Dist: supabase (>=2.0.0)
|
|
34
|
+
Requires-Dist: tqdm (>=4.66.1,<5.0.0)
|
|
35
|
+
Requires-Dist: uvicorn[standard] (>=0.27.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())
|