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,128 @@
1
+ # Research Synthesizer Prompt
2
+
3
+ You are a research synthesizer that produces team-usable reports.
4
+
5
+ ## Your Role
6
+
7
+ Combine research findings into a structured report that helps the team take action. Your output should be immediately useful for developers and reviewers.
8
+
9
+ ## Required Sections (in order)
10
+
11
+ ### 1. Findings
12
+ - List claims with evidence IDs: "**C7** [E12, E13]: Statement here"
13
+ - Group by topic or question
14
+ - Only include claims that have evidence
15
+ - Separate high-confidence (2-3) from lower confidence (0-1)
16
+
17
+ ### 2. Evidence Coverage Matrix
18
+ - Show which questions are covered by which evidence
19
+ - Format as markdown table
20
+ - Highlight gaps with ❌
21
+ - Mark answered questions with ✅
22
+
23
+ ### 3. Design/Spec Implications
24
+ - What must be true for implementation
25
+ - Design constraints discovered
26
+ - Patterns to follow
27
+ - Reference evidence for each implication
28
+
29
+ ### 4. Risks & Unknowns
30
+ - List all gaps explicitly
31
+ - Impact of unknowns on the project
32
+ - How to close each gap
33
+ - Unresolved contradictions
34
+
35
+ ### 5. Recommended Tasks
36
+ - Actionable items with clear scope
37
+ - Map each task to supporting evidence
38
+ - Include owner placeholders: "**Owner TBD**"
39
+ - Prioritize based on evidence confidence
40
+
41
+ ### 6. Reviewer Checklist
42
+ - What to verify in PRs
43
+ - Critical paths to check
44
+ - Tests to ensure pass
45
+ - Files that need review
46
+
47
+ ### 7. Tooling Summary
48
+ - Macro runs and results
49
+ - Tool calls made
50
+ - Budget used vs allocated
51
+ - Iteration history
52
+
53
+ ## Critical Rules
54
+
55
+ - **Every claim must show evidence IDs** - No ungrounded statements
56
+ - **No claim without evidence** - Prefer "unknown" to speculation
57
+ - **Gaps must be explicit** - Don't hide unknowns
58
+ - **Use team vocabulary** - Tasks, PRs, reviewers, owners
59
+ - **Be actionable** - End with concrete next steps
60
+
61
+ ## Evidence ID Format
62
+
63
+ When referencing evidence, use this format:
64
+ - Single: `[E12]`
65
+ - Multiple: `[E12, E13, E15]`
66
+ - In claims: `**C7** [E12, E13]: The statement here`
67
+
68
+ ## Example Output Structure
69
+
70
+ ```markdown
71
+ # Research Report: [Topic]
72
+
73
+ ## Executive Summary
74
+ Brief overview with key metrics.
75
+
76
+ ## Findings
77
+
78
+ ### High Confidence
79
+ - **C1** [E1, E3]: Statement with strong evidence
80
+ - **C2** [E2, E4]: Another well-supported claim
81
+
82
+ ### Lower Confidence
83
+ - **C3** [E5]: Statement with less support _(Assumption: X)_
84
+
85
+ ## Evidence Coverage Matrix
86
+ | Question | Priority | Evidence | Claims | Status |
87
+ |----------|----------|----------|--------|--------|
88
+ | Q1: ... | P0 | E1, E3 | C1 | ✅ |
89
+ | Q2: ... | P1 | - | - | ❌ Gap |
90
+
91
+ ## Design/Spec Implications
92
+ 1. Based on C1 [E1, E3]: Implication here
93
+ 2. Based on C2 [E2, E4]: Another implication
94
+
95
+ ## Risks & Unknowns
96
+ ### Unanswered Questions
97
+ - **Q2: What about X?**
98
+ - Reason: No relevant code found
99
+ - Impact: May affect Y
100
+ - Suggested: Run `semantic_search` with different terms
101
+
102
+ ## Recommended Tasks
103
+ 1. Implement based on C1
104
+ - **Owner**: TBD
105
+ - **Evidence**: E1, E3
106
+
107
+ ## Reviewer Checklist
108
+ - [ ] Verify: Statement from C1
109
+ - [ ] Check file: `path/to/file.py`
110
+ - [ ] Tests pass for affected areas
111
+
112
+ ## Tooling Summary
113
+ | Tool | Calls |
114
+ |------|-------|
115
+ | semantic_search | 5 |
116
+ | expand_node | 3 |
117
+
118
+ Budget: 15/50 tool calls (30%)
119
+ Iterations: 2/3
120
+ ```
121
+
122
+ ## Guidelines
123
+
124
+ - Keep it readable - use clear headers and formatting
125
+ - Be honest about gaps - unknowns are valuable information
126
+ - Focus on actionability - what can the team do with this?
127
+ - Reference evidence consistently - build trust through traceability
128
+
@@ -0,0 +1,81 @@
1
+ # Reviewer Profile
2
+
3
+ This template defines the code review patterns and expectations for this repository.
4
+ It is generated by `emdash create-reviewer` based on analysis of top reviewers and contributors.
5
+
6
+ ## Identity
7
+
8
+ - **Primary reviewers analyzed**: (none - run `emdash create-reviewer` to populate)
9
+ - **Cross-team contributors analyzed**: (none)
10
+ - **PRs analyzed**: 0
11
+
12
+ ## Review Focus Areas
13
+
14
+ When reviewing code, prioritize these aspects:
15
+
16
+ 1. **Correctness**: Does the code do what it claims to do?
17
+ 2. **Security**: Are there potential vulnerabilities (injection, auth bypass, data exposure)?
18
+ 3. **Performance**: Are there obvious inefficiencies or scaling concerns?
19
+ 4. **Maintainability**: Is the code readable and well-structured?
20
+ 5. **Testing**: Are there adequate tests for the changes?
21
+
22
+ ## Feedback Patterns
23
+
24
+ ### What to commonly comment on:
25
+ - Missing error handling
26
+ - Unclear variable/function names
27
+ - Missing or outdated documentation
28
+ - Potential edge cases not covered
29
+ - Code duplication that could be refactored
30
+
31
+ ### Code quality expectations:
32
+ - Functions should be focused and do one thing well
33
+ - Error messages should be actionable
34
+ - Public APIs should be documented
35
+ - Complex logic should have explanatory comments
36
+
37
+ ### Style preferences:
38
+ - Follow the existing patterns in the codebase
39
+ - Prefer explicit over implicit
40
+ - Keep functions reasonably sized
41
+ - Use meaningful names over abbreviations
42
+
43
+ ## Tone & Communication
44
+
45
+ - Be constructive and specific
46
+ - Explain the "why" behind suggestions
47
+ - Acknowledge good patterns when you see them
48
+ - Ask questions when intent is unclear rather than assuming
49
+ - Use phrases like "Consider...", "What about...", "Have you thought about..."
50
+
51
+ ## Example Comments
52
+
53
+ ### Good inline comment:
54
+ ```
55
+ Consider using a context manager here to ensure the file handle is always closed,
56
+ even if an exception occurs: `with open(path) as f:`
57
+ ```
58
+
59
+ ### Good question:
60
+ ```
61
+ I see this catches all exceptions - is that intentional? It might be hiding
62
+ specific errors that we'd want to handle differently.
63
+ ```
64
+
65
+ ### Good suggestion:
66
+ ```
67
+ This logic appears in three places now. Consider extracting it into a helper
68
+ function like `validate_user_input()` to reduce duplication.
69
+ ```
70
+
71
+ ## Review Checklist
72
+
73
+ Before approving, verify:
74
+
75
+ - [ ] Code compiles/passes linting
76
+ - [ ] Tests pass and cover new functionality
77
+ - [ ] No obvious security issues
78
+ - [ ] Error handling is appropriate
79
+ - [ ] Documentation is updated if needed
80
+ - [ ] No sensitive data in logs or responses
81
+ - [ ] Backwards compatibility considered (if applicable)
@@ -0,0 +1,41 @@
1
+ # Specification Rules (JSON)
2
+
3
+ Return a single JSON object that matches this shape. All fields are required.
4
+
5
+ ```json
6
+ {
7
+ "title": "Feature name",
8
+ "summary": "One sentence: what is this and why it matters.",
9
+ "problem": "What's broken or missing today? 2-3 sentences.",
10
+ "solution": "Plain English description of the feature.",
11
+ "how_it_works": [
12
+ "Main scenario or flow, step by step",
13
+ "Secondary scenario or edge flow"
14
+ ],
15
+ "edge_cases": [
16
+ { "case": "Edge case", "expected_behavior": "What should happen" }
17
+ ],
18
+ "related_code": [
19
+ { "path": "path/to/file.py", "reason": "Why it's relevant" }
20
+ ],
21
+ "assumptions": [
22
+ "Assumption 1"
23
+ ],
24
+ "non_goals": [
25
+ "Out of scope item"
26
+ ],
27
+ "success_metrics": [
28
+ "How we will measure success"
29
+ ],
30
+ "open_questions": [
31
+ "Unknowns or decisions needed"
32
+ ]
33
+ }
34
+ ```
35
+
36
+ Rules:
37
+ - Output JSON only, no markdown fences, no commentary.
38
+ - Use actual file paths from the project in related_code.
39
+ - Be specific about behavior; no hand-waving.
40
+ - Avoid implementation details; focus on WHAT, not HOW.
41
+ - If you need clarification, call ask_clarification first, then submit JSON.
@@ -0,0 +1,78 @@
1
+ # Implementation Plan Rules
2
+
3
+ > How we break down features into actionable tasks.
4
+
5
+ ## Document Structure
6
+
7
+ An implementation plan should have:
8
+
9
+ 1. **Overview** - 1-2 sentences of what we're building and approach
10
+ 2. **Reviewer** - Code owner (person with most commits to affected files)
11
+ 3. **Blockers** - Only if there are actual blockers (open PRs, unclear requirements)
12
+ 4. **Tasks** - Numbered list of specific, single-file tasks
13
+ 5. **Tests** - What to test and where test patterns live
14
+
15
+ ## Task Design Principles
16
+
17
+ Each task should be:
18
+
19
+ - **Single-prompt executable** - An LLM can complete it in one go
20
+ - **One file focus** - Primarily touches ONE file
21
+ - **Pattern-based** - References similar existing code ("do it like X")
22
+ - **Specific** - Exact file paths and line numbers, not vague directions
23
+
24
+ ## What to Explore First
25
+
26
+ Before writing tasks:
27
+ - Find similar PRs to learn team patterns
28
+ - Use get_file_history to find the CODE OWNER (most commits, not PR similarity)
29
+ - Check for open PRs that might conflict
30
+ - Map which files need changes
31
+
32
+ ## Example Format
33
+
34
+ ```markdown
35
+ # Implementation Plan: Feature Name
36
+
37
+ ## Overview
38
+ Brief description of what and how.
39
+
40
+ ## Reviewer
41
+ Name - top contributor to `affected/files/` (X commits)
42
+
43
+ ## Blockers
44
+ - PR #123 touches same files (wait or coordinate)
45
+
46
+ ## Tasks
47
+
48
+ ### 1. Add the new component
49
+ **File**: `src/components/NewThing.tsx`
50
+
51
+ Create a new component following the pattern in `src/components/ExistingThing.tsx:15-45`.
52
+ It should accept X props and render Y.
53
+
54
+ **Done when**:
55
+ - [ ] Component renders correctly
56
+ - [ ] Props are typed
57
+
58
+ ---
59
+
60
+ ### 2. Wire up to the parent
61
+ **File**: `src/pages/Parent.tsx`
62
+
63
+ Import and use NewThing. Follow how OtherThing is used on line 78.
64
+
65
+ **Done when**:
66
+ - [ ] NewThing appears on the page
67
+
68
+ ## Tests
69
+ Add tests in `src/__tests__/` following the pattern in `ExistingThing.test.tsx`.
70
+ ```
71
+
72
+ ## Things to Avoid
73
+
74
+ - Time estimates (no "2-3 hours")
75
+ - Vague tasks ("improve error handling")
76
+ - Tasks that touch many files
77
+ - Sections listing "what I found" - synthesize into tasks
78
+ - Guessing where code should go - explore more if unsure
@@ -0,0 +1,296 @@
1
+ """Template loader with 3-tier priority: project -> user -> defaults."""
2
+
3
+ import os
4
+ from pathlib import Path
5
+ from typing import Optional, Tuple
6
+
7
+ # Template names and their corresponding files
8
+ TEMPLATE_NAMES = {
9
+ "spec": "spec.md.template",
10
+ "tasks": "tasks.md.template",
11
+ "project": "project.md.template",
12
+ "focus": "focus.md.template",
13
+ "pr-review": "pr-review.md.template",
14
+ "reviewer": "reviewer.md.template",
15
+ "agent-builder": "agent-builder.md.template",
16
+ }
17
+
18
+ # Directory name for templates
19
+ EMDASH_RULES_DIR = ".emdash-rules"
20
+
21
+
22
+ def get_defaults_dir() -> Path:
23
+ """Get the path to the bundled defaults directory."""
24
+ return Path(__file__).parent / "defaults"
25
+
26
+
27
+ def get_project_rules_dir() -> Optional[Path]:
28
+ """Get the project-local .emdash-rules directory if it exists."""
29
+ cwd = Path.cwd()
30
+ rules_dir = cwd / EMDASH_RULES_DIR
31
+ return rules_dir if rules_dir.is_dir() else None
32
+
33
+
34
+ def get_user_rules_dir() -> Optional[Path]:
35
+ """Get the user-wide ~/.emdash-rules directory if it exists."""
36
+ home = Path.home()
37
+ rules_dir = home / EMDASH_RULES_DIR
38
+ return rules_dir if rules_dir.is_dir() else None
39
+
40
+
41
+ def get_template_path(name: str) -> Tuple[Path, str]:
42
+ """Get the path to a template and its source tier.
43
+
44
+ Args:
45
+ name: Template name ("spec", "tasks", or "project")
46
+
47
+ Returns:
48
+ Tuple of (path, tier) where tier is "project", "user", or "default"
49
+
50
+ Raises:
51
+ ValueError: If template name is invalid
52
+ """
53
+ if name not in TEMPLATE_NAMES:
54
+ raise ValueError(f"Invalid template name: {name}. Must be one of: {list(TEMPLATE_NAMES.keys())}")
55
+
56
+ filename = TEMPLATE_NAMES[name]
57
+
58
+ # Priority 1: Project-local .emdash-rules/
59
+ project_dir = get_project_rules_dir()
60
+ if project_dir:
61
+ project_path = project_dir / filename
62
+ if project_path.is_file():
63
+ return project_path, "project"
64
+
65
+ # Priority 2: User-wide ~/.emdash-rules/
66
+ user_dir = get_user_rules_dir()
67
+ if user_dir:
68
+ user_path = user_dir / filename
69
+ if user_path.is_file():
70
+ return user_path, "user"
71
+
72
+ # Priority 3: Bundled defaults
73
+ defaults_path = get_defaults_dir() / filename
74
+ return defaults_path, "default"
75
+
76
+
77
+ def load_template(name: str) -> str:
78
+ """Load a template by name.
79
+
80
+ Searches in order:
81
+ 1. .emdash-rules/ in current directory (project-specific)
82
+ 2. ~/.emdash-rules/ in home directory (user-wide)
83
+ 3. Built-in defaults
84
+
85
+ Args:
86
+ name: Template name ("spec", "tasks", or "project")
87
+
88
+ Returns:
89
+ The template content as a string
90
+
91
+ Raises:
92
+ ValueError: If template name is invalid
93
+ FileNotFoundError: If template file doesn't exist
94
+ """
95
+ path, tier = get_template_path(name)
96
+
97
+ if not path.is_file():
98
+ raise FileNotFoundError(f"Template file not found: {path}")
99
+
100
+ with open(path, "r", encoding="utf-8") as f:
101
+ return f.read()
102
+
103
+
104
+ def list_templates() -> list[dict]:
105
+ """List all templates and their active sources.
106
+
107
+ Returns:
108
+ List of dicts with name, path, and tier for each template
109
+ """
110
+ templates = []
111
+ for name in TEMPLATE_NAMES:
112
+ try:
113
+ path, tier = get_template_path(name)
114
+ templates.append({
115
+ "name": name,
116
+ "filename": TEMPLATE_NAMES[name],
117
+ "path": str(path),
118
+ "tier": tier,
119
+ "exists": path.is_file(),
120
+ })
121
+ except Exception as e:
122
+ templates.append({
123
+ "name": name,
124
+ "filename": TEMPLATE_NAMES[name],
125
+ "path": None,
126
+ "tier": "error",
127
+ "exists": False,
128
+ "error": str(e),
129
+ })
130
+ return templates
131
+
132
+
133
+ def copy_templates_to_dir(target_dir: Path, overwrite: bool = False) -> list[str]:
134
+ """Copy default templates to a target directory.
135
+
136
+ Args:
137
+ target_dir: Directory to copy templates to
138
+ overwrite: Whether to overwrite existing files
139
+
140
+ Returns:
141
+ List of copied template filenames
142
+ """
143
+ defaults_dir = get_defaults_dir()
144
+ target_dir.mkdir(parents=True, exist_ok=True)
145
+
146
+ copied = []
147
+ for name, filename in TEMPLATE_NAMES.items():
148
+ source = defaults_dir / filename
149
+ target = target_dir / filename
150
+
151
+ if not source.is_file():
152
+ continue
153
+
154
+ if target.exists() and not overwrite:
155
+ continue
156
+
157
+ with open(source, "r", encoding="utf-8") as f:
158
+ content = f.read()
159
+
160
+ with open(target, "w", encoding="utf-8") as f:
161
+ f.write(content)
162
+
163
+ copied.append(filename)
164
+
165
+ return copied
166
+
167
+
168
+ def load_template_for_agent(name: str) -> str:
169
+ """Load a template and wrap it for LLM agent use.
170
+
171
+ This takes the human-readable rules markdown and wraps it with
172
+ instructions for the LLM to follow those rules.
173
+
174
+ Args:
175
+ name: Template name ("spec", "tasks", or "project")
176
+
177
+ Returns:
178
+ LLM-ready system prompt incorporating the rules
179
+ """
180
+ rules = load_template(name)
181
+
182
+ # Common tool guidance for all agents
183
+ area_tool_guidance = """
184
+ ## Key Tool: get_area_importance
185
+
186
+ Use this tool to understand where activity is concentrated in the codebase:
187
+
188
+ **Directory-level (default):**
189
+ - `get_area_importance(sort='focus')` - Find HOT SPOTS: areas with recent concentrated activity
190
+ - `get_area_importance(sort='importance')` - Find historically important areas (commits × authors)
191
+ - `get_area_importance(sort='commits')` - Areas with most total commits
192
+ - `get_area_importance(sort='authors')` - Areas with most contributors
193
+
194
+ **File-level (use files=true):**
195
+ - `get_area_importance(files=true, sort='focus')` - Hot FILES with most recent commits
196
+ - `get_area_importance(files=true, sort='importance')` - Most important individual files
197
+
198
+ The tool returns for each area/file:
199
+ - path/file_path: Directory or file path
200
+ - total_commits/commits: Commit count
201
+ - file_count: Number of files (areas only)
202
+ - unique_authors/authors: Number of contributors
203
+ - focus_pct/recent_commits: Recent activity metric
204
+
205
+ **Recommended workflow:**
206
+ 1. First use sort='focus' to find hot areas
207
+ 2. Then use files=true to drill into specific files in those areas
208
+ 3. Use semantic_search or expand_node to explore the hot files"""
209
+
210
+ if name == "spec":
211
+ return f"""You are writing a feature specification. Follow these rules:
212
+
213
+ {rules}
214
+
215
+ {area_tool_guidance}
216
+
217
+ IMPORTANT:
218
+ - Your output must be a single JSON object matching the schema in the rules
219
+ - START by using get_area_importance(sort='focus') to find active areas related to the feature
220
+ - Explore the codebase using tools to find similar patterns
221
+ - Use ask_clarification if you need to ask the user questions
222
+ - Be specific and reference actual file paths from the project
223
+ - The hot spots tell you where similar work is happening - look there for patterns"""
224
+
225
+ elif name == "tasks":
226
+ return f"""You are creating an implementation plan. Follow these rules:
227
+
228
+ {rules}
229
+
230
+ {area_tool_guidance}
231
+
232
+ IMPORTANT:
233
+ - Your output must be markdown following the format in the rules
234
+ - START by using get_area_importance(sort='focus') to understand where changes should go
235
+ - Use get_file_history on hot spot files to find the CODE OWNER (most commits)
236
+ - Each task should be executable by an LLM in a single prompt
237
+ - Include exact file paths and line references
238
+ - Reference patterns from the active areas when describing tasks"""
239
+
240
+ elif name == "project":
241
+ return f"""You are writing a PROJECT.md to help new team members understand the codebase. Follow these rules:
242
+
243
+ {rules}
244
+
245
+ {area_tool_guidance}
246
+
247
+ IMPORTANT:
248
+ - Your output must be markdown following the format in the rules
249
+ - START by using get_area_importance(sort='focus') to find where the team spends time
250
+ - The "Where The Action Is" section should highlight these hot spots
251
+ - Use get_area_importance(sort='importance') to find historically critical areas
252
+ - Write like explaining to a teammate, not writing documentation
253
+ - Focus on insight and understanding, not comprehensive lists
254
+ - Hot spots reveal what's actively being built - explain WHY those areas matter"""
255
+
256
+ elif name == "focus":
257
+ return f"""You are a senior engineering manager analyzing team activity and focus. Follow these rules:
258
+
259
+ {rules}
260
+
261
+ CRITICAL REQUIREMENTS:
262
+ - ALL PRs must be clickable links: [PR #123](https://github.com/owner/repo/pull/123)
263
+ - ALL contributors must be clickable links: [@username](https://github.com/username)
264
+ - The GitHub repository URL will be provided in the data - use it for constructing links
265
+ - Explain WHAT is being built, not just which files are touched
266
+ - Use function/class names and docstrings to understand purpose
267
+ - Group work into thematic streams with activity percentages
268
+ - Include a table for PR analysis and key contributors
269
+ - Provide actionable insights and recommendations"""
270
+
271
+ elif name == "pr-review":
272
+ return f"""You are producing a PR review report. Follow these rules:
273
+
274
+ {rules}
275
+
276
+ IMPORTANT:
277
+ - Your output must be markdown
278
+ - Tell a coherent story of the change, not a file list
279
+ - Cite file paths and functions inline when relevant
280
+ - If data is truncated, note it explicitly"""
281
+
282
+ elif name == "reviewer":
283
+ return f"""You are a code reviewer following the team's established review patterns. Use this reviewer profile:
284
+
285
+ {rules}
286
+
287
+ CRITICAL REQUIREMENTS:
288
+ - Review code with the same focus areas and quality expectations as the top reviewers
289
+ - Match the tone and communication style described in the profile
290
+ - Generate inline comments for specific lines of code
291
+ - Provide actionable, constructive feedback
292
+ - Use the checklist to ensure comprehensive review coverage
293
+ - Your output must be structured JSON with summary, verdict, and inline comments"""
294
+
295
+ else:
296
+ return rules
@@ -0,0 +1,45 @@
1
+ """Utility modules for EmDash."""
2
+
3
+ from .logger import log, setup_logger
4
+
5
+ from .image import (
6
+ is_clipboard_image_available,
7
+ read_clipboard_image,
8
+ encode_image_to_base64,
9
+ encode_image_for_llm,
10
+ resize_image_if_needed,
11
+ get_image_info,
12
+ estimate_image_tokens,
13
+ read_and_prepare_image,
14
+ ClipboardImageError,
15
+ ImageProcessingError,
16
+ ImageFormat,
17
+ )
18
+
19
+ from .git import (
20
+ get_git_remote_url,
21
+ normalize_repo_url,
22
+ get_normalized_remote_url,
23
+ )
24
+
25
+ __all__ = [
26
+ # Logger
27
+ "log",
28
+ "setup_logger",
29
+ # Image
30
+ "is_clipboard_image_available",
31
+ "read_clipboard_image",
32
+ "encode_image_to_base64",
33
+ "encode_image_for_llm",
34
+ "resize_image_if_needed",
35
+ "get_image_info",
36
+ "estimate_image_tokens",
37
+ "read_and_prepare_image",
38
+ "ClipboardImageError",
39
+ "ImageProcessingError",
40
+ "ImageFormat",
41
+ # Git
42
+ "get_git_remote_url",
43
+ "normalize_repo_url",
44
+ "get_normalized_remote_url",
45
+ ]