emdash-core 0.1.37__py3-none-any.whl → 0.1.60__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.
- emdash_core/agent/agents.py +9 -0
- emdash_core/agent/background.py +481 -0
- emdash_core/agent/inprocess_subagent.py +70 -1
- emdash_core/agent/mcp/config.py +78 -2
- emdash_core/agent/prompts/main_agent.py +53 -1
- emdash_core/agent/prompts/plan_mode.py +65 -44
- emdash_core/agent/prompts/subagents.py +73 -1
- emdash_core/agent/prompts/workflow.py +179 -28
- emdash_core/agent/providers/models.py +1 -1
- emdash_core/agent/providers/openai_provider.py +10 -0
- emdash_core/agent/research/researcher.py +154 -45
- emdash_core/agent/runner/agent_runner.py +145 -19
- emdash_core/agent/runner/sdk_runner.py +29 -2
- emdash_core/agent/skills.py +81 -1
- emdash_core/agent/toolkit.py +87 -11
- emdash_core/agent/tools/__init__.py +2 -0
- emdash_core/agent/tools/coding.py +344 -52
- emdash_core/agent/tools/lsp.py +361 -0
- emdash_core/agent/tools/skill.py +21 -1
- emdash_core/agent/tools/task.py +16 -19
- emdash_core/agent/tools/task_output.py +262 -32
- emdash_core/agent/verifier/__init__.py +11 -0
- emdash_core/agent/verifier/manager.py +295 -0
- emdash_core/agent/verifier/models.py +97 -0
- emdash_core/{swarm/worktree_manager.py → agent/worktree.py} +19 -1
- emdash_core/api/agent.py +297 -2
- emdash_core/api/research.py +3 -3
- emdash_core/api/router.py +0 -4
- emdash_core/context/longevity.py +197 -0
- emdash_core/context/providers/explored_areas.py +83 -39
- emdash_core/context/reranker.py +35 -144
- emdash_core/context/simple_reranker.py +500 -0
- emdash_core/context/tool_relevance.py +84 -0
- emdash_core/core/config.py +8 -0
- emdash_core/graph/__init__.py +8 -1
- emdash_core/graph/connection.py +24 -3
- emdash_core/graph/writer.py +7 -1
- emdash_core/models/agent.py +10 -0
- emdash_core/server.py +1 -6
- emdash_core/sse/stream.py +16 -1
- emdash_core/utils/__init__.py +0 -2
- emdash_core/utils/git.py +103 -0
- emdash_core/utils/image.py +147 -160
- {emdash_core-0.1.37.dist-info → emdash_core-0.1.60.dist-info}/METADATA +6 -6
- {emdash_core-0.1.37.dist-info → emdash_core-0.1.60.dist-info}/RECORD +47 -52
- emdash_core/api/swarm.py +0 -223
- emdash_core/db/__init__.py +0 -67
- emdash_core/db/auth.py +0 -134
- emdash_core/db/models.py +0 -91
- emdash_core/db/provider.py +0 -222
- emdash_core/db/providers/__init__.py +0 -5
- emdash_core/db/providers/supabase.py +0 -452
- emdash_core/swarm/__init__.py +0 -17
- emdash_core/swarm/merge_agent.py +0 -383
- emdash_core/swarm/session_manager.py +0 -274
- emdash_core/swarm/swarm_runner.py +0 -226
- emdash_core/swarm/task_definition.py +0 -137
- emdash_core/swarm/worker_spawner.py +0 -319
- {emdash_core-0.1.37.dist-info → emdash_core-0.1.60.dist-info}/WHEEL +0 -0
- {emdash_core-0.1.37.dist-info → emdash_core-0.1.60.dist-info}/entry_points.txt +0 -0
emdash_core/agent/mcp/config.py
CHANGED
|
@@ -65,6 +65,14 @@ class MCPServerConfig:
|
|
|
65
65
|
# Default to .emdash/index/kuzu_db in cwd
|
|
66
66
|
default_path = Path.cwd() / ".emdash" / "index" / "kuzu_db"
|
|
67
67
|
return str(default_path)
|
|
68
|
+
# Check for cclsp config path - use emdash config default
|
|
69
|
+
if var_name == "CCLSP_CONFIG_PATH":
|
|
70
|
+
env_val = os.getenv(var_name)
|
|
71
|
+
if env_val:
|
|
72
|
+
return env_val
|
|
73
|
+
# Default to .emdash/cclsp.json in cwd
|
|
74
|
+
default_path = Path.cwd() / ".emdash" / "cclsp.json"
|
|
75
|
+
return str(default_path)
|
|
68
76
|
# Fall back to environment variable
|
|
69
77
|
return os.getenv(var_name, "")
|
|
70
78
|
|
|
@@ -244,6 +252,8 @@ def get_default_mcp_servers() -> dict[str, MCPServerConfig]:
|
|
|
244
252
|
"""
|
|
245
253
|
# Check if graph MCP is enabled via env flag
|
|
246
254
|
enable_graph_mcp = os.getenv("ENABLE_GRAPH_MCP", "false").lower() == "true"
|
|
255
|
+
# Check if LSP is enabled via env flag (disabled by default)
|
|
256
|
+
enable_cclsp = os.getenv("USE_LSP", "false").lower() == "true"
|
|
247
257
|
|
|
248
258
|
return {
|
|
249
259
|
"github": MCPServerConfig(
|
|
@@ -266,6 +276,16 @@ def get_default_mcp_servers() -> dict[str, MCPServerConfig]:
|
|
|
266
276
|
enabled=enable_graph_mcp,
|
|
267
277
|
timeout=30,
|
|
268
278
|
),
|
|
279
|
+
"cclsp": MCPServerConfig(
|
|
280
|
+
name="cclsp",
|
|
281
|
+
command="npx",
|
|
282
|
+
args=["cclsp@latest"],
|
|
283
|
+
env={
|
|
284
|
+
"CCLSP_CONFIG_PATH": "${CCLSP_CONFIG_PATH}",
|
|
285
|
+
},
|
|
286
|
+
enabled=enable_cclsp, # Disabled by default, enable with USE_LSP=true
|
|
287
|
+
timeout=60, # LSP operations can take longer
|
|
288
|
+
),
|
|
269
289
|
}
|
|
270
290
|
|
|
271
291
|
|
|
@@ -297,6 +317,62 @@ def ensure_mcp_config(path: Path) -> MCPConfigFile:
|
|
|
297
317
|
MCPConfigFile (loaded or newly created)
|
|
298
318
|
"""
|
|
299
319
|
if path.exists():
|
|
300
|
-
|
|
320
|
+
config = MCPConfigFile.load(path)
|
|
301
321
|
else:
|
|
302
|
-
|
|
322
|
+
config = create_default_mcp_config(path)
|
|
323
|
+
|
|
324
|
+
# Ensure cclsp.json config exists if cclsp is enabled
|
|
325
|
+
if config.servers.get("cclsp") and config.servers["cclsp"].enabled:
|
|
326
|
+
cclsp_config_path = path.parent / "cclsp.json"
|
|
327
|
+
ensure_cclsp_config(cclsp_config_path)
|
|
328
|
+
|
|
329
|
+
return config
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def get_default_cclsp_config() -> dict:
|
|
333
|
+
"""Get the default cclsp configuration for TypeScript/React projects.
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
Dictionary with cclsp configuration
|
|
337
|
+
"""
|
|
338
|
+
return {
|
|
339
|
+
"servers": [
|
|
340
|
+
{
|
|
341
|
+
"extensions": ["ts", "tsx", "js", "jsx"],
|
|
342
|
+
"command": ["npx", "typescript-language-server", "--stdio"],
|
|
343
|
+
"rootDir": ".",
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"extensions": ["py", "pyi"],
|
|
347
|
+
"command": ["pylsp"],
|
|
348
|
+
"rootDir": ".",
|
|
349
|
+
},
|
|
350
|
+
]
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def create_cclsp_config(path: Path) -> None:
|
|
355
|
+
"""Create a default cclsp configuration file.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
path: Path to save the cclsp.json file
|
|
359
|
+
"""
|
|
360
|
+
config = get_default_cclsp_config()
|
|
361
|
+
|
|
362
|
+
# Ensure directory exists
|
|
363
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
364
|
+
|
|
365
|
+
with open(path, "w") as f:
|
|
366
|
+
json.dump(config, f, indent=2)
|
|
367
|
+
|
|
368
|
+
log.info(f"Created default cclsp config at {path}")
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def ensure_cclsp_config(path: Path) -> None:
|
|
372
|
+
"""Ensure cclsp config exists, creating default if needed.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
path: Path to the cclsp.json file
|
|
376
|
+
"""
|
|
377
|
+
if not path.exists():
|
|
378
|
+
create_cclsp_config(path)
|
|
@@ -10,6 +10,8 @@ from .workflow import (
|
|
|
10
10
|
OUTPUT_GUIDELINES,
|
|
11
11
|
PARALLEL_EXECUTION,
|
|
12
12
|
TODO_LIST_GUIDANCE,
|
|
13
|
+
VERIFICATION_AND_CRITIQUE,
|
|
14
|
+
ACTION_NOT_ANNOUNCEMENT,
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
# Base system prompt template with placeholder for tools
|
|
@@ -20,7 +22,7 @@ _BASE_PROMPT = """You are a code exploration and implementation assistant. You o
|
|
|
20
22
|
|
|
21
23
|
# Main agent system prompt - same for both code and plan modes
|
|
22
24
|
# Main agent is always an orchestrator that delegates to subagents
|
|
23
|
-
BASE_SYSTEM_PROMPT = _BASE_PROMPT + WORKFLOW_PATTERNS + PARALLEL_EXECUTION + EXPLORATION_STRATEGY + TODO_LIST_GUIDANCE + OUTPUT_GUIDELINES
|
|
25
|
+
BASE_SYSTEM_PROMPT = _BASE_PROMPT + WORKFLOW_PATTERNS + PARALLEL_EXECUTION + EXPLORATION_STRATEGY + TODO_LIST_GUIDANCE + ACTION_NOT_ANNOUNCEMENT + VERIFICATION_AND_CRITIQUE + OUTPUT_GUIDELINES
|
|
24
26
|
|
|
25
27
|
# Legacy aliases
|
|
26
28
|
CODE_MODE_PROMPT = BASE_SYSTEM_PROMPT
|
|
@@ -40,10 +42,15 @@ def build_system_prompt(toolkit) -> str:
|
|
|
40
42
|
agents_section = build_agents_section(toolkit)
|
|
41
43
|
skills_section = build_skills_section()
|
|
42
44
|
rules_section = build_rules_section()
|
|
45
|
+
session_section = build_session_context_section(toolkit)
|
|
43
46
|
|
|
44
47
|
# Main agent always uses the same prompt - it orchestrates and delegates
|
|
45
48
|
prompt = BASE_SYSTEM_PROMPT.format(tools_section=tools_section)
|
|
46
49
|
|
|
50
|
+
# Add session context section first (repo, branch, status)
|
|
51
|
+
if session_section:
|
|
52
|
+
prompt += "\n" + session_section
|
|
53
|
+
|
|
47
54
|
# Add agents section so main agent knows what agents are available
|
|
48
55
|
if agents_section:
|
|
49
56
|
prompt += "\n" + agents_section
|
|
@@ -59,6 +66,51 @@ def build_system_prompt(toolkit) -> str:
|
|
|
59
66
|
return prompt
|
|
60
67
|
|
|
61
68
|
|
|
69
|
+
def build_session_context_section(toolkit) -> str:
|
|
70
|
+
"""Build the session context section with repo, branch, and git status.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
toolkit: The agent toolkit (to access repo_root)
|
|
74
|
+
|
|
75
|
+
Returns:
|
|
76
|
+
Formatted string with session context, or empty string if not in a git repo
|
|
77
|
+
"""
|
|
78
|
+
from ...utils.git import (
|
|
79
|
+
get_repo_name,
|
|
80
|
+
get_current_branch,
|
|
81
|
+
get_git_status_summary,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
repo_root = getattr(toolkit, '_repo_root', None)
|
|
85
|
+
if not repo_root:
|
|
86
|
+
return ""
|
|
87
|
+
|
|
88
|
+
repo_name = get_repo_name(repo_root)
|
|
89
|
+
branch = get_current_branch(repo_root)
|
|
90
|
+
status = get_git_status_summary(repo_root)
|
|
91
|
+
|
|
92
|
+
# Only include if we have at least some git info
|
|
93
|
+
if not any([repo_name, branch, status]):
|
|
94
|
+
return ""
|
|
95
|
+
|
|
96
|
+
lines = [
|
|
97
|
+
"## Session Context",
|
|
98
|
+
"",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
if repo_name:
|
|
102
|
+
lines.append(f"- **Repository**: {repo_name}")
|
|
103
|
+
if branch:
|
|
104
|
+
lines.append(f"- **Branch**: {branch}")
|
|
105
|
+
if status:
|
|
106
|
+
lines.append(f"- **Git Status**: {status}")
|
|
107
|
+
|
|
108
|
+
lines.append(f"- **Working Directory**: {repo_root}")
|
|
109
|
+
lines.append("")
|
|
110
|
+
|
|
111
|
+
return "\n".join(lines)
|
|
112
|
+
|
|
113
|
+
|
|
62
114
|
def build_rules_section() -> str:
|
|
63
115
|
"""Build the rules section of the system prompt.
|
|
64
116
|
|
|
@@ -62,21 +62,45 @@ Even for new features, explore first to understand:
|
|
|
62
62
|
- Where similar features exist
|
|
63
63
|
- What conventions to follow
|
|
64
64
|
|
|
65
|
-
|
|
65
|
+
**Direct Tools (for targeted queries):**
|
|
66
66
|
- `glob` - Find files by pattern (e.g., `glob(pattern="**/*.py")`)
|
|
67
67
|
- `grep` - Search file contents (e.g., `grep(pattern="class User", path="src/")`)
|
|
68
68
|
- `read_file` - Read specific files
|
|
69
69
|
- `semantic_search` - Find conceptually related code
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
**Parallel Explore Agents (for complex tasks):**
|
|
72
|
+
For non-trivial tasks, launch up to 3 Explore agents IN PARALLEL to speed up exploration:
|
|
73
|
+
|
|
72
74
|
```python
|
|
75
|
+
# Launch multiple agents in a SINGLE response for parallel execution
|
|
73
76
|
task(
|
|
74
77
|
subagent_type="Explore",
|
|
75
78
|
prompt="Find all authentication-related files and patterns",
|
|
76
79
|
description="Explore auth patterns"
|
|
77
80
|
)
|
|
81
|
+
task(
|
|
82
|
+
subagent_type="Explore",
|
|
83
|
+
prompt="Find all API endpoint definitions and routing",
|
|
84
|
+
description="Explore API routes"
|
|
85
|
+
)
|
|
86
|
+
task(
|
|
87
|
+
subagent_type="Explore",
|
|
88
|
+
prompt="Find all database models and schemas",
|
|
89
|
+
description="Explore data models"
|
|
90
|
+
)
|
|
78
91
|
```
|
|
79
92
|
|
|
93
|
+
**When to use parallel agents:**
|
|
94
|
+
- Task touches multiple subsystems (auth, API, database, etc.)
|
|
95
|
+
- Codebase is large or unfamiliar
|
|
96
|
+
- Requirements span multiple domains
|
|
97
|
+
- You need to understand architectural patterns quickly
|
|
98
|
+
|
|
99
|
+
**When to use direct tools:**
|
|
100
|
+
- Simple, targeted queries ("find the User class")
|
|
101
|
+
- You already know where to look
|
|
102
|
+
- Quick file reads or pattern matches
|
|
103
|
+
|
|
80
104
|
### Phase 2: CLARIFY (Only After Exploring)
|
|
81
105
|
If requirements are still unclear AFTER exploration, ask focused questions.
|
|
82
106
|
|
|
@@ -94,6 +118,27 @@ Consider:
|
|
|
94
118
|
- Edge cases and error handling
|
|
95
119
|
- Verification/testing strategy
|
|
96
120
|
|
|
121
|
+
**For complex architectural decisions**, you can launch a Plan agent:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
task(
|
|
125
|
+
subagent_type="Plan",
|
|
126
|
+
prompt="Design the authentication system architecture considering: existing patterns found in auth/, session management in services/session.py, and the need for OAuth2 support",
|
|
127
|
+
description="Design auth architecture"
|
|
128
|
+
)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**When to use Plan agents:**
|
|
132
|
+
- Multiple valid architectural approaches exist
|
|
133
|
+
- Trade-offs need careful analysis
|
|
134
|
+
- Design impacts multiple subsystems
|
|
135
|
+
- You need expert-level design recommendations
|
|
136
|
+
|
|
137
|
+
**When to synthesize directly:**
|
|
138
|
+
- Implementation is straightforward
|
|
139
|
+
- Clear patterns exist to follow
|
|
140
|
+
- Single file or localized changes
|
|
141
|
+
|
|
97
142
|
### Phase 4: REVIEW
|
|
98
143
|
Before finalizing, verify the plan is complete and actionable.
|
|
99
144
|
|
|
@@ -124,52 +169,27 @@ exit_plan()
|
|
|
124
169
|
|
|
125
170
|
## PLAN FILE FORMAT
|
|
126
171
|
|
|
127
|
-
Your plan must be written to `{plan_file_path}
|
|
172
|
+
Your plan must be written to `{plan_file_path}`. Use **compact formatting** - no blank lines between headers and content:
|
|
128
173
|
|
|
129
174
|
```markdown
|
|
130
|
-
#
|
|
131
|
-
|
|
175
|
+
# <Title>
|
|
132
176
|
## Summary
|
|
133
|
-
<1-2 sentence overview
|
|
134
|
-
|
|
177
|
+
<1-2 sentence overview>
|
|
135
178
|
## Approach
|
|
136
|
-
<High-level strategy -
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
-
|
|
140
|
-
|
|
141
|
-
- Regression prevention
|
|
142
|
-
|
|
143
|
-
### For New Features:
|
|
144
|
-
- Architecture decisions
|
|
145
|
-
- Component breakdown
|
|
146
|
-
- Integration points
|
|
147
|
-
|
|
148
|
-
### For Refactors:
|
|
149
|
-
- Current state problems
|
|
150
|
-
- Target state benefits
|
|
151
|
-
- Migration strategy
|
|
152
|
-
|
|
153
|
-
## Implementation Steps
|
|
179
|
+
<High-level strategy - WHAT changes, not HOW>
|
|
180
|
+
- For bugs: root cause, fix location, regression prevention
|
|
181
|
+
- For features: architecture, components, integration points
|
|
182
|
+
- For refactors: current problems, target benefits, migration
|
|
183
|
+
## Steps
|
|
154
184
|
1. <Step 1 - what to do, which file>
|
|
155
185
|
2. <Step 2 - what to do, which file>
|
|
156
|
-
...
|
|
157
|
-
|
|
158
186
|
## Critical Files
|
|
159
|
-
List the 3-5 most important files with brief justification:
|
|
160
|
-
|
|
161
187
|
| File | Purpose |
|
|
162
188
|
|------|---------|
|
|
163
|
-
| `path/to/
|
|
164
|
-
| `path/to/file2.py` | <why this file is critical> |
|
|
165
|
-
...
|
|
166
|
-
|
|
189
|
+
| `path/to/file.py` | <why critical> |
|
|
167
190
|
## Verification
|
|
168
|
-
- [ ] <
|
|
169
|
-
|
|
170
|
-
...
|
|
171
|
-
|
|
172
|
-
## Risks & Considerations
|
|
191
|
+
- [ ] <Test or check to verify>
|
|
192
|
+
## Risks
|
|
173
193
|
- <Potential issue and mitigation>
|
|
174
194
|
```
|
|
175
195
|
|
|
@@ -179,8 +199,8 @@ List the 3-5 most important files with brief justification:
|
|
|
179
199
|
|
|
180
200
|
```
|
|
181
201
|
┌─────────────────┐
|
|
182
|
-
│ 1. EXPLORE │ ◄───
|
|
183
|
-
│
|
|
202
|
+
│ 1. EXPLORE │ ◄─── Direct tools OR up to 3 Explore agents in parallel
|
|
203
|
+
│ (tools/agents) │
|
|
184
204
|
└────────┬────────┘
|
|
185
205
|
│ Codebase understood
|
|
186
206
|
▼
|
|
@@ -191,8 +211,8 @@ List the 3-5 most important files with brief justification:
|
|
|
191
211
|
│ Requirements clear
|
|
192
212
|
▼
|
|
193
213
|
┌─────────────────┐
|
|
194
|
-
│ 3. DESIGN │ ◄───
|
|
195
|
-
│
|
|
214
|
+
│ 3. DESIGN │ ◄─── Direct synthesis OR Plan agent for complex decisions
|
|
215
|
+
│(analysis/agent) │
|
|
196
216
|
└────────┬────────┘
|
|
197
217
|
│ Design complete
|
|
198
218
|
▼
|
|
@@ -247,8 +267,9 @@ Only call `exit_plan` when ALL of these are true:
|
|
|
247
267
|
## CORRECT BEHAVIOR
|
|
248
268
|
|
|
249
269
|
- Use `ask_followup_question` for clarifying requirements
|
|
250
|
-
- Use your tools directly (glob, grep, read_file) for exploration
|
|
251
|
-
- Use `task(subagent_type="Explore", ...)`
|
|
270
|
+
- Use your tools directly (glob, grep, read_file) for simple, targeted exploration
|
|
271
|
+
- Use `task(subagent_type="Explore", ...)` for complex tasks - launch up to 3 in parallel
|
|
272
|
+
- Use `task(subagent_type="Plan", ...)` for complex architectural decisions
|
|
252
273
|
- Write plan to `{plan_file_path}` before exiting
|
|
253
274
|
- Use `exit_plan` to request plan approval
|
|
254
275
|
- Focus on WHAT to change, not HOW (no code snippets)
|
|
@@ -198,6 +198,8 @@ def get_subagent_prompt(subagent_type: str, repo_root=None) -> str:
|
|
|
198
198
|
Raises:
|
|
199
199
|
ValueError: If agent type is not known
|
|
200
200
|
"""
|
|
201
|
+
from pathlib import Path
|
|
202
|
+
|
|
201
203
|
# Check built-in agents first
|
|
202
204
|
if subagent_type in SUBAGENT_PROMPTS:
|
|
203
205
|
return SUBAGENT_PROMPTS[subagent_type]
|
|
@@ -206,10 +208,80 @@ def get_subagent_prompt(subagent_type: str, repo_root=None) -> str:
|
|
|
206
208
|
from ..toolkits import get_custom_agent
|
|
207
209
|
custom_agent = get_custom_agent(subagent_type, repo_root)
|
|
208
210
|
if custom_agent:
|
|
209
|
-
|
|
211
|
+
prompt_parts = [custom_agent.system_prompt]
|
|
212
|
+
|
|
213
|
+
# Inject rules if specified
|
|
214
|
+
if custom_agent.rules:
|
|
215
|
+
rules_content = _load_rules_by_names(custom_agent.rules, repo_root)
|
|
216
|
+
if rules_content:
|
|
217
|
+
prompt_parts.append(f"\n\n## Rules\n\n{rules_content}")
|
|
218
|
+
|
|
219
|
+
# Inject skills if specified
|
|
220
|
+
if custom_agent.skills:
|
|
221
|
+
skills_content = _load_skills_by_names(custom_agent.skills, repo_root)
|
|
222
|
+
if skills_content:
|
|
223
|
+
prompt_parts.append(f"\n\n## Skills\n\n{skills_content}")
|
|
224
|
+
|
|
225
|
+
return "".join(prompt_parts)
|
|
210
226
|
|
|
211
227
|
# Not found
|
|
212
228
|
available = list(SUBAGENT_PROMPTS.keys())
|
|
213
229
|
raise ValueError(
|
|
214
230
|
f"Unknown agent type: {subagent_type}. Available: {available}"
|
|
215
231
|
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
def _load_rules_by_names(rule_names: list[str], repo_root=None) -> str:
|
|
235
|
+
"""Load specific rules by name.
|
|
236
|
+
|
|
237
|
+
Args:
|
|
238
|
+
rule_names: List of rule names to load
|
|
239
|
+
repo_root: Repository root
|
|
240
|
+
|
|
241
|
+
Returns:
|
|
242
|
+
Combined rules content
|
|
243
|
+
"""
|
|
244
|
+
from pathlib import Path
|
|
245
|
+
|
|
246
|
+
rules_dir = (repo_root or Path.cwd()) / ".emdash" / "rules"
|
|
247
|
+
if not rules_dir.exists():
|
|
248
|
+
return ""
|
|
249
|
+
|
|
250
|
+
parts = []
|
|
251
|
+
for name in rule_names:
|
|
252
|
+
rule_file = rules_dir / f"{name}.md"
|
|
253
|
+
if rule_file.exists():
|
|
254
|
+
try:
|
|
255
|
+
content = rule_file.read_text().strip()
|
|
256
|
+
if content:
|
|
257
|
+
parts.append(content)
|
|
258
|
+
except Exception:
|
|
259
|
+
pass
|
|
260
|
+
|
|
261
|
+
return "\n\n---\n\n".join(parts)
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
def _load_skills_by_names(skill_names: list[str], repo_root=None) -> str:
|
|
265
|
+
"""Load specific skills by name and return their instructions.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
skill_names: List of skill names to load
|
|
269
|
+
repo_root: Repository root
|
|
270
|
+
|
|
271
|
+
Returns:
|
|
272
|
+
Combined skills instructions
|
|
273
|
+
"""
|
|
274
|
+
from pathlib import Path
|
|
275
|
+
from ..skills import SkillRegistry
|
|
276
|
+
|
|
277
|
+
skills_dir = (repo_root or Path.cwd()) / ".emdash" / "skills"
|
|
278
|
+
registry = SkillRegistry.get_instance()
|
|
279
|
+
registry.load_skills(skills_dir)
|
|
280
|
+
|
|
281
|
+
parts = []
|
|
282
|
+
for name in skill_names:
|
|
283
|
+
skill = registry.get_skill(name)
|
|
284
|
+
if skill and skill.instructions:
|
|
285
|
+
parts.append(f"### {skill.name}\n\n{skill.instructions}")
|
|
286
|
+
|
|
287
|
+
return "\n\n".join(parts)
|