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
gobby/cli/linear.py
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI commands for Linear integration.
|
|
3
|
+
|
|
4
|
+
Provides commands for syncing gobby tasks with Linear issues.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from gobby.cli.tasks._utils import resolve_task_id
|
|
14
|
+
from gobby.integrations.linear import LinearIntegration
|
|
15
|
+
from gobby.mcp_proxy.manager import MCPClientManager
|
|
16
|
+
from gobby.storage.database import LocalDatabase
|
|
17
|
+
from gobby.storage.projects import LocalProjectManager
|
|
18
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
19
|
+
from gobby.sync.linear import LinearSyncService
|
|
20
|
+
from gobby.utils.project_context import get_project_context
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_linear_deps() -> tuple[LocalTaskManager, MCPClientManager, LocalProjectManager, str]:
|
|
26
|
+
"""Get dependencies for Linear commands."""
|
|
27
|
+
db = LocalDatabase()
|
|
28
|
+
task_manager = LocalTaskManager(db)
|
|
29
|
+
project_manager = LocalProjectManager(db)
|
|
30
|
+
mcp_manager = MCPClientManager()
|
|
31
|
+
|
|
32
|
+
ctx = get_project_context()
|
|
33
|
+
if not ctx or not ctx.get("id"):
|
|
34
|
+
raise click.ClickException("Not in a gobby project directory. Run 'gobby init' first.")
|
|
35
|
+
|
|
36
|
+
project_id: str = ctx["id"]
|
|
37
|
+
return task_manager, mcp_manager, project_manager, project_id
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_sync_service(team_id: str | None = None) -> LinearSyncService:
|
|
41
|
+
"""Create LinearSyncService for CLI commands."""
|
|
42
|
+
task_manager, mcp_manager, _, project_id = get_linear_deps()
|
|
43
|
+
return LinearSyncService(
|
|
44
|
+
mcp_manager=mcp_manager,
|
|
45
|
+
task_manager=task_manager,
|
|
46
|
+
project_id=project_id,
|
|
47
|
+
linear_team_id=team_id,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@click.group()
|
|
52
|
+
def linear() -> None:
|
|
53
|
+
"""Linear integration commands."""
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@linear.command("status")
|
|
58
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
59
|
+
def linear_status(json_format: bool) -> None:
|
|
60
|
+
"""Show Linear integration status."""
|
|
61
|
+
try:
|
|
62
|
+
task_manager, mcp_manager, project_manager, project_id = get_linear_deps()
|
|
63
|
+
|
|
64
|
+
# Get project info
|
|
65
|
+
project = project_manager.get(project_id)
|
|
66
|
+
linear_team_id = project.linear_team_id if project else None
|
|
67
|
+
|
|
68
|
+
# Check Linear MCP availability
|
|
69
|
+
linear = LinearIntegration(mcp_manager)
|
|
70
|
+
available = linear.is_available()
|
|
71
|
+
unavailable_reason = linear.get_unavailable_reason() if not available else None
|
|
72
|
+
|
|
73
|
+
# Count linked tasks
|
|
74
|
+
row = task_manager.db.fetchone(
|
|
75
|
+
"SELECT COUNT(*) as count FROM tasks WHERE project_id = ? AND linear_issue_id IS NOT NULL",
|
|
76
|
+
(project_id,),
|
|
77
|
+
)
|
|
78
|
+
linked_count = row["count"] if row else 0
|
|
79
|
+
|
|
80
|
+
if json_format:
|
|
81
|
+
click.echo(
|
|
82
|
+
json.dumps(
|
|
83
|
+
{
|
|
84
|
+
"project_id": project_id,
|
|
85
|
+
"linear_team_id": linear_team_id,
|
|
86
|
+
"linear_available": available,
|
|
87
|
+
"unavailable_reason": unavailable_reason,
|
|
88
|
+
"linked_tasks_count": linked_count,
|
|
89
|
+
},
|
|
90
|
+
indent=2,
|
|
91
|
+
)
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
click.echo("Linear Integration Status")
|
|
95
|
+
click.echo("=" * 40)
|
|
96
|
+
click.echo(f"Project ID: {project_id}")
|
|
97
|
+
click.echo(f"Linked team: {linear_team_id or '(not linked)'}")
|
|
98
|
+
click.echo(f"Linear MCP available: {'✓' if available else '✗'}")
|
|
99
|
+
if not available:
|
|
100
|
+
click.echo(f" Reason: {unavailable_reason}")
|
|
101
|
+
click.echo(f"Linked tasks: {linked_count}")
|
|
102
|
+
|
|
103
|
+
except click.ClickException:
|
|
104
|
+
raise
|
|
105
|
+
except Exception as e:
|
|
106
|
+
raise click.ClickException(str(e)) from None
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
@linear.command("link")
|
|
110
|
+
@click.argument("team_id")
|
|
111
|
+
def linear_link(team_id: str) -> None:
|
|
112
|
+
"""Link a Linear team to this project.
|
|
113
|
+
|
|
114
|
+
TEAM_ID is the Linear team identifier (e.g., 'ENG-123' or UUID).
|
|
115
|
+
"""
|
|
116
|
+
try:
|
|
117
|
+
_, _, project_manager, project_id = get_linear_deps()
|
|
118
|
+
|
|
119
|
+
project_manager.update(project_id, linear_team_id=team_id)
|
|
120
|
+
click.echo(f"✓ Linked project to Linear team: {team_id}")
|
|
121
|
+
|
|
122
|
+
except click.ClickException:
|
|
123
|
+
raise
|
|
124
|
+
except Exception as e:
|
|
125
|
+
raise click.ClickException(str(e)) from None
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
@linear.command("unlink")
|
|
129
|
+
def linear_unlink() -> None:
|
|
130
|
+
"""Remove Linear team link from this project."""
|
|
131
|
+
try:
|
|
132
|
+
_, _, project_manager, project_id = get_linear_deps()
|
|
133
|
+
|
|
134
|
+
project_manager.update(project_id, linear_team_id=None)
|
|
135
|
+
click.echo("✓ Unlinked Linear team from project")
|
|
136
|
+
|
|
137
|
+
except click.ClickException:
|
|
138
|
+
raise
|
|
139
|
+
except Exception as e:
|
|
140
|
+
raise click.ClickException(str(e)) from None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@linear.command("import")
|
|
144
|
+
@click.argument("team_id", required=False)
|
|
145
|
+
@click.option("--state", help="Issue state filter (e.g., 'Todo', 'In Progress')")
|
|
146
|
+
@click.option("--labels", help="Comma-separated labels to filter issues")
|
|
147
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
148
|
+
def linear_import(
|
|
149
|
+
team_id: str | None, state: str | None, labels: str | None, json_format: bool
|
|
150
|
+
) -> None:
|
|
151
|
+
"""Import Linear issues as gobby tasks.
|
|
152
|
+
|
|
153
|
+
If TEAM_ID is not specified, uses the linked team.
|
|
154
|
+
"""
|
|
155
|
+
try:
|
|
156
|
+
task_manager, mcp_manager, project_manager, project_id = get_linear_deps()
|
|
157
|
+
|
|
158
|
+
# Get team from argument or project config
|
|
159
|
+
if not team_id:
|
|
160
|
+
project = project_manager.get(project_id)
|
|
161
|
+
team_id = project.linear_team_id if project else None
|
|
162
|
+
if not team_id:
|
|
163
|
+
raise click.ClickException(
|
|
164
|
+
"No team specified and project not linked to a Linear team. "
|
|
165
|
+
"Use 'gobby linear link <team_id>' first or specify the team."
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
service = LinearSyncService(
|
|
169
|
+
mcp_manager=mcp_manager,
|
|
170
|
+
task_manager=task_manager,
|
|
171
|
+
project_id=project_id,
|
|
172
|
+
linear_team_id=team_id,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
# Run async import
|
|
176
|
+
label_list = labels.split(",") if labels else None
|
|
177
|
+
tasks = asyncio.run(
|
|
178
|
+
service.import_linear_issues(team_id=team_id, state=state, labels=label_list)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if json_format:
|
|
182
|
+
click.echo(json.dumps({"tasks": tasks, "count": len(tasks)}, indent=2))
|
|
183
|
+
else:
|
|
184
|
+
click.echo(f"✓ Imported {len(tasks)} issues from Linear team {team_id}")
|
|
185
|
+
for task in tasks:
|
|
186
|
+
click.echo(f" - {task.get('id', 'unknown')}: {task.get('title', 'Untitled')}")
|
|
187
|
+
|
|
188
|
+
except click.ClickException:
|
|
189
|
+
raise
|
|
190
|
+
except Exception as e:
|
|
191
|
+
raise click.ClickException(str(e)) from None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
@linear.command("sync")
|
|
195
|
+
@click.argument("task_id")
|
|
196
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
197
|
+
def linear_sync(task_id: str, json_format: bool) -> None:
|
|
198
|
+
"""Sync a task to its linked Linear issue.
|
|
199
|
+
|
|
200
|
+
Updates the Linear issue title and description to match the task.
|
|
201
|
+
"""
|
|
202
|
+
try:
|
|
203
|
+
task_manager, _, _, _ = get_linear_deps()
|
|
204
|
+
resolved = resolve_task_id(task_manager, task_id)
|
|
205
|
+
if not resolved:
|
|
206
|
+
return
|
|
207
|
+
|
|
208
|
+
service = get_sync_service()
|
|
209
|
+
result = asyncio.run(service.sync_task_to_linear(resolved.id))
|
|
210
|
+
|
|
211
|
+
if json_format:
|
|
212
|
+
click.echo(json.dumps(result, indent=2))
|
|
213
|
+
else:
|
|
214
|
+
click.echo(f"✓ Synced task {task_id} to Linear")
|
|
215
|
+
|
|
216
|
+
except click.ClickException:
|
|
217
|
+
raise
|
|
218
|
+
except ValueError as e:
|
|
219
|
+
raise click.ClickException(str(e)) from None
|
|
220
|
+
except Exception as e:
|
|
221
|
+
raise click.ClickException(str(e)) from None
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@linear.command("create")
|
|
225
|
+
@click.argument("task_id")
|
|
226
|
+
@click.option("--team", "team_id", help="Linear team ID")
|
|
227
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
228
|
+
def linear_create(task_id: str, team_id: str | None, json_format: bool) -> None:
|
|
229
|
+
"""Create a Linear issue from a gobby task."""
|
|
230
|
+
try:
|
|
231
|
+
task_manager, _, _, _ = get_linear_deps()
|
|
232
|
+
resolved = resolve_task_id(task_manager, task_id)
|
|
233
|
+
if not resolved:
|
|
234
|
+
return
|
|
235
|
+
|
|
236
|
+
service = get_sync_service(team_id)
|
|
237
|
+
result = asyncio.run(service.create_issue_for_task(task_id=resolved.id, team_id=team_id))
|
|
238
|
+
|
|
239
|
+
if json_format:
|
|
240
|
+
click.echo(json.dumps(result, indent=2))
|
|
241
|
+
else:
|
|
242
|
+
issue_id = result.get("id", "unknown")
|
|
243
|
+
click.echo(f"✓ Created Linear issue {issue_id} for task {task_id}")
|
|
244
|
+
|
|
245
|
+
except click.ClickException:
|
|
246
|
+
raise
|
|
247
|
+
except ValueError as e:
|
|
248
|
+
raise click.ClickException(str(e)) from None
|
|
249
|
+
except Exception as e:
|
|
250
|
+
raise click.ClickException(str(e)) from None
|
gobby/cli/mcp.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""
|
|
2
|
+
MCP server commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@click.command()
|
|
14
|
+
@click.pass_context
|
|
15
|
+
def mcp_server(ctx: click.Context) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Run stdio MCP server for AI CLI integration.
|
|
18
|
+
|
|
19
|
+
This command starts a stdio-based MCP server that:
|
|
20
|
+
- Auto-starts the daemon if not running
|
|
21
|
+
- Provides daemon lifecycle tools (start/stop/restart)
|
|
22
|
+
- Proxies all HTTP MCP tools from the daemon
|
|
23
|
+
|
|
24
|
+
Example usage:
|
|
25
|
+
claude mcp add --transport stdio gobby -- gobby mcp-server
|
|
26
|
+
"""
|
|
27
|
+
from gobby.mcp_proxy.stdio import main as mcp_main
|
|
28
|
+
|
|
29
|
+
# Run the stdio MCP server
|
|
30
|
+
asyncio.run(mcp_main())
|