gobby 0.2.5__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.
- gobby/__init__.py +3 -0
- gobby/adapters/__init__.py +30 -0
- gobby/adapters/base.py +93 -0
- gobby/adapters/claude_code.py +276 -0
- gobby/adapters/codex.py +1292 -0
- gobby/adapters/gemini.py +343 -0
- gobby/agents/__init__.py +37 -0
- gobby/agents/codex_session.py +120 -0
- gobby/agents/constants.py +112 -0
- gobby/agents/context.py +362 -0
- gobby/agents/definitions.py +133 -0
- gobby/agents/gemini_session.py +111 -0
- gobby/agents/registry.py +618 -0
- gobby/agents/runner.py +968 -0
- gobby/agents/session.py +259 -0
- gobby/agents/spawn.py +916 -0
- gobby/agents/spawners/__init__.py +77 -0
- gobby/agents/spawners/base.py +142 -0
- gobby/agents/spawners/cross_platform.py +266 -0
- gobby/agents/spawners/embedded.py +225 -0
- gobby/agents/spawners/headless.py +226 -0
- gobby/agents/spawners/linux.py +125 -0
- gobby/agents/spawners/macos.py +277 -0
- gobby/agents/spawners/windows.py +308 -0
- gobby/agents/tty_config.py +319 -0
- gobby/autonomous/__init__.py +32 -0
- gobby/autonomous/progress_tracker.py +447 -0
- gobby/autonomous/stop_registry.py +269 -0
- gobby/autonomous/stuck_detector.py +383 -0
- gobby/cli/__init__.py +67 -0
- gobby/cli/__main__.py +8 -0
- gobby/cli/agents.py +529 -0
- gobby/cli/artifacts.py +266 -0
- gobby/cli/daemon.py +329 -0
- gobby/cli/extensions.py +526 -0
- gobby/cli/github.py +263 -0
- gobby/cli/init.py +53 -0
- gobby/cli/install.py +614 -0
- gobby/cli/installers/__init__.py +37 -0
- gobby/cli/installers/antigravity.py +65 -0
- gobby/cli/installers/claude.py +363 -0
- gobby/cli/installers/codex.py +192 -0
- gobby/cli/installers/gemini.py +294 -0
- gobby/cli/installers/git_hooks.py +377 -0
- gobby/cli/installers/shared.py +737 -0
- gobby/cli/linear.py +250 -0
- gobby/cli/mcp.py +30 -0
- gobby/cli/mcp_proxy.py +698 -0
- gobby/cli/memory.py +304 -0
- gobby/cli/merge.py +384 -0
- gobby/cli/projects.py +79 -0
- gobby/cli/sessions.py +622 -0
- gobby/cli/tasks/__init__.py +30 -0
- gobby/cli/tasks/_utils.py +658 -0
- gobby/cli/tasks/ai.py +1025 -0
- gobby/cli/tasks/commits.py +169 -0
- gobby/cli/tasks/crud.py +685 -0
- gobby/cli/tasks/deps.py +135 -0
- gobby/cli/tasks/labels.py +63 -0
- gobby/cli/tasks/main.py +273 -0
- gobby/cli/tasks/search.py +178 -0
- gobby/cli/tui.py +34 -0
- gobby/cli/utils.py +513 -0
- gobby/cli/workflows.py +927 -0
- gobby/cli/worktrees.py +481 -0
- gobby/config/__init__.py +129 -0
- gobby/config/app.py +551 -0
- gobby/config/extensions.py +167 -0
- gobby/config/features.py +472 -0
- gobby/config/llm_providers.py +98 -0
- gobby/config/logging.py +66 -0
- gobby/config/mcp.py +346 -0
- gobby/config/persistence.py +247 -0
- gobby/config/servers.py +141 -0
- gobby/config/sessions.py +250 -0
- gobby/config/tasks.py +784 -0
- gobby/hooks/__init__.py +104 -0
- gobby/hooks/artifact_capture.py +213 -0
- gobby/hooks/broadcaster.py +243 -0
- gobby/hooks/event_handlers.py +723 -0
- gobby/hooks/events.py +218 -0
- gobby/hooks/git.py +169 -0
- gobby/hooks/health_monitor.py +171 -0
- gobby/hooks/hook_manager.py +856 -0
- gobby/hooks/hook_types.py +575 -0
- gobby/hooks/plugins.py +813 -0
- gobby/hooks/session_coordinator.py +396 -0
- gobby/hooks/verification_runner.py +268 -0
- gobby/hooks/webhooks.py +339 -0
- gobby/install/claude/commands/gobby/bug.md +51 -0
- gobby/install/claude/commands/gobby/chore.md +51 -0
- gobby/install/claude/commands/gobby/epic.md +52 -0
- gobby/install/claude/commands/gobby/eval.md +235 -0
- gobby/install/claude/commands/gobby/feat.md +49 -0
- gobby/install/claude/commands/gobby/nit.md +52 -0
- gobby/install/claude/commands/gobby/ref.md +52 -0
- gobby/install/claude/hooks/HOOK_SCHEMAS.md +632 -0
- gobby/install/claude/hooks/hook_dispatcher.py +364 -0
- gobby/install/claude/hooks/validate_settings.py +102 -0
- gobby/install/claude/hooks-template.json +118 -0
- gobby/install/codex/hooks/hook_dispatcher.py +153 -0
- gobby/install/codex/prompts/forget.md +7 -0
- gobby/install/codex/prompts/memories.md +7 -0
- gobby/install/codex/prompts/recall.md +7 -0
- gobby/install/codex/prompts/remember.md +13 -0
- gobby/install/gemini/hooks/hook_dispatcher.py +268 -0
- gobby/install/gemini/hooks-template.json +138 -0
- gobby/install/shared/plugins/code_guardian.py +456 -0
- gobby/install/shared/plugins/example_notify.py +331 -0
- gobby/integrations/__init__.py +10 -0
- gobby/integrations/github.py +145 -0
- gobby/integrations/linear.py +145 -0
- gobby/llm/__init__.py +40 -0
- gobby/llm/base.py +120 -0
- gobby/llm/claude.py +578 -0
- gobby/llm/claude_executor.py +503 -0
- gobby/llm/codex.py +322 -0
- gobby/llm/codex_executor.py +513 -0
- gobby/llm/executor.py +316 -0
- gobby/llm/factory.py +34 -0
- gobby/llm/gemini.py +258 -0
- gobby/llm/gemini_executor.py +339 -0
- gobby/llm/litellm.py +287 -0
- gobby/llm/litellm_executor.py +303 -0
- gobby/llm/resolver.py +499 -0
- gobby/llm/service.py +236 -0
- gobby/mcp_proxy/__init__.py +29 -0
- gobby/mcp_proxy/actions.py +175 -0
- gobby/mcp_proxy/daemon_control.py +198 -0
- gobby/mcp_proxy/importer.py +436 -0
- gobby/mcp_proxy/lazy.py +325 -0
- gobby/mcp_proxy/manager.py +798 -0
- gobby/mcp_proxy/metrics.py +609 -0
- gobby/mcp_proxy/models.py +139 -0
- gobby/mcp_proxy/registries.py +215 -0
- gobby/mcp_proxy/schema_hash.py +381 -0
- gobby/mcp_proxy/semantic_search.py +706 -0
- gobby/mcp_proxy/server.py +549 -0
- gobby/mcp_proxy/services/__init__.py +0 -0
- gobby/mcp_proxy/services/fallback.py +306 -0
- gobby/mcp_proxy/services/recommendation.py +224 -0
- gobby/mcp_proxy/services/server_mgmt.py +214 -0
- gobby/mcp_proxy/services/system.py +72 -0
- gobby/mcp_proxy/services/tool_filter.py +231 -0
- gobby/mcp_proxy/services/tool_proxy.py +309 -0
- gobby/mcp_proxy/stdio.py +565 -0
- gobby/mcp_proxy/tools/__init__.py +27 -0
- gobby/mcp_proxy/tools/agents.py +1103 -0
- gobby/mcp_proxy/tools/artifacts.py +207 -0
- gobby/mcp_proxy/tools/hub.py +335 -0
- gobby/mcp_proxy/tools/internal.py +337 -0
- gobby/mcp_proxy/tools/memory.py +543 -0
- gobby/mcp_proxy/tools/merge.py +422 -0
- gobby/mcp_proxy/tools/metrics.py +283 -0
- gobby/mcp_proxy/tools/orchestration/__init__.py +23 -0
- gobby/mcp_proxy/tools/orchestration/cleanup.py +619 -0
- gobby/mcp_proxy/tools/orchestration/monitor.py +380 -0
- gobby/mcp_proxy/tools/orchestration/orchestrate.py +746 -0
- gobby/mcp_proxy/tools/orchestration/review.py +736 -0
- gobby/mcp_proxy/tools/orchestration/utils.py +16 -0
- gobby/mcp_proxy/tools/session_messages.py +1056 -0
- gobby/mcp_proxy/tools/task_dependencies.py +219 -0
- gobby/mcp_proxy/tools/task_expansion.py +591 -0
- gobby/mcp_proxy/tools/task_github.py +393 -0
- gobby/mcp_proxy/tools/task_linear.py +379 -0
- gobby/mcp_proxy/tools/task_orchestration.py +77 -0
- gobby/mcp_proxy/tools/task_readiness.py +522 -0
- gobby/mcp_proxy/tools/task_sync.py +351 -0
- gobby/mcp_proxy/tools/task_validation.py +843 -0
- gobby/mcp_proxy/tools/tasks/__init__.py +25 -0
- gobby/mcp_proxy/tools/tasks/_context.py +112 -0
- gobby/mcp_proxy/tools/tasks/_crud.py +516 -0
- gobby/mcp_proxy/tools/tasks/_factory.py +176 -0
- gobby/mcp_proxy/tools/tasks/_helpers.py +129 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle.py +517 -0
- gobby/mcp_proxy/tools/tasks/_lifecycle_validation.py +301 -0
- gobby/mcp_proxy/tools/tasks/_resolution.py +55 -0
- gobby/mcp_proxy/tools/tasks/_search.py +215 -0
- gobby/mcp_proxy/tools/tasks/_session.py +125 -0
- gobby/mcp_proxy/tools/workflows.py +973 -0
- gobby/mcp_proxy/tools/worktrees.py +1264 -0
- gobby/mcp_proxy/transports/__init__.py +0 -0
- gobby/mcp_proxy/transports/base.py +95 -0
- gobby/mcp_proxy/transports/factory.py +44 -0
- gobby/mcp_proxy/transports/http.py +139 -0
- gobby/mcp_proxy/transports/stdio.py +213 -0
- gobby/mcp_proxy/transports/websocket.py +136 -0
- gobby/memory/backends/__init__.py +116 -0
- gobby/memory/backends/mem0.py +408 -0
- gobby/memory/backends/memu.py +485 -0
- gobby/memory/backends/null.py +111 -0
- gobby/memory/backends/openmemory.py +537 -0
- gobby/memory/backends/sqlite.py +304 -0
- gobby/memory/context.py +87 -0
- gobby/memory/manager.py +1001 -0
- gobby/memory/protocol.py +451 -0
- gobby/memory/search/__init__.py +66 -0
- gobby/memory/search/text.py +127 -0
- gobby/memory/viz.py +258 -0
- gobby/prompts/__init__.py +13 -0
- gobby/prompts/defaults/expansion/system.md +119 -0
- gobby/prompts/defaults/expansion/user.md +48 -0
- gobby/prompts/defaults/external_validation/agent.md +72 -0
- gobby/prompts/defaults/external_validation/external.md +63 -0
- gobby/prompts/defaults/external_validation/spawn.md +83 -0
- gobby/prompts/defaults/external_validation/system.md +6 -0
- gobby/prompts/defaults/features/import_mcp.md +22 -0
- gobby/prompts/defaults/features/import_mcp_github.md +17 -0
- gobby/prompts/defaults/features/import_mcp_search.md +16 -0
- gobby/prompts/defaults/features/recommend_tools.md +32 -0
- gobby/prompts/defaults/features/recommend_tools_hybrid.md +35 -0
- gobby/prompts/defaults/features/recommend_tools_llm.md +30 -0
- gobby/prompts/defaults/features/server_description.md +20 -0
- gobby/prompts/defaults/features/server_description_system.md +6 -0
- gobby/prompts/defaults/features/task_description.md +31 -0
- gobby/prompts/defaults/features/task_description_system.md +6 -0
- gobby/prompts/defaults/features/tool_summary.md +17 -0
- gobby/prompts/defaults/features/tool_summary_system.md +6 -0
- gobby/prompts/defaults/research/step.md +58 -0
- gobby/prompts/defaults/validation/criteria.md +47 -0
- gobby/prompts/defaults/validation/validate.md +38 -0
- gobby/prompts/loader.py +346 -0
- gobby/prompts/models.py +113 -0
- gobby/py.typed +0 -0
- gobby/runner.py +488 -0
- gobby/search/__init__.py +23 -0
- gobby/search/protocol.py +104 -0
- gobby/search/tfidf.py +232 -0
- gobby/servers/__init__.py +7 -0
- gobby/servers/http.py +636 -0
- gobby/servers/models.py +31 -0
- gobby/servers/routes/__init__.py +23 -0
- gobby/servers/routes/admin.py +416 -0
- gobby/servers/routes/dependencies.py +118 -0
- gobby/servers/routes/mcp/__init__.py +24 -0
- gobby/servers/routes/mcp/hooks.py +135 -0
- gobby/servers/routes/mcp/plugins.py +121 -0
- gobby/servers/routes/mcp/tools.py +1337 -0
- gobby/servers/routes/mcp/webhooks.py +159 -0
- gobby/servers/routes/sessions.py +582 -0
- gobby/servers/websocket.py +766 -0
- gobby/sessions/__init__.py +13 -0
- gobby/sessions/analyzer.py +322 -0
- gobby/sessions/lifecycle.py +240 -0
- gobby/sessions/manager.py +563 -0
- gobby/sessions/processor.py +225 -0
- gobby/sessions/summary.py +532 -0
- gobby/sessions/transcripts/__init__.py +41 -0
- gobby/sessions/transcripts/base.py +125 -0
- gobby/sessions/transcripts/claude.py +386 -0
- gobby/sessions/transcripts/codex.py +143 -0
- gobby/sessions/transcripts/gemini.py +195 -0
- gobby/storage/__init__.py +21 -0
- gobby/storage/agents.py +409 -0
- gobby/storage/artifact_classifier.py +341 -0
- gobby/storage/artifacts.py +285 -0
- gobby/storage/compaction.py +67 -0
- gobby/storage/database.py +357 -0
- gobby/storage/inter_session_messages.py +194 -0
- gobby/storage/mcp.py +680 -0
- gobby/storage/memories.py +562 -0
- gobby/storage/merge_resolutions.py +550 -0
- gobby/storage/migrations.py +860 -0
- gobby/storage/migrations_legacy.py +1359 -0
- gobby/storage/projects.py +166 -0
- gobby/storage/session_messages.py +251 -0
- gobby/storage/session_tasks.py +97 -0
- gobby/storage/sessions.py +817 -0
- gobby/storage/task_dependencies.py +223 -0
- gobby/storage/tasks/__init__.py +42 -0
- gobby/storage/tasks/_aggregates.py +180 -0
- gobby/storage/tasks/_crud.py +449 -0
- gobby/storage/tasks/_id.py +104 -0
- gobby/storage/tasks/_lifecycle.py +311 -0
- gobby/storage/tasks/_manager.py +889 -0
- gobby/storage/tasks/_models.py +300 -0
- gobby/storage/tasks/_ordering.py +119 -0
- gobby/storage/tasks/_path_cache.py +110 -0
- gobby/storage/tasks/_queries.py +343 -0
- gobby/storage/tasks/_search.py +143 -0
- gobby/storage/workflow_audit.py +393 -0
- gobby/storage/worktrees.py +547 -0
- gobby/sync/__init__.py +29 -0
- gobby/sync/github.py +333 -0
- gobby/sync/linear.py +304 -0
- gobby/sync/memories.py +284 -0
- gobby/sync/tasks.py +641 -0
- gobby/tasks/__init__.py +8 -0
- gobby/tasks/build_verification.py +193 -0
- gobby/tasks/commits.py +633 -0
- gobby/tasks/context.py +747 -0
- gobby/tasks/criteria.py +342 -0
- gobby/tasks/enhanced_validator.py +226 -0
- gobby/tasks/escalation.py +263 -0
- gobby/tasks/expansion.py +626 -0
- gobby/tasks/external_validator.py +764 -0
- gobby/tasks/issue_extraction.py +171 -0
- gobby/tasks/prompts/expand.py +327 -0
- gobby/tasks/research.py +421 -0
- gobby/tasks/tdd.py +352 -0
- gobby/tasks/tree_builder.py +263 -0
- gobby/tasks/validation.py +712 -0
- gobby/tasks/validation_history.py +357 -0
- gobby/tasks/validation_models.py +89 -0
- gobby/tools/__init__.py +0 -0
- gobby/tools/summarizer.py +170 -0
- gobby/tui/__init__.py +5 -0
- gobby/tui/api_client.py +281 -0
- gobby/tui/app.py +327 -0
- gobby/tui/screens/__init__.py +25 -0
- gobby/tui/screens/agents.py +333 -0
- gobby/tui/screens/chat.py +450 -0
- gobby/tui/screens/dashboard.py +377 -0
- gobby/tui/screens/memory.py +305 -0
- gobby/tui/screens/metrics.py +231 -0
- gobby/tui/screens/orchestrator.py +904 -0
- gobby/tui/screens/sessions.py +412 -0
- gobby/tui/screens/tasks.py +442 -0
- gobby/tui/screens/workflows.py +289 -0
- gobby/tui/screens/worktrees.py +174 -0
- gobby/tui/widgets/__init__.py +21 -0
- gobby/tui/widgets/chat.py +210 -0
- gobby/tui/widgets/conductor.py +104 -0
- gobby/tui/widgets/menu.py +132 -0
- gobby/tui/widgets/message_panel.py +160 -0
- gobby/tui/widgets/review_gate.py +224 -0
- gobby/tui/widgets/task_tree.py +99 -0
- gobby/tui/widgets/token_budget.py +166 -0
- gobby/tui/ws_client.py +258 -0
- gobby/utils/__init__.py +3 -0
- gobby/utils/daemon_client.py +235 -0
- gobby/utils/git.py +222 -0
- gobby/utils/id.py +38 -0
- gobby/utils/json_helpers.py +161 -0
- gobby/utils/logging.py +376 -0
- gobby/utils/machine_id.py +135 -0
- gobby/utils/metrics.py +589 -0
- gobby/utils/project_context.py +182 -0
- gobby/utils/project_init.py +263 -0
- gobby/utils/status.py +256 -0
- gobby/utils/validation.py +80 -0
- gobby/utils/version.py +23 -0
- gobby/workflows/__init__.py +4 -0
- gobby/workflows/actions.py +1310 -0
- gobby/workflows/approval_flow.py +138 -0
- gobby/workflows/artifact_actions.py +103 -0
- gobby/workflows/audit_helpers.py +110 -0
- gobby/workflows/autonomous_actions.py +286 -0
- gobby/workflows/context_actions.py +394 -0
- gobby/workflows/definitions.py +130 -0
- gobby/workflows/detection_helpers.py +208 -0
- gobby/workflows/engine.py +485 -0
- gobby/workflows/evaluator.py +669 -0
- gobby/workflows/git_utils.py +96 -0
- gobby/workflows/hooks.py +169 -0
- gobby/workflows/lifecycle_evaluator.py +613 -0
- gobby/workflows/llm_actions.py +70 -0
- gobby/workflows/loader.py +333 -0
- gobby/workflows/mcp_actions.py +60 -0
- gobby/workflows/memory_actions.py +272 -0
- gobby/workflows/premature_stop.py +164 -0
- gobby/workflows/session_actions.py +139 -0
- gobby/workflows/state_actions.py +123 -0
- gobby/workflows/state_manager.py +104 -0
- gobby/workflows/stop_signal_actions.py +163 -0
- gobby/workflows/summary_actions.py +344 -0
- gobby/workflows/task_actions.py +249 -0
- gobby/workflows/task_enforcement_actions.py +901 -0
- gobby/workflows/templates.py +52 -0
- gobby/workflows/todo_actions.py +84 -0
- gobby/workflows/webhook.py +223 -0
- gobby/workflows/webhook_executor.py +399 -0
- gobby/worktrees/__init__.py +5 -0
- gobby/worktrees/git.py +690 -0
- gobby/worktrees/merge/__init__.py +20 -0
- gobby/worktrees/merge/conflict_parser.py +177 -0
- gobby/worktrees/merge/resolver.py +485 -0
- gobby-0.2.5.dist-info/METADATA +351 -0
- gobby-0.2.5.dist-info/RECORD +383 -0
- gobby-0.2.5.dist-info/WHEEL +5 -0
- gobby-0.2.5.dist-info/entry_points.txt +2 -0
- gobby-0.2.5.dist-info/licenses/LICENSE.md +193 -0
- gobby-0.2.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
"""
|
|
2
|
+
GitHub integration tools for gobby-tasks registry.
|
|
3
|
+
|
|
4
|
+
Provides MCP tools for syncing between gobby tasks and GitHub issues/PRs:
|
|
5
|
+
- import_github_issues: Import GitHub issues as gobby tasks
|
|
6
|
+
- sync_task_to_github: Sync a task to its linked GitHub issue
|
|
7
|
+
- create_pr_for_task: Create a PR for a completed task
|
|
8
|
+
- link_github_repo: Link a GitHub repo to the project
|
|
9
|
+
- get_github_pr_status: Get PR status for a task
|
|
10
|
+
|
|
11
|
+
All tools delegate to GitHubSyncService which uses the official GitHub MCP server.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import logging
|
|
17
|
+
from typing import TYPE_CHECKING, Any
|
|
18
|
+
|
|
19
|
+
from gobby.mcp_proxy.tools.internal import InternalToolRegistry
|
|
20
|
+
from gobby.sync.github import (
|
|
21
|
+
GitHubNotFoundError,
|
|
22
|
+
GitHubRateLimitError,
|
|
23
|
+
GitHubSyncError,
|
|
24
|
+
GitHubSyncService,
|
|
25
|
+
)
|
|
26
|
+
from gobby.utils.project_context import get_project_context
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from gobby.mcp_proxy.manager import MCPClientManager
|
|
30
|
+
from gobby.storage.projects import LocalProjectManager
|
|
31
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
32
|
+
|
|
33
|
+
__all__ = ["create_github_sync_registry"]
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_github_sync_registry(
|
|
39
|
+
task_manager: LocalTaskManager,
|
|
40
|
+
mcp_manager: MCPClientManager,
|
|
41
|
+
project_manager: LocalProjectManager,
|
|
42
|
+
project_id: str | None = None,
|
|
43
|
+
) -> InternalToolRegistry:
|
|
44
|
+
"""
|
|
45
|
+
Create a GitHub sync tool registry.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
task_manager: LocalTaskManager instance for task CRUD.
|
|
49
|
+
mcp_manager: MCPClientManager for GitHub MCP server access.
|
|
50
|
+
project_manager: LocalProjectManager for project config.
|
|
51
|
+
project_id: Default project ID (optional, uses context if not provided).
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
InternalToolRegistry with GitHub sync tools registered.
|
|
55
|
+
"""
|
|
56
|
+
registry = InternalToolRegistry(
|
|
57
|
+
name="gobby-tasks",
|
|
58
|
+
description="GitHub integration tools",
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
def get_current_project_id() -> str | None:
|
|
62
|
+
"""Get the current project ID from context."""
|
|
63
|
+
if project_id:
|
|
64
|
+
return project_id
|
|
65
|
+
ctx = get_project_context()
|
|
66
|
+
if ctx and ctx.get("id"):
|
|
67
|
+
pid_str: str = ctx["id"]
|
|
68
|
+
return pid_str
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
def get_sync_service(repo: str | None = None) -> GitHubSyncService:
|
|
72
|
+
"""Create GitHubSyncService with current project context."""
|
|
73
|
+
pid = get_current_project_id()
|
|
74
|
+
if not pid:
|
|
75
|
+
raise ValueError("No project context - run from a gobby project directory")
|
|
76
|
+
|
|
77
|
+
return GitHubSyncService(
|
|
78
|
+
mcp_manager=mcp_manager,
|
|
79
|
+
task_manager=task_manager,
|
|
80
|
+
project_id=pid,
|
|
81
|
+
github_repo=repo,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# --- GitHub Sync Tools ---
|
|
85
|
+
|
|
86
|
+
@registry.tool(
|
|
87
|
+
name="import_github_issues",
|
|
88
|
+
description=(
|
|
89
|
+
"Import GitHub issues as gobby tasks. "
|
|
90
|
+
"Each issue becomes a task linked to the original GitHub issue."
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
async def import_github_issues(
|
|
94
|
+
repo: str,
|
|
95
|
+
labels: str | None = None,
|
|
96
|
+
state: str = "open",
|
|
97
|
+
) -> dict[str, Any]:
|
|
98
|
+
"""Import GitHub issues as gobby tasks.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
repo: GitHub repo in 'owner/repo' format
|
|
102
|
+
labels: Comma-separated labels to filter issues (optional)
|
|
103
|
+
state: Issue state filter: 'open', 'closed', or 'all' (default: 'open')
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
Dict with 'tasks' list of created task dicts and 'count' of imported issues.
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
service = get_sync_service(repo)
|
|
110
|
+
label_list = labels.split(",") if labels else None
|
|
111
|
+
|
|
112
|
+
tasks = await service.import_github_issues(
|
|
113
|
+
repo=repo,
|
|
114
|
+
labels=label_list,
|
|
115
|
+
state=state,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
"success": True,
|
|
120
|
+
"tasks": tasks,
|
|
121
|
+
"count": len(tasks),
|
|
122
|
+
"repo": repo,
|
|
123
|
+
}
|
|
124
|
+
except GitHubRateLimitError as e:
|
|
125
|
+
return {
|
|
126
|
+
"success": False,
|
|
127
|
+
"error": str(e),
|
|
128
|
+
"error_type": "rate_limit",
|
|
129
|
+
"reset_at": e.reset_at,
|
|
130
|
+
}
|
|
131
|
+
except GitHubNotFoundError as e:
|
|
132
|
+
return {
|
|
133
|
+
"success": False,
|
|
134
|
+
"error": str(e),
|
|
135
|
+
"error_type": "not_found",
|
|
136
|
+
"resource": e.resource,
|
|
137
|
+
}
|
|
138
|
+
except GitHubSyncError as e:
|
|
139
|
+
return {
|
|
140
|
+
"success": False,
|
|
141
|
+
"error": str(e),
|
|
142
|
+
"error_type": "sync_error",
|
|
143
|
+
}
|
|
144
|
+
except RuntimeError as e:
|
|
145
|
+
return {
|
|
146
|
+
"success": False,
|
|
147
|
+
"error": str(e),
|
|
148
|
+
"error_type": "unavailable",
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
@registry.tool(
|
|
152
|
+
name="sync_task_to_github",
|
|
153
|
+
description=(
|
|
154
|
+
"Sync a gobby task to its linked GitHub issue. "
|
|
155
|
+
"Updates the issue title and body to match the task."
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
async def sync_task_to_github(task_id: str) -> dict[str, Any]:
|
|
159
|
+
"""Sync a task to its linked GitHub issue.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
task_id: ID of the task to sync
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
Dict with sync result including updated issue info.
|
|
166
|
+
"""
|
|
167
|
+
try:
|
|
168
|
+
service = get_sync_service()
|
|
169
|
+
result = await service.sync_task_to_github(task_id)
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
"success": True,
|
|
173
|
+
"task_id": task_id,
|
|
174
|
+
"github_result": result,
|
|
175
|
+
}
|
|
176
|
+
except ValueError as e:
|
|
177
|
+
return {
|
|
178
|
+
"success": False,
|
|
179
|
+
"error": str(e),
|
|
180
|
+
"error_type": "invalid_task",
|
|
181
|
+
}
|
|
182
|
+
except GitHubRateLimitError as e:
|
|
183
|
+
return {
|
|
184
|
+
"success": False,
|
|
185
|
+
"error": str(e),
|
|
186
|
+
"error_type": "rate_limit",
|
|
187
|
+
"reset_at": e.reset_at,
|
|
188
|
+
}
|
|
189
|
+
except GitHubNotFoundError as e:
|
|
190
|
+
return {
|
|
191
|
+
"success": False,
|
|
192
|
+
"error": str(e),
|
|
193
|
+
"error_type": "not_found",
|
|
194
|
+
"resource": e.resource,
|
|
195
|
+
}
|
|
196
|
+
except GitHubSyncError as e:
|
|
197
|
+
return {
|
|
198
|
+
"success": False,
|
|
199
|
+
"error": str(e),
|
|
200
|
+
"error_type": "sync_error",
|
|
201
|
+
}
|
|
202
|
+
except RuntimeError as e:
|
|
203
|
+
return {
|
|
204
|
+
"success": False,
|
|
205
|
+
"error": str(e),
|
|
206
|
+
"error_type": "unavailable",
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
@registry.tool(
|
|
210
|
+
name="create_pr_for_task",
|
|
211
|
+
description=(
|
|
212
|
+
"Create a GitHub pull request for a task. Links the PR to the task for tracking."
|
|
213
|
+
),
|
|
214
|
+
)
|
|
215
|
+
async def create_pr_for_task(
|
|
216
|
+
task_id: str,
|
|
217
|
+
head_branch: str,
|
|
218
|
+
base_branch: str = "main",
|
|
219
|
+
draft: bool = False,
|
|
220
|
+
) -> dict[str, Any]:
|
|
221
|
+
"""Create a GitHub PR for a task.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
task_id: ID of the task to create PR for
|
|
225
|
+
head_branch: Branch containing the changes
|
|
226
|
+
base_branch: Branch to merge into (default: 'main')
|
|
227
|
+
draft: Create as draft PR (default: False)
|
|
228
|
+
|
|
229
|
+
Returns:
|
|
230
|
+
Dict with PR info including number and URL.
|
|
231
|
+
"""
|
|
232
|
+
try:
|
|
233
|
+
service = get_sync_service()
|
|
234
|
+
result = await service.create_pr_for_task(
|
|
235
|
+
task_id=task_id,
|
|
236
|
+
head_branch=head_branch,
|
|
237
|
+
base_branch=base_branch,
|
|
238
|
+
draft=draft,
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
return {
|
|
242
|
+
"success": True,
|
|
243
|
+
"task_id": task_id,
|
|
244
|
+
"pr_number": result.get("number"),
|
|
245
|
+
"pr_url": result.get("html_url") or result.get("url"),
|
|
246
|
+
"github_result": result,
|
|
247
|
+
}
|
|
248
|
+
except ValueError as e:
|
|
249
|
+
return {
|
|
250
|
+
"success": False,
|
|
251
|
+
"error": str(e),
|
|
252
|
+
"error_type": "invalid_task",
|
|
253
|
+
}
|
|
254
|
+
except GitHubRateLimitError as e:
|
|
255
|
+
return {
|
|
256
|
+
"success": False,
|
|
257
|
+
"error": str(e),
|
|
258
|
+
"error_type": "rate_limit",
|
|
259
|
+
"reset_at": e.reset_at,
|
|
260
|
+
}
|
|
261
|
+
except GitHubNotFoundError as e:
|
|
262
|
+
return {
|
|
263
|
+
"success": False,
|
|
264
|
+
"error": str(e),
|
|
265
|
+
"error_type": "not_found",
|
|
266
|
+
"resource": e.resource,
|
|
267
|
+
}
|
|
268
|
+
except GitHubSyncError as e:
|
|
269
|
+
return {
|
|
270
|
+
"success": False,
|
|
271
|
+
"error": str(e),
|
|
272
|
+
"error_type": "sync_error",
|
|
273
|
+
}
|
|
274
|
+
except RuntimeError as e:
|
|
275
|
+
return {
|
|
276
|
+
"success": False,
|
|
277
|
+
"error": str(e),
|
|
278
|
+
"error_type": "unavailable",
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
@registry.tool(
|
|
282
|
+
name="link_github_repo",
|
|
283
|
+
description=(
|
|
284
|
+
"Link a GitHub repo to the current project. "
|
|
285
|
+
"Sets the default repo for GitHub operations."
|
|
286
|
+
),
|
|
287
|
+
)
|
|
288
|
+
async def link_github_repo(repo: str) -> dict[str, Any]:
|
|
289
|
+
"""Link a GitHub repo to the current project.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
repo: GitHub repo in 'owner/repo' format
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
Dict with link result.
|
|
296
|
+
"""
|
|
297
|
+
pid = get_current_project_id()
|
|
298
|
+
if not pid:
|
|
299
|
+
return {
|
|
300
|
+
"success": False,
|
|
301
|
+
"error": "No project context - run from a gobby project directory",
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
# Validate repo format
|
|
305
|
+
if "/" not in repo or repo.count("/") != 1:
|
|
306
|
+
return {
|
|
307
|
+
"success": False,
|
|
308
|
+
"error": f"Invalid repo format: '{repo}'. Expected 'owner/repo'",
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
# Update project with github_repo
|
|
312
|
+
project_manager.update(pid, github_repo=repo)
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
"success": True,
|
|
316
|
+
"project_id": pid,
|
|
317
|
+
"github_repo": repo,
|
|
318
|
+
"message": f"Linked project to GitHub repo: {repo}",
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
@registry.tool(
|
|
322
|
+
name="unlink_github_repo",
|
|
323
|
+
description="Remove GitHub repo link from the current project.",
|
|
324
|
+
)
|
|
325
|
+
async def unlink_github_repo() -> dict[str, Any]:
|
|
326
|
+
"""Unlink GitHub repo from the current project.
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
Dict with unlink result.
|
|
330
|
+
"""
|
|
331
|
+
pid = get_current_project_id()
|
|
332
|
+
if not pid:
|
|
333
|
+
return {
|
|
334
|
+
"success": False,
|
|
335
|
+
"error": "No project context - run from a gobby project directory",
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
# Clear github_repo from project
|
|
339
|
+
project_manager.update(pid, github_repo=None)
|
|
340
|
+
|
|
341
|
+
return {
|
|
342
|
+
"success": True,
|
|
343
|
+
"project_id": pid,
|
|
344
|
+
"message": "Unlinked GitHub repo from project",
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
@registry.tool(
|
|
348
|
+
name="get_github_status",
|
|
349
|
+
description=(
|
|
350
|
+
"Get GitHub integration status for the current project. "
|
|
351
|
+
"Shows linked repo, MCP server availability, and task links."
|
|
352
|
+
),
|
|
353
|
+
)
|
|
354
|
+
async def get_github_status() -> dict[str, Any]:
|
|
355
|
+
"""Get GitHub integration status.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Dict with GitHub status including linked repo and availability.
|
|
359
|
+
"""
|
|
360
|
+
pid = get_current_project_id()
|
|
361
|
+
if not pid:
|
|
362
|
+
return {
|
|
363
|
+
"success": False,
|
|
364
|
+
"error": "No project context - run from a gobby project directory",
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
project = project_manager.get(pid)
|
|
368
|
+
github_repo = getattr(project, "github_repo", None) if project else None
|
|
369
|
+
|
|
370
|
+
# Check GitHub MCP availability
|
|
371
|
+
from gobby.integrations.github import GitHubIntegration
|
|
372
|
+
|
|
373
|
+
github = GitHubIntegration(mcp_manager)
|
|
374
|
+
available = github.is_available()
|
|
375
|
+
unavailable_reason = github.get_unavailable_reason() if not available else None
|
|
376
|
+
|
|
377
|
+
# Count linked tasks (direct query since list_tasks doesn't support filters)
|
|
378
|
+
row = task_manager.db.fetchone(
|
|
379
|
+
"SELECT COUNT(*) as count FROM tasks WHERE project_id = ? AND github_issue_number IS NOT NULL",
|
|
380
|
+
(pid,),
|
|
381
|
+
)
|
|
382
|
+
linked_count = row["count"] if row else 0
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
"success": True,
|
|
386
|
+
"project_id": pid,
|
|
387
|
+
"github_repo": github_repo,
|
|
388
|
+
"github_available": available,
|
|
389
|
+
"unavailable_reason": unavailable_reason,
|
|
390
|
+
"linked_tasks_count": linked_count,
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
return registry
|