spec-kitty-cli 0.12.1__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.
- spec_kitty_cli-0.12.1.dist-info/METADATA +1767 -0
- spec_kitty_cli-0.12.1.dist-info/RECORD +242 -0
- spec_kitty_cli-0.12.1.dist-info/WHEEL +4 -0
- spec_kitty_cli-0.12.1.dist-info/entry_points.txt +2 -0
- spec_kitty_cli-0.12.1.dist-info/licenses/LICENSE +21 -0
- specify_cli/__init__.py +171 -0
- specify_cli/acceptance.py +627 -0
- specify_cli/agent_utils/README.md +157 -0
- specify_cli/agent_utils/__init__.py +9 -0
- specify_cli/agent_utils/status.py +356 -0
- specify_cli/cli/__init__.py +6 -0
- specify_cli/cli/commands/__init__.py +46 -0
- specify_cli/cli/commands/accept.py +189 -0
- specify_cli/cli/commands/agent/__init__.py +22 -0
- specify_cli/cli/commands/agent/config.py +382 -0
- specify_cli/cli/commands/agent/context.py +191 -0
- specify_cli/cli/commands/agent/feature.py +1057 -0
- specify_cli/cli/commands/agent/release.py +11 -0
- specify_cli/cli/commands/agent/tasks.py +1253 -0
- specify_cli/cli/commands/agent/workflow.py +801 -0
- specify_cli/cli/commands/context.py +246 -0
- specify_cli/cli/commands/dashboard.py +85 -0
- specify_cli/cli/commands/implement.py +973 -0
- specify_cli/cli/commands/init.py +827 -0
- specify_cli/cli/commands/init_help.py +62 -0
- specify_cli/cli/commands/merge.py +755 -0
- specify_cli/cli/commands/mission.py +240 -0
- specify_cli/cli/commands/ops.py +265 -0
- specify_cli/cli/commands/orchestrate.py +640 -0
- specify_cli/cli/commands/repair.py +175 -0
- specify_cli/cli/commands/research.py +165 -0
- specify_cli/cli/commands/sync.py +364 -0
- specify_cli/cli/commands/upgrade.py +249 -0
- specify_cli/cli/commands/validate_encoding.py +186 -0
- specify_cli/cli/commands/validate_tasks.py +186 -0
- specify_cli/cli/commands/verify.py +310 -0
- specify_cli/cli/helpers.py +123 -0
- specify_cli/cli/step_tracker.py +91 -0
- specify_cli/cli/ui.py +192 -0
- specify_cli/core/__init__.py +53 -0
- specify_cli/core/agent_context.py +311 -0
- specify_cli/core/config.py +96 -0
- specify_cli/core/context_validation.py +362 -0
- specify_cli/core/dependency_graph.py +351 -0
- specify_cli/core/git_ops.py +129 -0
- specify_cli/core/multi_parent_merge.py +323 -0
- specify_cli/core/paths.py +260 -0
- specify_cli/core/project_resolver.py +110 -0
- specify_cli/core/stale_detection.py +263 -0
- specify_cli/core/tool_checker.py +79 -0
- specify_cli/core/utils.py +43 -0
- specify_cli/core/vcs/__init__.py +114 -0
- specify_cli/core/vcs/detection.py +341 -0
- specify_cli/core/vcs/exceptions.py +85 -0
- specify_cli/core/vcs/git.py +1304 -0
- specify_cli/core/vcs/jujutsu.py +1208 -0
- specify_cli/core/vcs/protocol.py +285 -0
- specify_cli/core/vcs/types.py +249 -0
- specify_cli/core/version_checker.py +261 -0
- specify_cli/core/worktree.py +506 -0
- specify_cli/dashboard/__init__.py +28 -0
- specify_cli/dashboard/diagnostics.py +204 -0
- specify_cli/dashboard/handlers/__init__.py +17 -0
- specify_cli/dashboard/handlers/api.py +143 -0
- specify_cli/dashboard/handlers/base.py +65 -0
- specify_cli/dashboard/handlers/features.py +390 -0
- specify_cli/dashboard/handlers/router.py +81 -0
- specify_cli/dashboard/handlers/static.py +50 -0
- specify_cli/dashboard/lifecycle.py +541 -0
- specify_cli/dashboard/scanner.py +437 -0
- specify_cli/dashboard/server.py +123 -0
- specify_cli/dashboard/static/dashboard/dashboard.css +722 -0
- specify_cli/dashboard/static/dashboard/dashboard.js +1424 -0
- specify_cli/dashboard/static/spec-kitty.png +0 -0
- specify_cli/dashboard/templates/__init__.py +36 -0
- specify_cli/dashboard/templates/index.html +258 -0
- specify_cli/doc_generators.py +621 -0
- specify_cli/doc_state.py +408 -0
- specify_cli/frontmatter.py +384 -0
- specify_cli/gap_analysis.py +915 -0
- specify_cli/gitignore_manager.py +300 -0
- specify_cli/guards.py +145 -0
- specify_cli/legacy_detector.py +83 -0
- specify_cli/manifest.py +286 -0
- specify_cli/merge/__init__.py +63 -0
- specify_cli/merge/executor.py +653 -0
- specify_cli/merge/forecast.py +215 -0
- specify_cli/merge/ordering.py +126 -0
- specify_cli/merge/preflight.py +230 -0
- specify_cli/merge/state.py +185 -0
- specify_cli/merge/status_resolver.py +354 -0
- specify_cli/mission.py +654 -0
- specify_cli/missions/documentation/command-templates/implement.md +309 -0
- specify_cli/missions/documentation/command-templates/plan.md +275 -0
- specify_cli/missions/documentation/command-templates/review.md +344 -0
- specify_cli/missions/documentation/command-templates/specify.md +206 -0
- specify_cli/missions/documentation/command-templates/tasks.md +189 -0
- specify_cli/missions/documentation/mission.yaml +113 -0
- specify_cli/missions/documentation/templates/divio/explanation-template.md +192 -0
- specify_cli/missions/documentation/templates/divio/howto-template.md +168 -0
- specify_cli/missions/documentation/templates/divio/reference-template.md +179 -0
- specify_cli/missions/documentation/templates/divio/tutorial-template.md +146 -0
- specify_cli/missions/documentation/templates/generators/jsdoc.json.template +18 -0
- specify_cli/missions/documentation/templates/generators/sphinx-conf.py.template +36 -0
- specify_cli/missions/documentation/templates/plan-template.md +269 -0
- specify_cli/missions/documentation/templates/release-template.md +222 -0
- specify_cli/missions/documentation/templates/spec-template.md +172 -0
- specify_cli/missions/documentation/templates/task-prompt-template.md +140 -0
- specify_cli/missions/documentation/templates/tasks-template.md +159 -0
- specify_cli/missions/research/command-templates/merge.md +388 -0
- specify_cli/missions/research/command-templates/plan.md +125 -0
- specify_cli/missions/research/command-templates/review.md +144 -0
- specify_cli/missions/research/command-templates/tasks.md +225 -0
- specify_cli/missions/research/mission.yaml +115 -0
- specify_cli/missions/research/templates/data-model-template.md +33 -0
- specify_cli/missions/research/templates/plan-template.md +161 -0
- specify_cli/missions/research/templates/research/evidence-log.csv +18 -0
- specify_cli/missions/research/templates/research/source-register.csv +18 -0
- specify_cli/missions/research/templates/research-template.md +35 -0
- specify_cli/missions/research/templates/spec-template.md +64 -0
- specify_cli/missions/research/templates/task-prompt-template.md +148 -0
- specify_cli/missions/research/templates/tasks-template.md +114 -0
- specify_cli/missions/software-dev/command-templates/accept.md +75 -0
- specify_cli/missions/software-dev/command-templates/analyze.md +183 -0
- specify_cli/missions/software-dev/command-templates/checklist.md +286 -0
- specify_cli/missions/software-dev/command-templates/clarify.md +157 -0
- specify_cli/missions/software-dev/command-templates/constitution.md +432 -0
- specify_cli/missions/software-dev/command-templates/dashboard.md +101 -0
- specify_cli/missions/software-dev/command-templates/implement.md +41 -0
- specify_cli/missions/software-dev/command-templates/merge.md +383 -0
- specify_cli/missions/software-dev/command-templates/plan.md +171 -0
- specify_cli/missions/software-dev/command-templates/review.md +32 -0
- specify_cli/missions/software-dev/command-templates/specify.md +321 -0
- specify_cli/missions/software-dev/command-templates/tasks.md +566 -0
- specify_cli/missions/software-dev/mission.yaml +100 -0
- specify_cli/missions/software-dev/templates/plan-template.md +132 -0
- specify_cli/missions/software-dev/templates/spec-template.md +116 -0
- specify_cli/missions/software-dev/templates/task-prompt-template.md +140 -0
- specify_cli/missions/software-dev/templates/tasks-template.md +159 -0
- specify_cli/orchestrator/__init__.py +75 -0
- specify_cli/orchestrator/agent_config.py +224 -0
- specify_cli/orchestrator/agents/__init__.py +170 -0
- specify_cli/orchestrator/agents/augment.py +112 -0
- specify_cli/orchestrator/agents/base.py +243 -0
- specify_cli/orchestrator/agents/claude.py +112 -0
- specify_cli/orchestrator/agents/codex.py +106 -0
- specify_cli/orchestrator/agents/copilot.py +137 -0
- specify_cli/orchestrator/agents/cursor.py +139 -0
- specify_cli/orchestrator/agents/gemini.py +115 -0
- specify_cli/orchestrator/agents/kilocode.py +94 -0
- specify_cli/orchestrator/agents/opencode.py +132 -0
- specify_cli/orchestrator/agents/qwen.py +96 -0
- specify_cli/orchestrator/config.py +455 -0
- specify_cli/orchestrator/executor.py +642 -0
- specify_cli/orchestrator/integration.py +1230 -0
- specify_cli/orchestrator/monitor.py +898 -0
- specify_cli/orchestrator/scheduler.py +832 -0
- specify_cli/orchestrator/state.py +508 -0
- specify_cli/orchestrator/testing/__init__.py +122 -0
- specify_cli/orchestrator/testing/availability.py +346 -0
- specify_cli/orchestrator/testing/fixtures.py +684 -0
- specify_cli/orchestrator/testing/paths.py +218 -0
- specify_cli/plan_validation.py +107 -0
- specify_cli/scripts/debug-dashboard-scan.py +61 -0
- specify_cli/scripts/tasks/acceptance_support.py +695 -0
- specify_cli/scripts/tasks/task_helpers.py +506 -0
- specify_cli/scripts/tasks/tasks_cli.py +848 -0
- specify_cli/scripts/validate_encoding.py +180 -0
- specify_cli/task_metadata_validation.py +274 -0
- specify_cli/tasks_support.py +447 -0
- specify_cli/template/__init__.py +47 -0
- specify_cli/template/asset_generator.py +206 -0
- specify_cli/template/github_client.py +334 -0
- specify_cli/template/manager.py +193 -0
- specify_cli/template/renderer.py +99 -0
- specify_cli/templates/AGENTS.md +190 -0
- specify_cli/templates/POWERSHELL_SYNTAX.md +229 -0
- specify_cli/templates/agent-file-template.md +35 -0
- specify_cli/templates/checklist-template.md +42 -0
- specify_cli/templates/claudeignore-template +58 -0
- specify_cli/templates/command-templates/accept.md +141 -0
- specify_cli/templates/command-templates/analyze.md +253 -0
- specify_cli/templates/command-templates/checklist.md +352 -0
- specify_cli/templates/command-templates/clarify.md +224 -0
- specify_cli/templates/command-templates/constitution.md +432 -0
- specify_cli/templates/command-templates/dashboard.md +175 -0
- specify_cli/templates/command-templates/implement.md +190 -0
- specify_cli/templates/command-templates/merge.md +374 -0
- specify_cli/templates/command-templates/plan.md +171 -0
- specify_cli/templates/command-templates/research.md +88 -0
- specify_cli/templates/command-templates/review.md +510 -0
- specify_cli/templates/command-templates/specify.md +321 -0
- specify_cli/templates/command-templates/status.md +92 -0
- specify_cli/templates/command-templates/tasks.md +199 -0
- specify_cli/templates/git-hooks/pre-commit +22 -0
- specify_cli/templates/git-hooks/pre-commit-agent-check +37 -0
- specify_cli/templates/git-hooks/pre-commit-encoding-check +142 -0
- specify_cli/templates/plan-template.md +108 -0
- specify_cli/templates/spec-template.md +118 -0
- specify_cli/templates/task-prompt-template.md +165 -0
- specify_cli/templates/tasks-template.md +161 -0
- specify_cli/templates/vscode-settings.json +13 -0
- specify_cli/text_sanitization.py +225 -0
- specify_cli/upgrade/__init__.py +18 -0
- specify_cli/upgrade/detector.py +239 -0
- specify_cli/upgrade/metadata.py +182 -0
- specify_cli/upgrade/migrations/__init__.py +65 -0
- specify_cli/upgrade/migrations/base.py +80 -0
- specify_cli/upgrade/migrations/m_0_10_0_python_only.py +359 -0
- specify_cli/upgrade/migrations/m_0_10_12_constitution_cleanup.py +99 -0
- specify_cli/upgrade/migrations/m_0_10_14_update_implement_slash_command.py +176 -0
- specify_cli/upgrade/migrations/m_0_10_1_populate_slash_commands.py +174 -0
- specify_cli/upgrade/migrations/m_0_10_2_update_slash_commands.py +172 -0
- specify_cli/upgrade/migrations/m_0_10_6_workflow_simplification.py +174 -0
- specify_cli/upgrade/migrations/m_0_10_8_fix_memory_structure.py +252 -0
- specify_cli/upgrade/migrations/m_0_10_9_repair_templates.py +168 -0
- specify_cli/upgrade/migrations/m_0_11_0_workspace_per_wp.py +182 -0
- specify_cli/upgrade/migrations/m_0_11_1_improved_workflow_templates.py +173 -0
- specify_cli/upgrade/migrations/m_0_11_1_update_implement_slash_command.py +160 -0
- specify_cli/upgrade/migrations/m_0_11_2_improved_workflow_templates.py +173 -0
- specify_cli/upgrade/migrations/m_0_11_3_workflow_agent_flag.py +114 -0
- specify_cli/upgrade/migrations/m_0_12_0_documentation_mission.py +155 -0
- specify_cli/upgrade/migrations/m_0_12_1_remove_kitty_specs_from_gitignore.py +183 -0
- specify_cli/upgrade/migrations/m_0_2_0_specify_to_kittify.py +80 -0
- specify_cli/upgrade/migrations/m_0_4_8_gitignore_agents.py +118 -0
- specify_cli/upgrade/migrations/m_0_5_0_encoding_hooks.py +141 -0
- specify_cli/upgrade/migrations/m_0_6_5_commands_rename.py +169 -0
- specify_cli/upgrade/migrations/m_0_6_7_ensure_missions.py +228 -0
- specify_cli/upgrade/migrations/m_0_7_2_worktree_commands_dedup.py +89 -0
- specify_cli/upgrade/migrations/m_0_7_3_update_scripts.py +114 -0
- specify_cli/upgrade/migrations/m_0_8_0_remove_active_mission.py +82 -0
- specify_cli/upgrade/migrations/m_0_8_0_worktree_agents_symlink.py +148 -0
- specify_cli/upgrade/migrations/m_0_9_0_frontmatter_only_lanes.py +346 -0
- specify_cli/upgrade/migrations/m_0_9_1_complete_lane_migration.py +656 -0
- specify_cli/upgrade/migrations/m_0_9_2_research_mission_templates.py +221 -0
- specify_cli/upgrade/registry.py +121 -0
- specify_cli/upgrade/runner.py +284 -0
- specify_cli/validators/__init__.py +14 -0
- specify_cli/validators/paths.py +154 -0
- specify_cli/validators/research.py +428 -0
- specify_cli/verify_enhanced.py +270 -0
- specify_cli/workspace_context.py +224 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""Context command - query workspace context information.
|
|
2
|
+
|
|
3
|
+
Provides visibility into current workspace context for LLM agents and users.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import typer
|
|
12
|
+
from rich.console import Console
|
|
13
|
+
from rich.table import Table
|
|
14
|
+
|
|
15
|
+
from specify_cli.tasks_support import find_repo_root, TaskCliError
|
|
16
|
+
from specify_cli.workspace_context import (
|
|
17
|
+
cleanup_orphaned_contexts,
|
|
18
|
+
find_orphaned_contexts,
|
|
19
|
+
list_contexts,
|
|
20
|
+
load_context,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
console = Console()
|
|
24
|
+
app = typer.Typer(help="Query workspace context information")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def detect_current_workspace(cwd: Path, repo_root: Path) -> str | None:
|
|
28
|
+
"""Detect if current directory is inside a worktree.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
cwd: Current working directory
|
|
32
|
+
repo_root: Repository root path
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Workspace name if inside worktree, None otherwise
|
|
36
|
+
"""
|
|
37
|
+
# Check if .worktrees is in path
|
|
38
|
+
if ".worktrees" not in cwd.parts:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
# Extract workspace name from path
|
|
42
|
+
for i, part in enumerate(cwd.parts):
|
|
43
|
+
if part == ".worktrees" and i + 1 < len(cwd.parts):
|
|
44
|
+
return cwd.parts[i + 1]
|
|
45
|
+
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@app.command(name="info")
|
|
50
|
+
def info_command(
|
|
51
|
+
workspace: str = typer.Option(None, "--workspace", "-w", help="Workspace name (auto-detected if inside worktree)"),
|
|
52
|
+
json_output: bool = typer.Option(False, "--json", help="Output in JSON format"),
|
|
53
|
+
) -> None:
|
|
54
|
+
"""Show context information for current or specified workspace.
|
|
55
|
+
|
|
56
|
+
Examples:
|
|
57
|
+
# Auto-detect from current directory (if inside worktree)
|
|
58
|
+
spec-kitty context info
|
|
59
|
+
|
|
60
|
+
# Explicit workspace
|
|
61
|
+
spec-kitty context info --workspace 010-feature-WP02
|
|
62
|
+
|
|
63
|
+
# JSON output
|
|
64
|
+
spec-kitty context info --json
|
|
65
|
+
"""
|
|
66
|
+
try:
|
|
67
|
+
repo_root = find_repo_root()
|
|
68
|
+
except TaskCliError as e:
|
|
69
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
70
|
+
raise typer.Exit(1)
|
|
71
|
+
|
|
72
|
+
# Auto-detect workspace if not provided
|
|
73
|
+
if workspace is None:
|
|
74
|
+
workspace = detect_current_workspace(Path.cwd(), repo_root)
|
|
75
|
+
if workspace is None:
|
|
76
|
+
console.print("[red]Error:[/red] Not inside a worktree and no --workspace specified")
|
|
77
|
+
console.print("\nRun from inside a worktree or use --workspace flag:")
|
|
78
|
+
console.print(" spec-kitty context info --workspace 010-feature-WP02")
|
|
79
|
+
raise typer.Exit(1)
|
|
80
|
+
|
|
81
|
+
# Load context
|
|
82
|
+
context = load_context(repo_root, workspace)
|
|
83
|
+
if context is None:
|
|
84
|
+
console.print(f"[red]Error:[/red] No context found for workspace: {workspace}")
|
|
85
|
+
console.print("\nContext file not found:")
|
|
86
|
+
console.print(f" {repo_root / '.kittify' / 'workspaces' / f'{workspace}.json'}")
|
|
87
|
+
raise typer.Exit(1)
|
|
88
|
+
|
|
89
|
+
# Output
|
|
90
|
+
if json_output:
|
|
91
|
+
print(json.dumps(context.to_dict(), indent=2))
|
|
92
|
+
else:
|
|
93
|
+
console.print("\n[bold cyan]📍 Workspace Context[/bold cyan]")
|
|
94
|
+
console.print("─" * 50)
|
|
95
|
+
|
|
96
|
+
# Build info table
|
|
97
|
+
table = Table(show_header=False, box=None, padding=(0, 2))
|
|
98
|
+
table.add_column("Field", style="dim")
|
|
99
|
+
table.add_column("Value")
|
|
100
|
+
|
|
101
|
+
table.add_row("Work Package", f"[bold]{context.wp_id}[/bold]")
|
|
102
|
+
table.add_row("Feature", context.feature_slug)
|
|
103
|
+
table.add_row("Base Branch", f"[cyan]{context.base_branch}[/cyan]")
|
|
104
|
+
table.add_row("Base Commit", f"[dim]{context.base_commit[:12]}[/dim]")
|
|
105
|
+
table.add_row("Dependencies", ", ".join(context.dependencies) if context.dependencies else "[dim]none[/dim]")
|
|
106
|
+
table.add_row("Created", context.created_at)
|
|
107
|
+
table.add_row("Worktree", context.worktree_path)
|
|
108
|
+
table.add_row("Branch", context.branch_name)
|
|
109
|
+
table.add_row("VCS Backend", context.vcs_backend)
|
|
110
|
+
|
|
111
|
+
console.print(table)
|
|
112
|
+
console.print()
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
@app.command(name="list")
|
|
116
|
+
def list_command(
|
|
117
|
+
json_output: bool = typer.Option(False, "--json", help="Output in JSON format"),
|
|
118
|
+
show_orphaned: bool = typer.Option(False, "--orphaned", help="Show only orphaned contexts"),
|
|
119
|
+
) -> None:
|
|
120
|
+
"""List all workspace contexts.
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
# List all contexts
|
|
124
|
+
spec-kitty context list
|
|
125
|
+
|
|
126
|
+
# List only orphaned contexts (worktree deleted)
|
|
127
|
+
spec-kitty context list --orphaned
|
|
128
|
+
|
|
129
|
+
# JSON output
|
|
130
|
+
spec-kitty context list --json
|
|
131
|
+
"""
|
|
132
|
+
try:
|
|
133
|
+
repo_root = find_repo_root()
|
|
134
|
+
except TaskCliError as e:
|
|
135
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
136
|
+
raise typer.Exit(1)
|
|
137
|
+
|
|
138
|
+
if show_orphaned:
|
|
139
|
+
orphaned = find_orphaned_contexts(repo_root)
|
|
140
|
+
if json_output:
|
|
141
|
+
print(json.dumps([
|
|
142
|
+
{"workspace": name, "context": ctx.to_dict()}
|
|
143
|
+
for name, ctx in orphaned
|
|
144
|
+
], indent=2))
|
|
145
|
+
else:
|
|
146
|
+
if not orphaned:
|
|
147
|
+
console.print("[green]✓[/green] No orphaned contexts found")
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
console.print(f"\n[yellow]⚠️ Found {len(orphaned)} orphaned context(s)[/yellow]\n")
|
|
151
|
+
for name, ctx in orphaned:
|
|
152
|
+
console.print(f" [bold]{name}[/bold]")
|
|
153
|
+
console.print(f" Worktree: [dim]{ctx.worktree_path} (deleted)[/dim]")
|
|
154
|
+
console.print(f" Created: {ctx.created_at}")
|
|
155
|
+
console.print()
|
|
156
|
+
|
|
157
|
+
console.print("[dim]Clean up with: spec-kitty context cleanup[/dim]\n")
|
|
158
|
+
else:
|
|
159
|
+
contexts = list_contexts(repo_root)
|
|
160
|
+
if json_output:
|
|
161
|
+
print(json.dumps([ctx.to_dict() for ctx in contexts], indent=2))
|
|
162
|
+
else:
|
|
163
|
+
if not contexts:
|
|
164
|
+
console.print("[dim]No workspace contexts found[/dim]")
|
|
165
|
+
return
|
|
166
|
+
|
|
167
|
+
console.print(f"\n[bold]Workspace Contexts[/bold] ({len(contexts)} total)\n")
|
|
168
|
+
|
|
169
|
+
table = Table(show_header=True)
|
|
170
|
+
table.add_column("WP", style="bold")
|
|
171
|
+
table.add_column("Feature", style="dim")
|
|
172
|
+
table.add_column("Base", style="cyan")
|
|
173
|
+
table.add_column("Dependencies")
|
|
174
|
+
table.add_column("Status")
|
|
175
|
+
|
|
176
|
+
for ctx in sorted(contexts, key=lambda c: (c.feature_slug, c.wp_id)):
|
|
177
|
+
# Check if worktree exists
|
|
178
|
+
worktree_path = repo_root / ctx.worktree_path
|
|
179
|
+
status = "[green]Active[/green]" if worktree_path.exists() else "[yellow]Orphaned[/yellow]"
|
|
180
|
+
|
|
181
|
+
deps = ", ".join(ctx.dependencies) if ctx.dependencies else "[dim]none[/dim]"
|
|
182
|
+
|
|
183
|
+
table.add_row(
|
|
184
|
+
ctx.wp_id,
|
|
185
|
+
ctx.feature_slug,
|
|
186
|
+
ctx.base_branch,
|
|
187
|
+
deps,
|
|
188
|
+
status,
|
|
189
|
+
)
|
|
190
|
+
|
|
191
|
+
console.print(table)
|
|
192
|
+
console.print()
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
@app.command(name="cleanup")
|
|
196
|
+
def cleanup_command(
|
|
197
|
+
dry_run: bool = typer.Option(False, "--dry-run", help="Show what would be cleaned up without deleting"),
|
|
198
|
+
) -> None:
|
|
199
|
+
"""Clean up orphaned workspace contexts.
|
|
200
|
+
|
|
201
|
+
Removes context files for workspaces that no longer exist.
|
|
202
|
+
|
|
203
|
+
Examples:
|
|
204
|
+
# Preview cleanup
|
|
205
|
+
spec-kitty context cleanup --dry-run
|
|
206
|
+
|
|
207
|
+
# Clean up orphaned contexts
|
|
208
|
+
spec-kitty context cleanup
|
|
209
|
+
"""
|
|
210
|
+
try:
|
|
211
|
+
repo_root = find_repo_root()
|
|
212
|
+
except TaskCliError as e:
|
|
213
|
+
console.print(f"[red]Error:[/red] {e}")
|
|
214
|
+
raise typer.Exit(1)
|
|
215
|
+
|
|
216
|
+
orphaned = find_orphaned_contexts(repo_root)
|
|
217
|
+
|
|
218
|
+
if not orphaned:
|
|
219
|
+
console.print("[green]✓[/green] No orphaned contexts to clean up")
|
|
220
|
+
return
|
|
221
|
+
|
|
222
|
+
console.print(f"\n[yellow]Found {len(orphaned)} orphaned context(s):[/yellow]\n")
|
|
223
|
+
for name, ctx in orphaned:
|
|
224
|
+
console.print(f" [bold]{name}[/bold]")
|
|
225
|
+
console.print(f" {ctx.worktree_path}")
|
|
226
|
+
|
|
227
|
+
console.print()
|
|
228
|
+
|
|
229
|
+
if dry_run:
|
|
230
|
+
console.print("[dim]Dry run - no files deleted[/dim]")
|
|
231
|
+
console.print(f"[dim]Would delete {len(orphaned)} context file(s)[/dim]")
|
|
232
|
+
else:
|
|
233
|
+
cleaned = cleanup_orphaned_contexts(repo_root)
|
|
234
|
+
console.print(f"[green]✓[/green] Cleaned up {cleaned} orphaned context(s)")
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
# Default command when no subcommand specified
|
|
238
|
+
@app.callback(invoke_without_command=True)
|
|
239
|
+
def main(ctx: typer.Context):
|
|
240
|
+
"""Query workspace context information."""
|
|
241
|
+
if ctx.invoked_subcommand is None:
|
|
242
|
+
# No subcommand - default to "info"
|
|
243
|
+
info_command()
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
__all__ = ["app"]
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"""Dashboard command implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import webbrowser
|
|
6
|
+
from typing import Optional
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
from specify_cli.cli.helpers import check_version_compatibility, console, get_project_root_or_exit
|
|
11
|
+
from specify_cli.dashboard import ensure_dashboard_running, stop_dashboard
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def dashboard(
|
|
15
|
+
port: Optional[int] = typer.Option(
|
|
16
|
+
None,
|
|
17
|
+
"--port",
|
|
18
|
+
help="Preferred port for the dashboard (falls back to the first available port).",
|
|
19
|
+
),
|
|
20
|
+
kill: bool = typer.Option(
|
|
21
|
+
False,
|
|
22
|
+
"--kill",
|
|
23
|
+
help="Stop the running dashboard for this project and clear its metadata.",
|
|
24
|
+
),
|
|
25
|
+
) -> None:
|
|
26
|
+
"""Open or stop the Spec Kitty dashboard."""
|
|
27
|
+
project_root = get_project_root_or_exit()
|
|
28
|
+
check_version_compatibility(project_root, "dashboard")
|
|
29
|
+
|
|
30
|
+
console.print()
|
|
31
|
+
|
|
32
|
+
if kill:
|
|
33
|
+
stopped, message = stop_dashboard(project_root)
|
|
34
|
+
console.print(f"[green]✅ {message}[/green]" if stopped else f"[yellow]⚠️ {message}[/yellow]")
|
|
35
|
+
console.print()
|
|
36
|
+
return
|
|
37
|
+
|
|
38
|
+
if port is not None and not (1 <= port <= 65535):
|
|
39
|
+
console.print("[red]❌ Invalid port specified. Use a value between 1 and 65535.[/red]")
|
|
40
|
+
console.print()
|
|
41
|
+
raise typer.Exit(1)
|
|
42
|
+
|
|
43
|
+
try:
|
|
44
|
+
dashboard_url, active_port, started = ensure_dashboard_running(project_root, preferred_port=port)
|
|
45
|
+
except Exception as exc: # pragma: no cover
|
|
46
|
+
console.print("[red]❌ Unable to start or locate the dashboard[/red]")
|
|
47
|
+
console.print(f" {exc}")
|
|
48
|
+
console.print()
|
|
49
|
+
console.print("[yellow]💡 Try running:[/yellow]")
|
|
50
|
+
console.print(f" [cyan]cd {project_root}[/cyan]")
|
|
51
|
+
console.print(" [cyan]spec-kitty init .[/cyan]")
|
|
52
|
+
console.print()
|
|
53
|
+
raise typer.Exit(1)
|
|
54
|
+
|
|
55
|
+
console.print("[bold green]Spec Kitty Dashboard[/bold green]")
|
|
56
|
+
console.print("[cyan]" + "=" * 60 + "[/cyan]")
|
|
57
|
+
console.print()
|
|
58
|
+
console.print(f" [bold cyan]Project Root:[/bold cyan] {project_root}")
|
|
59
|
+
console.print(f" [bold cyan]URL:[/bold cyan] {dashboard_url}")
|
|
60
|
+
console.print(f" [bold cyan]Port:[/bold cyan] {active_port}")
|
|
61
|
+
if port is not None and port != active_port:
|
|
62
|
+
console.print(f" [yellow]⚠️ Requested port {port} was unavailable; using {active_port} instead.[/yellow]")
|
|
63
|
+
console.print()
|
|
64
|
+
|
|
65
|
+
status_msg = (
|
|
66
|
+
f" [green]✅ Status:[/green] Started new dashboard instance on port {active_port}"
|
|
67
|
+
if started
|
|
68
|
+
else f" [green]✅ Status:[/green] Dashboard already running on port {active_port}"
|
|
69
|
+
)
|
|
70
|
+
console.print(status_msg)
|
|
71
|
+
console.print()
|
|
72
|
+
console.print("[cyan]" + "=" * 60 + "[/cyan]")
|
|
73
|
+
console.print()
|
|
74
|
+
|
|
75
|
+
try:
|
|
76
|
+
webbrowser.open(dashboard_url)
|
|
77
|
+
console.print("[green]✅ Opening dashboard in your browser...[/green]")
|
|
78
|
+
console.print()
|
|
79
|
+
except Exception:
|
|
80
|
+
console.print("[yellow]⚠️ Could not automatically open browser[/yellow]")
|
|
81
|
+
console.print(f" Please open this URL manually: [cyan]{dashboard_url}[/cyan]")
|
|
82
|
+
console.print()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
__all__ = ["dashboard"]
|