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,379 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Linear integration tools for gobby-tasks registry.
|
|
3
|
+
|
|
4
|
+
Provides MCP tools for syncing between gobby tasks and Linear issues:
|
|
5
|
+
- import_linear_issues: Import Linear issues as gobby tasks
|
|
6
|
+
- sync_task_to_linear: Sync a task to its linked Linear issue
|
|
7
|
+
- create_issue_for_task: Create a Linear issue for a task
|
|
8
|
+
- link_linear_team: Set default Linear team for the project
|
|
9
|
+
- get_linear_status: Get Linear integration status
|
|
10
|
+
|
|
11
|
+
All tools delegate to LinearSyncService which uses the official Linear 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.linear import (
|
|
21
|
+
LinearNotFoundError,
|
|
22
|
+
LinearRateLimitError,
|
|
23
|
+
LinearSyncError,
|
|
24
|
+
LinearSyncService,
|
|
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_linear_sync_registry"]
|
|
34
|
+
|
|
35
|
+
logger = logging.getLogger(__name__)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def create_linear_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 Linear sync tool registry.
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
task_manager: LocalTaskManager instance for task CRUD.
|
|
49
|
+
mcp_manager: MCPClientManager for Linear 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 Linear sync tools registered.
|
|
55
|
+
"""
|
|
56
|
+
registry = InternalToolRegistry(
|
|
57
|
+
name="gobby-tasks",
|
|
58
|
+
description="Linear 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(team_id: str | None = None) -> LinearSyncService:
|
|
72
|
+
"""Create LinearSyncService 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 LinearSyncService(
|
|
78
|
+
mcp_manager=mcp_manager,
|
|
79
|
+
task_manager=task_manager,
|
|
80
|
+
project_id=pid,
|
|
81
|
+
linear_team_id=team_id,
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
# --- Linear Sync Tools ---
|
|
85
|
+
|
|
86
|
+
@registry.tool(
|
|
87
|
+
name="import_linear_issues",
|
|
88
|
+
description=(
|
|
89
|
+
"Import Linear issues as gobby tasks. "
|
|
90
|
+
"Each issue becomes a task linked to the original Linear issue."
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
async def import_linear_issues(
|
|
94
|
+
team_id: str | None = None,
|
|
95
|
+
state: str | None = None,
|
|
96
|
+
labels: str | None = None,
|
|
97
|
+
) -> dict[str, Any]:
|
|
98
|
+
"""Import Linear issues as gobby tasks.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
team_id: Linear team ID to filter issues (uses project default if not set)
|
|
102
|
+
state: Issue state filter (e.g., 'Todo', 'In Progress', 'Done')
|
|
103
|
+
labels: Comma-separated labels to filter issues (optional)
|
|
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(team_id)
|
|
110
|
+
label_list = labels.split(",") if labels else None
|
|
111
|
+
|
|
112
|
+
tasks = await service.import_linear_issues(
|
|
113
|
+
team_id=team_id,
|
|
114
|
+
state=state,
|
|
115
|
+
labels=label_list,
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
return {
|
|
119
|
+
"success": True,
|
|
120
|
+
"tasks": tasks,
|
|
121
|
+
"count": len(tasks),
|
|
122
|
+
"team_id": team_id,
|
|
123
|
+
}
|
|
124
|
+
except LinearRateLimitError 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 LinearNotFoundError as e:
|
|
132
|
+
return {
|
|
133
|
+
"success": False,
|
|
134
|
+
"error": str(e),
|
|
135
|
+
"error_type": "not_found",
|
|
136
|
+
"resource": e.resource,
|
|
137
|
+
}
|
|
138
|
+
except LinearSyncError 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_linear",
|
|
153
|
+
description=(
|
|
154
|
+
"Sync a gobby task to its linked Linear issue. "
|
|
155
|
+
"Updates the issue title and description to match the task."
|
|
156
|
+
),
|
|
157
|
+
)
|
|
158
|
+
async def sync_task_to_linear(task_id: str) -> dict[str, Any]:
|
|
159
|
+
"""Sync a task to its linked Linear 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_linear(task_id)
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
"success": True,
|
|
173
|
+
"task_id": task_id,
|
|
174
|
+
"linear_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 LinearRateLimitError 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 LinearNotFoundError as e:
|
|
190
|
+
return {
|
|
191
|
+
"success": False,
|
|
192
|
+
"error": str(e),
|
|
193
|
+
"error_type": "not_found",
|
|
194
|
+
"resource": e.resource,
|
|
195
|
+
}
|
|
196
|
+
except LinearSyncError 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_linear_issue_for_task",
|
|
211
|
+
description=(
|
|
212
|
+
"Create a Linear issue from a gobby task. Links the issue to the task for tracking."
|
|
213
|
+
),
|
|
214
|
+
)
|
|
215
|
+
async def create_linear_issue_for_task(
|
|
216
|
+
task_id: str,
|
|
217
|
+
team_id: str | None = None,
|
|
218
|
+
) -> dict[str, Any]:
|
|
219
|
+
"""Create a Linear issue for a task.
|
|
220
|
+
|
|
221
|
+
Args:
|
|
222
|
+
task_id: ID of the task to create issue for
|
|
223
|
+
team_id: Linear team ID (uses task's team_id or project default if not set)
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Dict with issue info including ID.
|
|
227
|
+
"""
|
|
228
|
+
try:
|
|
229
|
+
service = get_sync_service(team_id)
|
|
230
|
+
result = await service.create_issue_for_task(
|
|
231
|
+
task_id=task_id,
|
|
232
|
+
team_id=team_id,
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
return {
|
|
236
|
+
"success": True,
|
|
237
|
+
"task_id": task_id,
|
|
238
|
+
"issue_id": result.get("id"),
|
|
239
|
+
"linear_result": result,
|
|
240
|
+
}
|
|
241
|
+
except ValueError as e:
|
|
242
|
+
return {
|
|
243
|
+
"success": False,
|
|
244
|
+
"error": str(e),
|
|
245
|
+
"error_type": "invalid_task",
|
|
246
|
+
}
|
|
247
|
+
except LinearRateLimitError as e:
|
|
248
|
+
return {
|
|
249
|
+
"success": False,
|
|
250
|
+
"error": str(e),
|
|
251
|
+
"error_type": "rate_limit",
|
|
252
|
+
"reset_at": e.reset_at,
|
|
253
|
+
}
|
|
254
|
+
except LinearNotFoundError as e:
|
|
255
|
+
return {
|
|
256
|
+
"success": False,
|
|
257
|
+
"error": str(e),
|
|
258
|
+
"error_type": "not_found",
|
|
259
|
+
"resource": e.resource,
|
|
260
|
+
}
|
|
261
|
+
except LinearSyncError as e:
|
|
262
|
+
return {
|
|
263
|
+
"success": False,
|
|
264
|
+
"error": str(e),
|
|
265
|
+
"error_type": "sync_error",
|
|
266
|
+
}
|
|
267
|
+
except RuntimeError as e:
|
|
268
|
+
return {
|
|
269
|
+
"success": False,
|
|
270
|
+
"error": str(e),
|
|
271
|
+
"error_type": "unavailable",
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
@registry.tool(
|
|
275
|
+
name="link_linear_team",
|
|
276
|
+
description=(
|
|
277
|
+
"Link a Linear team to the current project. "
|
|
278
|
+
"Sets the default team for Linear operations."
|
|
279
|
+
),
|
|
280
|
+
)
|
|
281
|
+
async def link_linear_team(team_id: str) -> dict[str, Any]:
|
|
282
|
+
"""Link a Linear team to the current project.
|
|
283
|
+
|
|
284
|
+
Args:
|
|
285
|
+
team_id: Linear team ID to link
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Dict with link result.
|
|
289
|
+
"""
|
|
290
|
+
pid = get_current_project_id()
|
|
291
|
+
if not pid:
|
|
292
|
+
return {
|
|
293
|
+
"success": False,
|
|
294
|
+
"error": "No project context - run from a gobby project directory",
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
# Update project with linear_team_id
|
|
298
|
+
project_manager.update(pid, linear_team_id=team_id)
|
|
299
|
+
|
|
300
|
+
return {
|
|
301
|
+
"success": True,
|
|
302
|
+
"project_id": pid,
|
|
303
|
+
"linear_team_id": team_id,
|
|
304
|
+
"message": f"Linked project to Linear team: {team_id}",
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@registry.tool(
|
|
308
|
+
name="unlink_linear_team",
|
|
309
|
+
description="Remove Linear team link from the current project.",
|
|
310
|
+
)
|
|
311
|
+
async def unlink_linear_team() -> dict[str, Any]:
|
|
312
|
+
"""Unlink Linear team from the current project.
|
|
313
|
+
|
|
314
|
+
Returns:
|
|
315
|
+
Dict with unlink result.
|
|
316
|
+
"""
|
|
317
|
+
pid = get_current_project_id()
|
|
318
|
+
if not pid:
|
|
319
|
+
return {
|
|
320
|
+
"success": False,
|
|
321
|
+
"error": "No project context - run from a gobby project directory",
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Clear linear_team_id from project
|
|
325
|
+
project_manager.update(pid, linear_team_id=None)
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
"success": True,
|
|
329
|
+
"project_id": pid,
|
|
330
|
+
"message": "Unlinked Linear team from project",
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
@registry.tool(
|
|
334
|
+
name="get_linear_status",
|
|
335
|
+
description=(
|
|
336
|
+
"Get Linear integration status for the current project. "
|
|
337
|
+
"Shows linked team, MCP server availability, and task links."
|
|
338
|
+
),
|
|
339
|
+
)
|
|
340
|
+
async def get_linear_status() -> dict[str, Any]:
|
|
341
|
+
"""Get Linear integration status.
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
Dict with Linear status including linked team and availability.
|
|
345
|
+
"""
|
|
346
|
+
pid = get_current_project_id()
|
|
347
|
+
if not pid:
|
|
348
|
+
return {
|
|
349
|
+
"success": False,
|
|
350
|
+
"error": "No project context - run from a gobby project directory",
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
project = project_manager.get(pid)
|
|
354
|
+
linear_team_id = getattr(project, "linear_team_id", None) if project else None
|
|
355
|
+
|
|
356
|
+
# Check Linear MCP availability
|
|
357
|
+
from gobby.integrations.linear import LinearIntegration
|
|
358
|
+
|
|
359
|
+
linear = LinearIntegration(mcp_manager)
|
|
360
|
+
available = linear.is_available()
|
|
361
|
+
unavailable_reason = linear.get_unavailable_reason() if not available else None
|
|
362
|
+
|
|
363
|
+
# Count linked tasks (direct query since list_tasks doesn't support filters)
|
|
364
|
+
row = task_manager.db.fetchone(
|
|
365
|
+
"SELECT COUNT(*) as count FROM tasks WHERE project_id = ? AND linear_issue_id IS NOT NULL",
|
|
366
|
+
(pid,),
|
|
367
|
+
)
|
|
368
|
+
linked_count = row["count"] if row else 0
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
"success": True,
|
|
372
|
+
"project_id": pid,
|
|
373
|
+
"linear_team_id": linear_team_id,
|
|
374
|
+
"linear_available": available,
|
|
375
|
+
"unavailable_reason": unavailable_reason,
|
|
376
|
+
"linked_tasks_count": linked_count,
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return registry
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"""Task orchestration tool registry."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from typing import TYPE_CHECKING
|
|
7
|
+
|
|
8
|
+
from gobby.mcp_proxy.tools.internal import InternalToolRegistry
|
|
9
|
+
from gobby.mcp_proxy.tools.orchestration.cleanup import register_cleanup
|
|
10
|
+
from gobby.mcp_proxy.tools.orchestration.monitor import register_monitor
|
|
11
|
+
from gobby.mcp_proxy.tools.orchestration.orchestrate import register_orchestrator
|
|
12
|
+
from gobby.mcp_proxy.tools.orchestration.review import register_reviewer
|
|
13
|
+
from gobby.mcp_proxy.tools.orchestration.utils import get_current_project_id
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from gobby.agents.runner import AgentRunner
|
|
17
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
18
|
+
from gobby.storage.worktrees import LocalWorktreeManager
|
|
19
|
+
from gobby.worktrees.git import WorktreeGitManager
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def create_orchestration_registry(
|
|
25
|
+
task_manager: LocalTaskManager,
|
|
26
|
+
worktree_storage: LocalWorktreeManager,
|
|
27
|
+
git_manager: WorktreeGitManager | None = None,
|
|
28
|
+
agent_runner: AgentRunner | None = None,
|
|
29
|
+
project_id: str | None = None,
|
|
30
|
+
) -> InternalToolRegistry:
|
|
31
|
+
"""Create registry with orchestration tools."""
|
|
32
|
+
registry = InternalToolRegistry(
|
|
33
|
+
name="gobby-orchestration",
|
|
34
|
+
description="Task orchestration, monitoring, and review tools",
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
# Determine default project ID if not provided
|
|
38
|
+
default_project_id = project_id or get_current_project_id()
|
|
39
|
+
|
|
40
|
+
# Register orchestration tools
|
|
41
|
+
register_orchestrator(
|
|
42
|
+
registry=registry,
|
|
43
|
+
task_manager=task_manager,
|
|
44
|
+
worktree_storage=worktree_storage,
|
|
45
|
+
git_manager=git_manager,
|
|
46
|
+
agent_runner=agent_runner,
|
|
47
|
+
default_project_id=default_project_id,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Register monitor tools
|
|
51
|
+
register_monitor(
|
|
52
|
+
registry=registry,
|
|
53
|
+
task_manager=task_manager,
|
|
54
|
+
worktree_storage=worktree_storage,
|
|
55
|
+
agent_runner=agent_runner,
|
|
56
|
+
default_project_id=default_project_id,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
# Register review tools
|
|
60
|
+
register_reviewer(
|
|
61
|
+
registry=registry,
|
|
62
|
+
task_manager=task_manager,
|
|
63
|
+
worktree_storage=worktree_storage,
|
|
64
|
+
agent_runner=agent_runner,
|
|
65
|
+
default_project_id=default_project_id,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Register cleanup tools
|
|
69
|
+
register_cleanup(
|
|
70
|
+
registry=registry,
|
|
71
|
+
task_manager=task_manager,
|
|
72
|
+
worktree_storage=worktree_storage,
|
|
73
|
+
git_manager=git_manager,
|
|
74
|
+
default_project_id=default_project_id,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return registry
|