diff --git a/agent/webapp.py b/agent/webapp.py index 1103d263..930417bd 100644 --- a/agent/webapp.py +++ b/agent/webapp.py @@ -324,12 +324,15 @@ async def get_slack_repo_config(message: str, channel_id: str, thread_ts: str) - owner: str | None = None name: str | None = None - if "repo:" in message: - match = re.search(r"repo:([^ ]+)", message) + if "repo:" in message or "repo " in message: + match = re.search(r"repo[: ]([a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+)", message) if match: - repo = match.group(1).strip() - if "/" in repo: - owner, name = repo.split("/", 1) + owner, name = match.group(1).split("/", 1) + + if not owner or not name: + github_match = re.search(r"github\.com/([a-zA-Z0-9_.-]+/[a-zA-Z0-9_.-]+)", message) + if github_match: + owner, name = github_match.group(1).split("/", 1) if not owner or not name: try: diff --git a/tests/test_slack_context.py b/tests/test_slack_context.py index d21947e8..6a114107 100644 --- a/tests/test_slack_context.py +++ b/tests/test_slack_context.py @@ -206,3 +206,118 @@ def test_get_slack_repo_config_existing_thread_without_repo_uses_default( assert threads_client.requested_thread_id == generate_thread_id_from_slack_thread( "C123", "1.234" ) + + +def test_get_slack_repo_config_space_syntax_detected( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """repo owner/name (space instead of colon) should be detected correctly.""" + threads_client = _FakeThreadsClient(raise_not_found=True) + + async def fake_post_slack_thread_reply(channel_id: str, thread_ts: str, text: str) -> bool: + return True + + monkeypatch.setattr(webapp, "get_client", lambda url: _FakeClient(threads_client)) + monkeypatch.setattr(webapp, "post_slack_thread_reply", fake_post_slack_thread_reply) + + repo = asyncio.run( + webapp.get_slack_repo_config( + "please fix the bug in repo langchain-ai/langchainjs", "C123", "1.234" + ) + ) + + assert repo == {"owner": "langchain-ai", "name": "langchainjs"} + + +def test_get_slack_repo_config_github_url_extracted( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """GitHub URL in message should be used to detect the repo.""" + threads_client = _FakeThreadsClient(raise_not_found=True) + + async def fake_post_slack_thread_reply(channel_id: str, thread_ts: str, text: str) -> bool: + return True + + monkeypatch.setattr(webapp, "get_client", lambda url: _FakeClient(threads_client)) + monkeypatch.setattr(webapp, "post_slack_thread_reply", fake_post_slack_thread_reply) + + repo = asyncio.run( + webapp.get_slack_repo_config( + "I found a bug in https://github.com/langchain-ai/langgraph-api please fix it", + "C123", + "1.234", + ) + ) + + assert repo == {"owner": "langchain-ai", "name": "langgraph-api"} + + +def test_get_slack_repo_config_explicit_repo_beats_github_url( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Explicit repo: syntax takes priority over a GitHub URL also present in the message.""" + threads_client = _FakeThreadsClient(raise_not_found=True) + + async def fake_post_slack_thread_reply(channel_id: str, thread_ts: str, text: str) -> bool: + return True + + monkeypatch.setattr(webapp, "get_client", lambda url: _FakeClient(threads_client)) + monkeypatch.setattr(webapp, "post_slack_thread_reply", fake_post_slack_thread_reply) + + repo = asyncio.run( + webapp.get_slack_repo_config( + "see https://github.com/langchain-ai/langgraph-api but use repo:my-org/my-repo", + "C123", + "1.234", + ) + ) + + assert repo == {"owner": "my-org", "name": "my-repo"} + + +def test_get_slack_repo_config_explicit_space_syntax_beats_thread_metadata( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """Explicit repo owner/name (space syntax) takes priority over saved thread metadata.""" + threads_client = _FakeThreadsClient( + thread={"metadata": {"repo": {"owner": "saved-owner", "name": "saved-repo"}}} + ) + + async def fake_post_slack_thread_reply(channel_id: str, thread_ts: str, text: str) -> bool: + return True + + monkeypatch.setattr(webapp, "get_client", lambda url: _FakeClient(threads_client)) + monkeypatch.setattr(webapp, "post_slack_thread_reply", fake_post_slack_thread_reply) + + repo = asyncio.run( + webapp.get_slack_repo_config( + "actually use repo langchain-ai/langchainjs today", "C123", "1.234" + ) + ) + + assert repo == {"owner": "langchain-ai", "name": "langchainjs"} + + +def test_get_slack_repo_config_github_url_beats_thread_metadata( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """A GitHub URL in the message takes priority over saved thread metadata.""" + threads_client = _FakeThreadsClient( + thread={"metadata": {"repo": {"owner": "saved-owner", "name": "saved-repo"}}} + ) + + async def fake_post_slack_thread_reply(channel_id: str, thread_ts: str, text: str) -> bool: + return True + + monkeypatch.setattr(webapp, "get_client", lambda url: _FakeClient(threads_client)) + monkeypatch.setattr(webapp, "post_slack_thread_reply", fake_post_slack_thread_reply) + + repo = asyncio.run( + webapp.get_slack_repo_config( + "I found a bug in https://github.com/langchain-ai/langgraph-api", + "C123", + "1.234", + ) + ) + + assert repo == {"owner": "langchain-ai", "name": "langgraph-api"}