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/github.py
ADDED
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
"""
|
|
2
|
+
CLI commands for GitHub integration.
|
|
3
|
+
|
|
4
|
+
Provides commands for syncing gobby tasks with GitHub issues and PRs.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import asyncio
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
|
|
13
|
+
from gobby.integrations.github import GitHubIntegration
|
|
14
|
+
from gobby.mcp_proxy.manager import MCPClientManager
|
|
15
|
+
from gobby.storage.database import LocalDatabase
|
|
16
|
+
from gobby.storage.projects import LocalProjectManager
|
|
17
|
+
from gobby.storage.tasks import LocalTaskManager
|
|
18
|
+
from gobby.sync.github import GitHubSyncService
|
|
19
|
+
from gobby.utils.project_context import get_project_context
|
|
20
|
+
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_github_deps() -> tuple[LocalTaskManager, MCPClientManager, LocalProjectManager, str]:
|
|
25
|
+
"""Get dependencies for GitHub commands."""
|
|
26
|
+
db = LocalDatabase()
|
|
27
|
+
task_manager = LocalTaskManager(db)
|
|
28
|
+
project_manager = LocalProjectManager(db)
|
|
29
|
+
mcp_manager = MCPClientManager()
|
|
30
|
+
|
|
31
|
+
ctx = get_project_context()
|
|
32
|
+
if not ctx or not ctx.get("id"):
|
|
33
|
+
raise click.ClickException("Not in a gobby project directory. Run 'gobby init' first.")
|
|
34
|
+
|
|
35
|
+
project_id: str = ctx["id"]
|
|
36
|
+
return task_manager, mcp_manager, project_manager, project_id
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def get_sync_service(repo: str | None = None) -> GitHubSyncService:
|
|
40
|
+
"""Create GitHubSyncService for CLI commands."""
|
|
41
|
+
task_manager, mcp_manager, _, project_id = get_github_deps()
|
|
42
|
+
return GitHubSyncService(
|
|
43
|
+
mcp_manager=mcp_manager,
|
|
44
|
+
task_manager=task_manager,
|
|
45
|
+
project_id=project_id,
|
|
46
|
+
github_repo=repo,
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@click.group()
|
|
51
|
+
def github() -> None:
|
|
52
|
+
"""GitHub integration commands."""
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@github.command("status")
|
|
57
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
58
|
+
def github_status(json_format: bool) -> None:
|
|
59
|
+
"""Show GitHub integration status."""
|
|
60
|
+
try:
|
|
61
|
+
task_manager, mcp_manager, project_manager, project_id = get_github_deps()
|
|
62
|
+
|
|
63
|
+
# Get project info
|
|
64
|
+
project = project_manager.get(project_id)
|
|
65
|
+
github_repo = project.github_repo if project else None
|
|
66
|
+
|
|
67
|
+
# Check GitHub MCP availability
|
|
68
|
+
github = GitHubIntegration(mcp_manager)
|
|
69
|
+
available = github.is_available()
|
|
70
|
+
unavailable_reason = github.get_unavailable_reason() if not available else None
|
|
71
|
+
|
|
72
|
+
# Count linked tasks
|
|
73
|
+
row = task_manager.db.fetchone(
|
|
74
|
+
"SELECT COUNT(*) as count FROM tasks WHERE project_id = ? AND github_issue_number IS NOT NULL",
|
|
75
|
+
(project_id,),
|
|
76
|
+
)
|
|
77
|
+
linked_count = row["count"] if row else 0
|
|
78
|
+
|
|
79
|
+
if json_format:
|
|
80
|
+
click.echo(
|
|
81
|
+
json.dumps(
|
|
82
|
+
{
|
|
83
|
+
"project_id": project_id,
|
|
84
|
+
"github_repo": github_repo,
|
|
85
|
+
"github_available": available,
|
|
86
|
+
"unavailable_reason": unavailable_reason,
|
|
87
|
+
"linked_tasks_count": linked_count,
|
|
88
|
+
},
|
|
89
|
+
indent=2,
|
|
90
|
+
)
|
|
91
|
+
)
|
|
92
|
+
else:
|
|
93
|
+
click.echo("GitHub Integration Status")
|
|
94
|
+
click.echo("=" * 40)
|
|
95
|
+
click.echo(f"Project ID: {project_id}")
|
|
96
|
+
click.echo(f"Linked repo: {github_repo or '(not linked)'}")
|
|
97
|
+
click.echo(f"GitHub MCP available: {'✓' if available else '✗'}")
|
|
98
|
+
if not available:
|
|
99
|
+
click.echo(f" Reason: {unavailable_reason}")
|
|
100
|
+
click.echo(f"Linked tasks: {linked_count}")
|
|
101
|
+
|
|
102
|
+
except click.ClickException:
|
|
103
|
+
raise
|
|
104
|
+
except Exception as e:
|
|
105
|
+
raise click.ClickException(str(e)) from None
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@github.command("link")
|
|
109
|
+
@click.argument("repo")
|
|
110
|
+
def github_link(repo: str) -> None:
|
|
111
|
+
"""Link a GitHub repo to this project.
|
|
112
|
+
|
|
113
|
+
REPO should be in 'owner/repo' format (e.g., 'anthropics/claude-code').
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
_, _, project_manager, project_id = get_github_deps()
|
|
117
|
+
|
|
118
|
+
# Validate repo format
|
|
119
|
+
if "/" not in repo or repo.count("/") != 1:
|
|
120
|
+
raise click.ClickException(f"Invalid repo format: '{repo}'. Expected 'owner/repo'")
|
|
121
|
+
|
|
122
|
+
project_manager.update(project_id, github_repo=repo)
|
|
123
|
+
click.echo(f"✓ Linked project to GitHub repo: {repo}")
|
|
124
|
+
|
|
125
|
+
except click.ClickException:
|
|
126
|
+
raise
|
|
127
|
+
except Exception as e:
|
|
128
|
+
raise click.ClickException(str(e)) from None
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@github.command("unlink")
|
|
132
|
+
def github_unlink() -> None:
|
|
133
|
+
"""Remove GitHub repo link from this project."""
|
|
134
|
+
try:
|
|
135
|
+
_, _, project_manager, project_id = get_github_deps()
|
|
136
|
+
|
|
137
|
+
project_manager.update(project_id, github_repo=None)
|
|
138
|
+
click.echo("✓ Unlinked GitHub repo from project")
|
|
139
|
+
|
|
140
|
+
except click.ClickException:
|
|
141
|
+
raise
|
|
142
|
+
except Exception as e:
|
|
143
|
+
raise click.ClickException(str(e)) from None
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@github.command("import")
|
|
147
|
+
@click.argument("repo", required=False)
|
|
148
|
+
@click.option("--labels", "-l", help="Comma-separated labels to filter issues")
|
|
149
|
+
@click.option(
|
|
150
|
+
"--state",
|
|
151
|
+
"-s",
|
|
152
|
+
type=click.Choice(["open", "closed", "all"]),
|
|
153
|
+
default="open",
|
|
154
|
+
help="Issue state filter",
|
|
155
|
+
)
|
|
156
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
157
|
+
def github_import(repo: str | None, labels: str | None, state: str, json_format: bool) -> None:
|
|
158
|
+
"""Import GitHub issues as gobby tasks.
|
|
159
|
+
|
|
160
|
+
If REPO is not specified, uses the linked repo.
|
|
161
|
+
"""
|
|
162
|
+
try:
|
|
163
|
+
task_manager, mcp_manager, project_manager, project_id = get_github_deps()
|
|
164
|
+
|
|
165
|
+
# Get repo from argument or project config
|
|
166
|
+
if not repo:
|
|
167
|
+
project = project_manager.get(project_id)
|
|
168
|
+
repo = project.github_repo if project else None
|
|
169
|
+
if not repo:
|
|
170
|
+
raise click.ClickException(
|
|
171
|
+
"No repo specified and project not linked to a GitHub repo. "
|
|
172
|
+
"Use 'gobby github link <owner/repo>' first or specify the repo."
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
service = GitHubSyncService(
|
|
176
|
+
mcp_manager=mcp_manager,
|
|
177
|
+
task_manager=task_manager,
|
|
178
|
+
project_id=project_id,
|
|
179
|
+
github_repo=repo,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
# Run async import
|
|
183
|
+
label_list = labels.split(",") if labels else None
|
|
184
|
+
tasks = asyncio.run(service.import_github_issues(repo=repo, labels=label_list, state=state))
|
|
185
|
+
|
|
186
|
+
if json_format:
|
|
187
|
+
click.echo(json.dumps({"tasks": tasks, "count": len(tasks)}, indent=2))
|
|
188
|
+
else:
|
|
189
|
+
click.echo(f"✓ Imported {len(tasks)} issues from {repo}")
|
|
190
|
+
for task in tasks:
|
|
191
|
+
click.echo(f" - {task.get('id', 'unknown')}: {task.get('title', 'Untitled')}")
|
|
192
|
+
|
|
193
|
+
except click.ClickException:
|
|
194
|
+
raise
|
|
195
|
+
except Exception as e:
|
|
196
|
+
raise click.ClickException(str(e)) from None
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@github.command("sync")
|
|
200
|
+
@click.argument("task_id")
|
|
201
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
202
|
+
def github_sync(task_id: str, json_format: bool) -> None:
|
|
203
|
+
"""Sync a task to its linked GitHub issue.
|
|
204
|
+
|
|
205
|
+
Updates the GitHub issue title and body to match the task.
|
|
206
|
+
"""
|
|
207
|
+
try:
|
|
208
|
+
service = get_sync_service()
|
|
209
|
+
result = asyncio.run(service.sync_task_to_github(task_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 GitHub")
|
|
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
|
+
@github.command("pr")
|
|
225
|
+
@click.argument("task_id")
|
|
226
|
+
@click.option("--head", "-H", "head_branch", required=True, help="Branch with changes")
|
|
227
|
+
@click.option("--base", "-b", "base_branch", default="main", help="Branch to merge into")
|
|
228
|
+
@click.option("--draft", "-d", is_flag=True, help="Create as draft PR")
|
|
229
|
+
@click.option("--json", "json_format", is_flag=True, help="Output as JSON")
|
|
230
|
+
def github_pr(
|
|
231
|
+
task_id: str,
|
|
232
|
+
head_branch: str,
|
|
233
|
+
base_branch: str,
|
|
234
|
+
draft: bool,
|
|
235
|
+
json_format: bool,
|
|
236
|
+
) -> None:
|
|
237
|
+
"""Create a GitHub PR for a task."""
|
|
238
|
+
try:
|
|
239
|
+
service = get_sync_service()
|
|
240
|
+
result = asyncio.run(
|
|
241
|
+
service.create_pr_for_task(
|
|
242
|
+
task_id=task_id,
|
|
243
|
+
head_branch=head_branch,
|
|
244
|
+
base_branch=base_branch,
|
|
245
|
+
draft=draft,
|
|
246
|
+
)
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if json_format:
|
|
250
|
+
click.echo(json.dumps(result, indent=2))
|
|
251
|
+
else:
|
|
252
|
+
pr_number = result.get("number", "unknown")
|
|
253
|
+
pr_url = result.get("html_url") or result.get("url", "")
|
|
254
|
+
click.echo(f"✓ Created PR #{pr_number} for task {task_id}")
|
|
255
|
+
if pr_url:
|
|
256
|
+
click.echo(f" {pr_url}")
|
|
257
|
+
|
|
258
|
+
except click.ClickException:
|
|
259
|
+
raise
|
|
260
|
+
except ValueError as e:
|
|
261
|
+
raise click.ClickException(str(e)) from None
|
|
262
|
+
except Exception as e:
|
|
263
|
+
raise click.ClickException(str(e)) from None
|
gobby/cli/init.py
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Project initialization commands.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from gobby.utils.project_init import initialize_project
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@click.command()
|
|
17
|
+
@click.option("--name", "-n", help="Project name")
|
|
18
|
+
@click.option("--github-url", "-g", help="GitHub repository URL")
|
|
19
|
+
@click.pass_context
|
|
20
|
+
def init(ctx: click.Context, name: str | None, github_url: str | None) -> None:
|
|
21
|
+
"""Initialize a new Gobby project in the current directory."""
|
|
22
|
+
cwd = Path.cwd()
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
result = initialize_project(cwd=cwd, name=name, github_url=github_url)
|
|
26
|
+
except Exception as e:
|
|
27
|
+
click.echo(f"Failed to initialize project: {e}", err=True)
|
|
28
|
+
sys.exit(1)
|
|
29
|
+
|
|
30
|
+
if result.already_existed:
|
|
31
|
+
click.echo(f"Project already initialized: {result.project_name}")
|
|
32
|
+
click.echo(f" Project ID: {result.project_id}")
|
|
33
|
+
else:
|
|
34
|
+
click.echo(f"Initialized project '{result.project_name}' in {cwd}")
|
|
35
|
+
click.echo(f" Project ID: {result.project_id}")
|
|
36
|
+
click.echo(f" Config: {cwd / '.gobby' / 'project.json'}")
|
|
37
|
+
|
|
38
|
+
# Show detected verification commands
|
|
39
|
+
if result.verification:
|
|
40
|
+
verification_dict = result.verification.to_dict()
|
|
41
|
+
if verification_dict:
|
|
42
|
+
click.echo(" Detected verification commands:")
|
|
43
|
+
for key, value in verification_dict.items():
|
|
44
|
+
if key != "custom":
|
|
45
|
+
if value is None:
|
|
46
|
+
continue
|
|
47
|
+
click.echo(f" {key}: {value}")
|
|
48
|
+
elif value: # custom dict
|
|
49
|
+
if isinstance(value, dict):
|
|
50
|
+
for custom_name, custom_cmd in value.items():
|
|
51
|
+
click.echo(f" {custom_name}: {custom_cmd}")
|
|
52
|
+
else:
|
|
53
|
+
click.echo(f" custom: {value}")
|