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/db/auth.py
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
"""Supabase authentication with GitHub OAuth."""
|
|
2
|
-
|
|
3
|
-
import os
|
|
4
|
-
from dataclasses import dataclass
|
|
5
|
-
from typing import Optional
|
|
6
|
-
|
|
7
|
-
from supabase import create_client, Client
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@dataclass
|
|
11
|
-
class User:
|
|
12
|
-
"""Authenticated user from Supabase Auth."""
|
|
13
|
-
|
|
14
|
-
id: str
|
|
15
|
-
email: Optional[str]
|
|
16
|
-
github_handle: Optional[str]
|
|
17
|
-
avatar_url: Optional[str]
|
|
18
|
-
access_token: Optional[str] = None
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class AuthProvider:
|
|
22
|
-
"""Handle Supabase authentication with GitHub OAuth."""
|
|
23
|
-
|
|
24
|
-
def __init__(self, url: Optional[str] = None, key: Optional[str] = None):
|
|
25
|
-
self.url = url or os.getenv("SUPABASE_URL")
|
|
26
|
-
self.key = key or os.getenv("SUPABASE_KEY")
|
|
27
|
-
|
|
28
|
-
if not self.url or not self.key:
|
|
29
|
-
raise ValueError("SUPABASE_URL and SUPABASE_KEY must be set")
|
|
30
|
-
|
|
31
|
-
self.client: Client = create_client(self.url, self.key)
|
|
32
|
-
|
|
33
|
-
def get_github_login_url(self, redirect_to: Optional[str] = None) -> str:
|
|
34
|
-
"""Get the GitHub OAuth login URL.
|
|
35
|
-
|
|
36
|
-
Args:
|
|
37
|
-
redirect_to: URL to redirect after login (for web apps)
|
|
38
|
-
|
|
39
|
-
Returns:
|
|
40
|
-
GitHub OAuth URL to redirect user to
|
|
41
|
-
"""
|
|
42
|
-
options = {}
|
|
43
|
-
if redirect_to:
|
|
44
|
-
options["redirect_to"] = redirect_to
|
|
45
|
-
|
|
46
|
-
response = self.client.auth.sign_in_with_oauth(
|
|
47
|
-
{"provider": "github", "options": options}
|
|
48
|
-
)
|
|
49
|
-
return response.url
|
|
50
|
-
|
|
51
|
-
def sign_in_with_github_token(self, access_token: str) -> Optional[User]:
|
|
52
|
-
"""Sign in with an existing GitHub access token.
|
|
53
|
-
|
|
54
|
-
Args:
|
|
55
|
-
access_token: GitHub OAuth access token
|
|
56
|
-
|
|
57
|
-
Returns:
|
|
58
|
-
User if successful, None otherwise
|
|
59
|
-
"""
|
|
60
|
-
try:
|
|
61
|
-
response = self.client.auth.sign_in_with_id_token(
|
|
62
|
-
{"provider": "github", "token": access_token}
|
|
63
|
-
)
|
|
64
|
-
return self._session_to_user(response)
|
|
65
|
-
except Exception:
|
|
66
|
-
return None
|
|
67
|
-
|
|
68
|
-
def get_session(self) -> Optional[User]:
|
|
69
|
-
"""Get the current authenticated user from session.
|
|
70
|
-
|
|
71
|
-
Returns:
|
|
72
|
-
User if authenticated, None otherwise
|
|
73
|
-
"""
|
|
74
|
-
try:
|
|
75
|
-
response = self.client.auth.get_session()
|
|
76
|
-
if response and response.user:
|
|
77
|
-
return self._response_to_user(response)
|
|
78
|
-
return None
|
|
79
|
-
except Exception:
|
|
80
|
-
return None
|
|
81
|
-
|
|
82
|
-
def sign_out(self) -> bool:
|
|
83
|
-
"""Sign out the current user.
|
|
84
|
-
|
|
85
|
-
Returns:
|
|
86
|
-
True if successful
|
|
87
|
-
"""
|
|
88
|
-
try:
|
|
89
|
-
self.client.auth.sign_out()
|
|
90
|
-
return True
|
|
91
|
-
except Exception:
|
|
92
|
-
return False
|
|
93
|
-
|
|
94
|
-
def _session_to_user(self, session) -> Optional[User]:
|
|
95
|
-
"""Convert Supabase session to User model."""
|
|
96
|
-
if not session or not session.user:
|
|
97
|
-
return None
|
|
98
|
-
|
|
99
|
-
user_meta = session.user.user_metadata or {}
|
|
100
|
-
|
|
101
|
-
return User(
|
|
102
|
-
id=session.user.id,
|
|
103
|
-
email=session.user.email,
|
|
104
|
-
github_handle=user_meta.get("user_name") or user_meta.get("preferred_username"),
|
|
105
|
-
avatar_url=user_meta.get("avatar_url"),
|
|
106
|
-
access_token=session.access_token if hasattr(session, "access_token") else None,
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
def _response_to_user(self, response) -> Optional[User]:
|
|
110
|
-
"""Convert Supabase auth response to User model."""
|
|
111
|
-
if not response or not response.user:
|
|
112
|
-
return None
|
|
113
|
-
|
|
114
|
-
user_meta = response.user.user_metadata or {}
|
|
115
|
-
|
|
116
|
-
return User(
|
|
117
|
-
id=response.user.id,
|
|
118
|
-
email=response.user.email,
|
|
119
|
-
github_handle=user_meta.get("user_name") or user_meta.get("preferred_username"),
|
|
120
|
-
avatar_url=user_meta.get("avatar_url"),
|
|
121
|
-
access_token=response.session.access_token if response.session else None,
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
# Singleton instance
|
|
126
|
-
_auth_instance: Optional[AuthProvider] = None
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def get_auth() -> AuthProvider:
|
|
130
|
-
"""Get the auth provider instance."""
|
|
131
|
-
global _auth_instance
|
|
132
|
-
if _auth_instance is None:
|
|
133
|
-
_auth_instance = AuthProvider()
|
|
134
|
-
return _auth_instance
|
emdash_core/db/models.py
DELETED
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
"""Data models for the database layer."""
|
|
2
|
-
|
|
3
|
-
from dataclasses import dataclass, field
|
|
4
|
-
from datetime import datetime
|
|
5
|
-
from enum import Enum
|
|
6
|
-
from typing import Optional
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class FeatureStatus(str, Enum):
|
|
10
|
-
"""Status of a feature in the workflow."""
|
|
11
|
-
|
|
12
|
-
TODO = "todo"
|
|
13
|
-
IN_DESIGN_REVIEW = "in_design_review"
|
|
14
|
-
IN_PROGRESS = "in_progress"
|
|
15
|
-
IN_PR = "in_pr"
|
|
16
|
-
DONE = "done"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
class PRStatus(str, Enum):
|
|
20
|
-
"""Status of a pull request."""
|
|
21
|
-
|
|
22
|
-
OPEN = "open"
|
|
23
|
-
MERGED = "merged"
|
|
24
|
-
CLOSED = "closed"
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@dataclass
|
|
28
|
-
class Project:
|
|
29
|
-
"""A project in the system."""
|
|
30
|
-
|
|
31
|
-
id: str
|
|
32
|
-
name: str
|
|
33
|
-
repo_url: Optional[str] = None
|
|
34
|
-
owner_id: Optional[str] = None
|
|
35
|
-
created_at: Optional[datetime] = None
|
|
36
|
-
updated_at: Optional[datetime] = None
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
@dataclass
|
|
40
|
-
class TeamMember:
|
|
41
|
-
"""A team member belonging to a project."""
|
|
42
|
-
|
|
43
|
-
id: str
|
|
44
|
-
project_id: str
|
|
45
|
-
name: str
|
|
46
|
-
email: Optional[str] = None
|
|
47
|
-
github_handle: Optional[str] = None
|
|
48
|
-
role: Optional[str] = None
|
|
49
|
-
user_id: Optional[str] = None # Links to auth.users
|
|
50
|
-
created_at: Optional[datetime] = None
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
@dataclass
|
|
54
|
-
class Feature:
|
|
55
|
-
"""A feature belonging to a project."""
|
|
56
|
-
|
|
57
|
-
id: str
|
|
58
|
-
project_id: str
|
|
59
|
-
title: str
|
|
60
|
-
description: Optional[str] = None
|
|
61
|
-
status: FeatureStatus = FeatureStatus.TODO
|
|
62
|
-
spec: Optional[str] = None
|
|
63
|
-
plan: Optional[str] = None
|
|
64
|
-
tasks: Optional[str] = None
|
|
65
|
-
created_at: Optional[datetime] = None
|
|
66
|
-
updated_at: Optional[datetime] = None
|
|
67
|
-
# Populated by joins
|
|
68
|
-
assignees: list["TeamMember"] = field(default_factory=list)
|
|
69
|
-
prs: list["FeaturePR"] = field(default_factory=list)
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
@dataclass
|
|
73
|
-
class FeatureAssignee:
|
|
74
|
-
"""Junction table for feature-assignee many-to-many relationship."""
|
|
75
|
-
|
|
76
|
-
feature_id: str
|
|
77
|
-
team_member_id: str
|
|
78
|
-
assigned_at: Optional[datetime] = None
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@dataclass
|
|
82
|
-
class FeaturePR:
|
|
83
|
-
"""A pull request linked to a feature."""
|
|
84
|
-
|
|
85
|
-
id: str
|
|
86
|
-
feature_id: str
|
|
87
|
-
pr_url: str
|
|
88
|
-
pr_number: int
|
|
89
|
-
title: Optional[str] = None
|
|
90
|
-
status: PRStatus = PRStatus.OPEN
|
|
91
|
-
created_at: Optional[datetime] = None
|
emdash_core/db/provider.py
DELETED
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
"""Abstract database provider interface."""
|
|
2
|
-
|
|
3
|
-
from abc import ABC, abstractmethod
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
from .models import (
|
|
7
|
-
Feature,
|
|
8
|
-
FeatureAssignee,
|
|
9
|
-
FeaturePR,
|
|
10
|
-
FeatureStatus,
|
|
11
|
-
PRStatus,
|
|
12
|
-
Project,
|
|
13
|
-
TeamMember,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class DatabaseProvider(ABC):
|
|
18
|
-
"""Abstract base class for database providers.
|
|
19
|
-
|
|
20
|
-
Implementations must provide all CRUD operations for the data models.
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
|
-
# -------------------------------------------------------------------------
|
|
24
|
-
# Projects
|
|
25
|
-
# -------------------------------------------------------------------------
|
|
26
|
-
|
|
27
|
-
@abstractmethod
|
|
28
|
-
async def create_project(
|
|
29
|
-
self, name: str, repo_url: Optional[str] = None, owner_id: Optional[str] = None
|
|
30
|
-
) -> Project:
|
|
31
|
-
"""Create a new project.
|
|
32
|
-
|
|
33
|
-
Args:
|
|
34
|
-
name: Project name
|
|
35
|
-
repo_url: GitHub repository URL
|
|
36
|
-
owner_id: Auth user ID of the project owner
|
|
37
|
-
"""
|
|
38
|
-
pass
|
|
39
|
-
|
|
40
|
-
@abstractmethod
|
|
41
|
-
async def get_project(self, project_id: str) -> Optional[Project]:
|
|
42
|
-
"""Get a project by ID."""
|
|
43
|
-
pass
|
|
44
|
-
|
|
45
|
-
@abstractmethod
|
|
46
|
-
async def get_project_by_name(self, name: str) -> Optional[Project]:
|
|
47
|
-
"""Get a project by name."""
|
|
48
|
-
pass
|
|
49
|
-
|
|
50
|
-
@abstractmethod
|
|
51
|
-
async def list_projects(self) -> list[Project]:
|
|
52
|
-
"""List all projects."""
|
|
53
|
-
pass
|
|
54
|
-
|
|
55
|
-
@abstractmethod
|
|
56
|
-
async def update_project(
|
|
57
|
-
self, project_id: str, name: Optional[str] = None, repo_url: Optional[str] = None
|
|
58
|
-
) -> Optional[Project]:
|
|
59
|
-
"""Update a project."""
|
|
60
|
-
pass
|
|
61
|
-
|
|
62
|
-
@abstractmethod
|
|
63
|
-
async def delete_project(self, project_id: str) -> bool:
|
|
64
|
-
"""Delete a project and all related data."""
|
|
65
|
-
pass
|
|
66
|
-
|
|
67
|
-
# -------------------------------------------------------------------------
|
|
68
|
-
# Team Members
|
|
69
|
-
# -------------------------------------------------------------------------
|
|
70
|
-
|
|
71
|
-
@abstractmethod
|
|
72
|
-
async def create_team_member(
|
|
73
|
-
self,
|
|
74
|
-
project_id: str,
|
|
75
|
-
name: str,
|
|
76
|
-
email: Optional[str] = None,
|
|
77
|
-
github_handle: Optional[str] = None,
|
|
78
|
-
role: Optional[str] = None,
|
|
79
|
-
user_id: Optional[str] = None,
|
|
80
|
-
) -> TeamMember:
|
|
81
|
-
"""Create a new team member.
|
|
82
|
-
|
|
83
|
-
Args:
|
|
84
|
-
project_id: Project this member belongs to
|
|
85
|
-
name: Display name
|
|
86
|
-
email: Email address
|
|
87
|
-
github_handle: GitHub username
|
|
88
|
-
role: Role in the project
|
|
89
|
-
user_id: Auth user ID (links to Supabase auth.users)
|
|
90
|
-
"""
|
|
91
|
-
pass
|
|
92
|
-
|
|
93
|
-
@abstractmethod
|
|
94
|
-
async def get_team_member(self, member_id: str) -> Optional[TeamMember]:
|
|
95
|
-
"""Get a team member by ID."""
|
|
96
|
-
pass
|
|
97
|
-
|
|
98
|
-
@abstractmethod
|
|
99
|
-
async def list_team_members(self, project_id: str) -> list[TeamMember]:
|
|
100
|
-
"""List all team members for a project."""
|
|
101
|
-
pass
|
|
102
|
-
|
|
103
|
-
@abstractmethod
|
|
104
|
-
async def update_team_member(
|
|
105
|
-
self,
|
|
106
|
-
member_id: str,
|
|
107
|
-
name: Optional[str] = None,
|
|
108
|
-
email: Optional[str] = None,
|
|
109
|
-
github_handle: Optional[str] = None,
|
|
110
|
-
role: Optional[str] = None,
|
|
111
|
-
) -> Optional[TeamMember]:
|
|
112
|
-
"""Update a team member."""
|
|
113
|
-
pass
|
|
114
|
-
|
|
115
|
-
@abstractmethod
|
|
116
|
-
async def delete_team_member(self, member_id: str) -> bool:
|
|
117
|
-
"""Delete a team member."""
|
|
118
|
-
pass
|
|
119
|
-
|
|
120
|
-
# -------------------------------------------------------------------------
|
|
121
|
-
# Features
|
|
122
|
-
# -------------------------------------------------------------------------
|
|
123
|
-
|
|
124
|
-
@abstractmethod
|
|
125
|
-
async def create_feature(
|
|
126
|
-
self,
|
|
127
|
-
project_id: str,
|
|
128
|
-
title: str,
|
|
129
|
-
description: Optional[str] = None,
|
|
130
|
-
status: FeatureStatus = FeatureStatus.TODO,
|
|
131
|
-
spec: Optional[str] = None,
|
|
132
|
-
plan: Optional[str] = None,
|
|
133
|
-
tasks: Optional[str] = None,
|
|
134
|
-
) -> Feature:
|
|
135
|
-
"""Create a new feature."""
|
|
136
|
-
pass
|
|
137
|
-
|
|
138
|
-
@abstractmethod
|
|
139
|
-
async def get_feature(self, feature_id: str, include_relations: bool = True) -> Optional[Feature]:
|
|
140
|
-
"""Get a feature by ID, optionally including assignees and PRs."""
|
|
141
|
-
pass
|
|
142
|
-
|
|
143
|
-
@abstractmethod
|
|
144
|
-
async def list_features(
|
|
145
|
-
self, project_id: str, status: Optional[FeatureStatus] = None, include_relations: bool = False
|
|
146
|
-
) -> list[Feature]:
|
|
147
|
-
"""List features for a project, optionally filtered by status."""
|
|
148
|
-
pass
|
|
149
|
-
|
|
150
|
-
@abstractmethod
|
|
151
|
-
async def update_feature(
|
|
152
|
-
self,
|
|
153
|
-
feature_id: str,
|
|
154
|
-
title: Optional[str] = None,
|
|
155
|
-
description: Optional[str] = None,
|
|
156
|
-
status: Optional[FeatureStatus] = None,
|
|
157
|
-
spec: Optional[str] = None,
|
|
158
|
-
plan: Optional[str] = None,
|
|
159
|
-
tasks: Optional[str] = None,
|
|
160
|
-
) -> Optional[Feature]:
|
|
161
|
-
"""Update a feature."""
|
|
162
|
-
pass
|
|
163
|
-
|
|
164
|
-
@abstractmethod
|
|
165
|
-
async def delete_feature(self, feature_id: str) -> bool:
|
|
166
|
-
"""Delete a feature and all related data."""
|
|
167
|
-
pass
|
|
168
|
-
|
|
169
|
-
# -------------------------------------------------------------------------
|
|
170
|
-
# Feature Assignees
|
|
171
|
-
# -------------------------------------------------------------------------
|
|
172
|
-
|
|
173
|
-
@abstractmethod
|
|
174
|
-
async def assign_feature(self, feature_id: str, team_member_id: str) -> FeatureAssignee:
|
|
175
|
-
"""Assign a team member to a feature."""
|
|
176
|
-
pass
|
|
177
|
-
|
|
178
|
-
@abstractmethod
|
|
179
|
-
async def unassign_feature(self, feature_id: str, team_member_id: str) -> bool:
|
|
180
|
-
"""Remove a team member from a feature."""
|
|
181
|
-
pass
|
|
182
|
-
|
|
183
|
-
@abstractmethod
|
|
184
|
-
async def get_feature_assignees(self, feature_id: str) -> list[TeamMember]:
|
|
185
|
-
"""Get all assignees for a feature."""
|
|
186
|
-
pass
|
|
187
|
-
|
|
188
|
-
# -------------------------------------------------------------------------
|
|
189
|
-
# Feature PRs
|
|
190
|
-
# -------------------------------------------------------------------------
|
|
191
|
-
|
|
192
|
-
@abstractmethod
|
|
193
|
-
async def add_feature_pr(
|
|
194
|
-
self,
|
|
195
|
-
feature_id: str,
|
|
196
|
-
pr_url: str,
|
|
197
|
-
pr_number: int,
|
|
198
|
-
title: Optional[str] = None,
|
|
199
|
-
status: PRStatus = PRStatus.OPEN,
|
|
200
|
-
) -> FeaturePR:
|
|
201
|
-
"""Add a PR to a feature."""
|
|
202
|
-
pass
|
|
203
|
-
|
|
204
|
-
@abstractmethod
|
|
205
|
-
async def update_feature_pr(
|
|
206
|
-
self,
|
|
207
|
-
pr_id: str,
|
|
208
|
-
title: Optional[str] = None,
|
|
209
|
-
status: Optional[PRStatus] = None,
|
|
210
|
-
) -> Optional[FeaturePR]:
|
|
211
|
-
"""Update a feature PR."""
|
|
212
|
-
pass
|
|
213
|
-
|
|
214
|
-
@abstractmethod
|
|
215
|
-
async def remove_feature_pr(self, pr_id: str) -> bool:
|
|
216
|
-
"""Remove a PR from a feature."""
|
|
217
|
-
pass
|
|
218
|
-
|
|
219
|
-
@abstractmethod
|
|
220
|
-
async def get_feature_prs(self, feature_id: str) -> list[FeaturePR]:
|
|
221
|
-
"""Get all PRs for a feature."""
|
|
222
|
-
pass
|