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
specify_cli/cli/ui.py
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Reusable UI helpers for Spec Kitty CLI interactions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
|
|
7
|
+
import readchar
|
|
8
|
+
import typer
|
|
9
|
+
from rich.console import Console
|
|
10
|
+
from rich.live import Live
|
|
11
|
+
from rich.panel import Panel
|
|
12
|
+
from rich.table import Table
|
|
13
|
+
|
|
14
|
+
from .step_tracker import StepTracker
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_key() -> str:
|
|
18
|
+
"""Get a single keypress in a cross-platform way using readchar."""
|
|
19
|
+
key = readchar.readkey()
|
|
20
|
+
|
|
21
|
+
if key == readchar.key.UP or key == readchar.key.CTRL_P:
|
|
22
|
+
return "up"
|
|
23
|
+
if key == readchar.key.DOWN or key == readchar.key.CTRL_N:
|
|
24
|
+
return "down"
|
|
25
|
+
|
|
26
|
+
if key == readchar.key.ENTER:
|
|
27
|
+
return "enter"
|
|
28
|
+
|
|
29
|
+
if key == readchar.key.ESC or key == "\x1b":
|
|
30
|
+
return "escape"
|
|
31
|
+
|
|
32
|
+
if key == readchar.key.CTRL_C:
|
|
33
|
+
raise KeyboardInterrupt
|
|
34
|
+
|
|
35
|
+
return key
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _resolve_console(console: Optional[Console]) -> Console:
|
|
39
|
+
return console or Console()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def select_with_arrows(
|
|
43
|
+
options: Dict,
|
|
44
|
+
prompt_text: str = "Select an option",
|
|
45
|
+
default_key: str | None = None,
|
|
46
|
+
console: Console | None = None,
|
|
47
|
+
) -> str:
|
|
48
|
+
"""
|
|
49
|
+
Interactive selection using arrow keys with Rich Live display.
|
|
50
|
+
"""
|
|
51
|
+
console = _resolve_console(console)
|
|
52
|
+
option_keys = list(options.keys())
|
|
53
|
+
if default_key and default_key in option_keys:
|
|
54
|
+
selected_index = option_keys.index(default_key)
|
|
55
|
+
else:
|
|
56
|
+
selected_index = 0
|
|
57
|
+
|
|
58
|
+
selected_key = None
|
|
59
|
+
|
|
60
|
+
def create_selection_panel():
|
|
61
|
+
"""Create the selection panel with current selection highlighted."""
|
|
62
|
+
table = Table.grid(padding=(0, 2))
|
|
63
|
+
table.add_column(style="cyan", justify="left", width=3)
|
|
64
|
+
table.add_column(style="white", justify="left")
|
|
65
|
+
|
|
66
|
+
for i, key in enumerate(option_keys):
|
|
67
|
+
if i == selected_index:
|
|
68
|
+
table.add_row("▶", f"[cyan]{key}[/cyan] [dim]({options[key]})[/dim]")
|
|
69
|
+
else:
|
|
70
|
+
table.add_row(" ", f"[cyan]{key}[/cyan] [dim]({options[key]})[/dim]")
|
|
71
|
+
|
|
72
|
+
table.add_row("", "")
|
|
73
|
+
table.add_row("", "[dim]Use ↑/↓ to navigate, Enter to select, Esc to cancel[/dim]")
|
|
74
|
+
|
|
75
|
+
return Panel(
|
|
76
|
+
table,
|
|
77
|
+
title=f"[bold]{prompt_text}[/bold]",
|
|
78
|
+
border_style="cyan",
|
|
79
|
+
padding=(1, 2),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
console.print()
|
|
83
|
+
|
|
84
|
+
def run_selection_loop():
|
|
85
|
+
nonlocal selected_key, selected_index
|
|
86
|
+
with Live(create_selection_panel(), console=console, transient=True, auto_refresh=False) as live:
|
|
87
|
+
while True:
|
|
88
|
+
try:
|
|
89
|
+
key = get_key()
|
|
90
|
+
if key == "up":
|
|
91
|
+
selected_index = (selected_index - 1) % len(option_keys)
|
|
92
|
+
elif key == "down":
|
|
93
|
+
selected_index = (selected_index + 1) % len(option_keys)
|
|
94
|
+
elif key == "enter":
|
|
95
|
+
selected_key = option_keys[selected_index]
|
|
96
|
+
break
|
|
97
|
+
elif key == "escape":
|
|
98
|
+
console.print("\n[yellow]Selection cancelled[/yellow]")
|
|
99
|
+
raise typer.Exit(1)
|
|
100
|
+
|
|
101
|
+
live.update(create_selection_panel(), refresh=True)
|
|
102
|
+
|
|
103
|
+
except KeyboardInterrupt:
|
|
104
|
+
console.print("\n[yellow]Selection cancelled[/yellow]")
|
|
105
|
+
raise typer.Exit(1)
|
|
106
|
+
|
|
107
|
+
run_selection_loop()
|
|
108
|
+
|
|
109
|
+
if selected_key is None:
|
|
110
|
+
console.print("\n[red]Selection failed.[/red]")
|
|
111
|
+
raise typer.Exit(1)
|
|
112
|
+
|
|
113
|
+
return selected_key
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def multi_select_with_arrows(
|
|
117
|
+
options: Dict[str, str],
|
|
118
|
+
prompt_text: str = "Select options",
|
|
119
|
+
default_keys: Optional[List[str]] = None,
|
|
120
|
+
console: Console | None = None,
|
|
121
|
+
) -> List[str]:
|
|
122
|
+
"""Allow selecting one or more options using arrow keys + space to toggle."""
|
|
123
|
+
|
|
124
|
+
console = _resolve_console(console)
|
|
125
|
+
option_keys = list(options.keys())
|
|
126
|
+
selected_indices: set[int] = set()
|
|
127
|
+
if default_keys:
|
|
128
|
+
for key in default_keys:
|
|
129
|
+
if key in option_keys:
|
|
130
|
+
selected_indices.add(option_keys.index(key))
|
|
131
|
+
if not selected_indices and option_keys:
|
|
132
|
+
selected_indices.add(0)
|
|
133
|
+
|
|
134
|
+
cursor_index = next(iter(selected_indices)) if selected_indices else 0
|
|
135
|
+
|
|
136
|
+
def build_panel():
|
|
137
|
+
table = Table.grid(padding=(0, 2))
|
|
138
|
+
table.add_column(style="cyan", justify="left", width=3)
|
|
139
|
+
table.add_column(style="white", justify="left")
|
|
140
|
+
|
|
141
|
+
for i, key in enumerate(option_keys):
|
|
142
|
+
indicator = "[cyan]☑" if i in selected_indices else "[bright_black]☐"
|
|
143
|
+
pointer = "▶" if i == cursor_index else " "
|
|
144
|
+
table.add_row(pointer, f"{indicator} [cyan]{key}[/cyan] [dim]({options[key]})[/dim]")
|
|
145
|
+
|
|
146
|
+
table.add_row("", "")
|
|
147
|
+
table.add_row(
|
|
148
|
+
"",
|
|
149
|
+
"[dim]Use ↑/↓ to move, Space to toggle, Enter to confirm, Esc to cancel[/dim]",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
return Panel(table, title=f"[bold]{prompt_text}[/bold]", border_style="cyan", padding=(1, 2))
|
|
153
|
+
|
|
154
|
+
def normalize_selection() -> List[str]:
|
|
155
|
+
return [option_keys[i] for i in range(len(option_keys)) if i in selected_indices]
|
|
156
|
+
|
|
157
|
+
console.print()
|
|
158
|
+
|
|
159
|
+
with Live(build_panel(), console=console, transient=True, auto_refresh=False) as live:
|
|
160
|
+
while True:
|
|
161
|
+
try:
|
|
162
|
+
key = get_key()
|
|
163
|
+
if key == "up":
|
|
164
|
+
cursor_index = (cursor_index - 1) % len(option_keys)
|
|
165
|
+
elif key == "down":
|
|
166
|
+
cursor_index = (cursor_index + 1) % len(option_keys)
|
|
167
|
+
elif key in (" ", readchar.key.SPACE):
|
|
168
|
+
if cursor_index in selected_indices:
|
|
169
|
+
selected_indices.remove(cursor_index)
|
|
170
|
+
else:
|
|
171
|
+
selected_indices.add(cursor_index)
|
|
172
|
+
elif key == "enter":
|
|
173
|
+
current = normalize_selection()
|
|
174
|
+
if current:
|
|
175
|
+
return current
|
|
176
|
+
elif key == "escape":
|
|
177
|
+
console.print("\n[yellow]Selection cancelled[/yellow]")
|
|
178
|
+
raise typer.Exit(1)
|
|
179
|
+
|
|
180
|
+
live.update(build_panel(), refresh=True)
|
|
181
|
+
|
|
182
|
+
except KeyboardInterrupt:
|
|
183
|
+
console.print("\n[yellow]Selection cancelled[/yellow]")
|
|
184
|
+
raise typer.Exit(1)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
__all__ = [
|
|
188
|
+
"StepTracker",
|
|
189
|
+
"get_key",
|
|
190
|
+
"select_with_arrows",
|
|
191
|
+
"multi_select_with_arrows",
|
|
192
|
+
]
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""Core utilities and configuration exports."""
|
|
2
|
+
|
|
3
|
+
from .config import (
|
|
4
|
+
AGENT_COMMAND_CONFIG,
|
|
5
|
+
AGENT_TOOL_REQUIREMENTS,
|
|
6
|
+
AI_CHOICES,
|
|
7
|
+
BANNER,
|
|
8
|
+
DEFAULT_MISSION_KEY,
|
|
9
|
+
DEFAULT_TEMPLATE_REPO,
|
|
10
|
+
MISSION_CHOICES,
|
|
11
|
+
SCRIPT_TYPE_CHOICES,
|
|
12
|
+
)
|
|
13
|
+
from .utils import format_path, ensure_directory, safe_remove, get_platform
|
|
14
|
+
from .git_ops import run_command, is_git_repo, init_git_repo, get_current_branch
|
|
15
|
+
from .project_resolver import (
|
|
16
|
+
locate_project_root,
|
|
17
|
+
resolve_template_path,
|
|
18
|
+
resolve_worktree_aware_feature_dir,
|
|
19
|
+
get_active_mission_key,
|
|
20
|
+
)
|
|
21
|
+
from .tool_checker import (
|
|
22
|
+
check_tool,
|
|
23
|
+
check_tool_for_tracker,
|
|
24
|
+
check_all_tools,
|
|
25
|
+
get_tool_version,
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"AGENT_COMMAND_CONFIG",
|
|
30
|
+
"AGENT_TOOL_REQUIREMENTS",
|
|
31
|
+
"AI_CHOICES",
|
|
32
|
+
"BANNER",
|
|
33
|
+
"DEFAULT_MISSION_KEY",
|
|
34
|
+
"DEFAULT_TEMPLATE_REPO",
|
|
35
|
+
"MISSION_CHOICES",
|
|
36
|
+
"SCRIPT_TYPE_CHOICES",
|
|
37
|
+
"format_path",
|
|
38
|
+
"ensure_directory",
|
|
39
|
+
"safe_remove",
|
|
40
|
+
"get_platform",
|
|
41
|
+
"run_command",
|
|
42
|
+
"is_git_repo",
|
|
43
|
+
"init_git_repo",
|
|
44
|
+
"get_current_branch",
|
|
45
|
+
"locate_project_root",
|
|
46
|
+
"resolve_template_path",
|
|
47
|
+
"resolve_worktree_aware_feature_dir",
|
|
48
|
+
"get_active_mission_key",
|
|
49
|
+
"check_tool",
|
|
50
|
+
"check_tool_for_tracker",
|
|
51
|
+
"check_all_tools",
|
|
52
|
+
"get_tool_version",
|
|
53
|
+
]
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
"""Agent context file management for updating CLAUDE.md, GEMINI.md, etc."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, List, Optional
|
|
6
|
+
import re
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
# Agent types and their file paths
|
|
10
|
+
AGENT_CONFIGS = {
|
|
11
|
+
"claude": "CLAUDE.md",
|
|
12
|
+
"gemini": "GEMINI.md",
|
|
13
|
+
"copilot": ".github/copilot-instructions.md",
|
|
14
|
+
"cursor": ".cursor/rules/specify-rules.mdc",
|
|
15
|
+
"qwen": "QWEN.md",
|
|
16
|
+
"opencode": "AGENTS.md",
|
|
17
|
+
"codex": "AGENTS.md",
|
|
18
|
+
"windsurf": ".windsurf/rules/specify-rules.md",
|
|
19
|
+
"kilocode": ".kilocode/rules/specify-rules.md",
|
|
20
|
+
"auggie": ".augment/rules/specify-rules.md",
|
|
21
|
+
"roo": ".roo/rules/specify-rules.md",
|
|
22
|
+
"q": "AGENTS.md",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def parse_plan_for_tech_stack(plan_path: Path) -> Dict[str, Optional[str]]:
|
|
27
|
+
"""
|
|
28
|
+
Extract tech stack information from plan.md Technical Context section.
|
|
29
|
+
|
|
30
|
+
Args:
|
|
31
|
+
plan_path: Path to plan.md file
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
Dictionary with language, dependencies, storage, testing, project_type keys
|
|
35
|
+
|
|
36
|
+
Example return:
|
|
37
|
+
{
|
|
38
|
+
"language": "Python 3.11+",
|
|
39
|
+
"dependencies": "Typer, Rich, pathlib, subprocess",
|
|
40
|
+
"storage": "Filesystem only (no database)",
|
|
41
|
+
"testing": "pytest with unit + integration tests",
|
|
42
|
+
"project_type": "Single Python package"
|
|
43
|
+
}
|
|
44
|
+
"""
|
|
45
|
+
if not plan_path.exists():
|
|
46
|
+
raise FileNotFoundError(f"Plan file not found: {plan_path}")
|
|
47
|
+
|
|
48
|
+
content = plan_path.read_text()
|
|
49
|
+
|
|
50
|
+
# Extract fields from Technical Context section using markdown patterns
|
|
51
|
+
def extract_field(pattern: str) -> Optional[str]:
|
|
52
|
+
# Match pattern like "**Language/Version**: Python 3.11+"
|
|
53
|
+
match = re.search(rf"\*\*{pattern}\*\*:\s*(.+?)(?:\n|$)", content, re.MULTILINE)
|
|
54
|
+
if match:
|
|
55
|
+
value = match.group(1).strip()
|
|
56
|
+
# Filter out placeholders
|
|
57
|
+
if value and value != "NEEDS CLARIFICATION" and value != "N/A":
|
|
58
|
+
return value
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
"language": extract_field("Language/Version"),
|
|
63
|
+
"dependencies": extract_field("Primary Dependencies"),
|
|
64
|
+
"storage": extract_field("Storage"),
|
|
65
|
+
"testing": extract_field("Testing"),
|
|
66
|
+
"project_type": extract_field("Project Type"),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def format_technology_stack(tech_stack: Dict[str, Optional[str]], feature_slug: str) -> List[str]:
|
|
71
|
+
"""
|
|
72
|
+
Format tech stack data into markdown bullet points for Active Technologies section.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
tech_stack: Dictionary from parse_plan_for_tech_stack()
|
|
76
|
+
feature_slug: Current feature branch/slug (e.g., "008-unified-python-cli")
|
|
77
|
+
|
|
78
|
+
Returns:
|
|
79
|
+
List of formatted markdown lines
|
|
80
|
+
|
|
81
|
+
Example:
|
|
82
|
+
["- Python 3.11+ (existing spec-kitty requirement) (008-unified-python-cli)",
|
|
83
|
+
"- Filesystem only (no database) (008-unified-python-cli)"]
|
|
84
|
+
"""
|
|
85
|
+
entries = []
|
|
86
|
+
|
|
87
|
+
# Add language + dependencies as one line
|
|
88
|
+
parts = []
|
|
89
|
+
if tech_stack.get("language"):
|
|
90
|
+
parts.append(tech_stack["language"])
|
|
91
|
+
if tech_stack.get("dependencies"):
|
|
92
|
+
parts.append(tech_stack["dependencies"])
|
|
93
|
+
|
|
94
|
+
if parts:
|
|
95
|
+
tech_line = " + ".join(parts)
|
|
96
|
+
entries.append(f"- {tech_line} ({feature_slug})")
|
|
97
|
+
|
|
98
|
+
# Add storage as separate line if present
|
|
99
|
+
if tech_stack.get("storage"):
|
|
100
|
+
entries.append(f"- {tech_stack['storage']} ({feature_slug})")
|
|
101
|
+
|
|
102
|
+
return entries
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def preserve_manual_additions(old_content: str, new_content: str) -> str:
|
|
106
|
+
"""
|
|
107
|
+
Preserve content between <!-- MANUAL ADDITIONS START/END --> markers.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
old_content: Original file content with manual additions
|
|
111
|
+
new_content: New generated content
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Merged content with manual additions from old_content injected into new_content
|
|
115
|
+
|
|
116
|
+
Note:
|
|
117
|
+
If markers are not found in old_content, returns new_content unchanged.
|
|
118
|
+
If markers are not found in new_content, manual section is appended.
|
|
119
|
+
"""
|
|
120
|
+
# Extract manual additions from old content
|
|
121
|
+
start_marker = "<!-- MANUAL ADDITIONS START -->"
|
|
122
|
+
end_marker = "<!-- MANUAL ADDITIONS END -->"
|
|
123
|
+
|
|
124
|
+
# Find manual additions in old content
|
|
125
|
+
start_idx = old_content.find(start_marker)
|
|
126
|
+
end_idx = old_content.find(end_marker)
|
|
127
|
+
|
|
128
|
+
if start_idx == -1 or end_idx == -1:
|
|
129
|
+
# No manual additions to preserve
|
|
130
|
+
return new_content
|
|
131
|
+
|
|
132
|
+
# Extract the manual section (including markers)
|
|
133
|
+
manual_section = old_content[start_idx:end_idx + len(end_marker)]
|
|
134
|
+
|
|
135
|
+
# Find where to inject in new content
|
|
136
|
+
new_start_idx = new_content.find(start_marker)
|
|
137
|
+
new_end_idx = new_content.find(end_marker)
|
|
138
|
+
|
|
139
|
+
if new_start_idx == -1 or new_end_idx == -1:
|
|
140
|
+
# New content doesn't have markers, append at end
|
|
141
|
+
return new_content.rstrip() + "\n\n" + manual_section + "\n"
|
|
142
|
+
|
|
143
|
+
# Replace the section in new content with the preserved manual section
|
|
144
|
+
before = new_content[:new_start_idx]
|
|
145
|
+
after = new_content[new_end_idx + len(end_marker):]
|
|
146
|
+
|
|
147
|
+
return before + manual_section + after
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def update_agent_context(
|
|
151
|
+
agent_type: str,
|
|
152
|
+
tech_stack: Dict[str, Optional[str]],
|
|
153
|
+
feature_slug: str,
|
|
154
|
+
repo_root: Path,
|
|
155
|
+
feature_dir: Optional[Path] = None,
|
|
156
|
+
) -> None:
|
|
157
|
+
"""
|
|
158
|
+
Update agent context file with tech stack from plan.md.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
agent_type: One of the keys in AGENT_CONFIGS (claude, gemini, etc.)
|
|
162
|
+
tech_stack: Dictionary from parse_plan_for_tech_stack()
|
|
163
|
+
feature_slug: Current feature branch/slug
|
|
164
|
+
repo_root: Repository root directory
|
|
165
|
+
feature_dir: Feature directory path (for worktree-local updates)
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
ValueError: If agent_type is not supported
|
|
169
|
+
FileNotFoundError: If agent file doesn't exist
|
|
170
|
+
"""
|
|
171
|
+
if agent_type not in AGENT_CONFIGS:
|
|
172
|
+
raise ValueError(
|
|
173
|
+
f"Unsupported agent type: {agent_type}. "
|
|
174
|
+
f"Supported types: {', '.join(AGENT_CONFIGS.keys())}"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
agent_file_path = repo_root / AGENT_CONFIGS[agent_type]
|
|
178
|
+
|
|
179
|
+
# If it's a worktree-local file, use feature_dir
|
|
180
|
+
if feature_dir and agent_file_path.is_relative_to(repo_root):
|
|
181
|
+
worktree_agent_file = feature_dir / AGENT_CONFIGS[agent_type]
|
|
182
|
+
if worktree_agent_file.exists():
|
|
183
|
+
agent_file_path = worktree_agent_file
|
|
184
|
+
|
|
185
|
+
if not agent_file_path.exists():
|
|
186
|
+
raise FileNotFoundError(f"Agent file not found: {agent_file_path}")
|
|
187
|
+
|
|
188
|
+
# Read existing content
|
|
189
|
+
old_content = agent_file_path.read_text()
|
|
190
|
+
|
|
191
|
+
# Format new tech entries
|
|
192
|
+
new_tech_entries = format_technology_stack(tech_stack, feature_slug)
|
|
193
|
+
|
|
194
|
+
# Prepare change entry for Recent Changes section
|
|
195
|
+
tech_parts = []
|
|
196
|
+
if tech_stack.get("language"):
|
|
197
|
+
tech_parts.append(tech_stack["language"])
|
|
198
|
+
if tech_stack.get("dependencies"):
|
|
199
|
+
tech_parts.append(tech_stack["dependencies"])
|
|
200
|
+
|
|
201
|
+
tech_description = " + ".join(tech_parts) if tech_parts else tech_stack.get("storage", "")
|
|
202
|
+
new_change_entry = f"- {feature_slug}: Added {tech_description}" if tech_description else ""
|
|
203
|
+
|
|
204
|
+
# Process file line by line to update sections
|
|
205
|
+
lines = old_content.splitlines(keepends=True)
|
|
206
|
+
new_lines = []
|
|
207
|
+
|
|
208
|
+
in_tech_section = False
|
|
209
|
+
in_changes_section = False
|
|
210
|
+
tech_entries_added = False
|
|
211
|
+
existing_changes_count = 0
|
|
212
|
+
current_date = datetime.now().strftime("%Y-%m-%d")
|
|
213
|
+
|
|
214
|
+
for line in lines:
|
|
215
|
+
# Handle Active Technologies section
|
|
216
|
+
if line.strip() == "## Active Technologies":
|
|
217
|
+
new_lines.append(line)
|
|
218
|
+
in_tech_section = True
|
|
219
|
+
continue
|
|
220
|
+
|
|
221
|
+
# Handle Recent Changes section - MUST come before generic section exit checks
|
|
222
|
+
if line.strip() == "## Recent Changes":
|
|
223
|
+
# If we were in tech section, close it first
|
|
224
|
+
if in_tech_section and not tech_entries_added and new_tech_entries:
|
|
225
|
+
for entry in new_tech_entries:
|
|
226
|
+
new_lines.append(entry + "\n")
|
|
227
|
+
tech_entries_added = True
|
|
228
|
+
in_tech_section = False
|
|
229
|
+
|
|
230
|
+
new_lines.append(line)
|
|
231
|
+
in_changes_section = True
|
|
232
|
+
# Add new change entry right after heading
|
|
233
|
+
if new_change_entry:
|
|
234
|
+
new_lines.append(new_change_entry + "\n")
|
|
235
|
+
continue
|
|
236
|
+
|
|
237
|
+
# Check if we're exiting tech section
|
|
238
|
+
if in_tech_section and line.strip().startswith("##"):
|
|
239
|
+
# Add new tech entries before closing section
|
|
240
|
+
if not tech_entries_added and new_tech_entries:
|
|
241
|
+
for entry in new_tech_entries:
|
|
242
|
+
new_lines.append(entry + "\n")
|
|
243
|
+
tech_entries_added = True
|
|
244
|
+
new_lines.append(line)
|
|
245
|
+
in_tech_section = False
|
|
246
|
+
continue
|
|
247
|
+
|
|
248
|
+
# Check if we're exiting changes section
|
|
249
|
+
if in_changes_section and line.strip().startswith("##"):
|
|
250
|
+
new_lines.append(line)
|
|
251
|
+
in_changes_section = False
|
|
252
|
+
continue
|
|
253
|
+
|
|
254
|
+
# In changes section: keep only first 2 existing changes, skip empty lines
|
|
255
|
+
if in_changes_section:
|
|
256
|
+
if line.strip().startswith("- "):
|
|
257
|
+
if existing_changes_count < 2:
|
|
258
|
+
new_lines.append(line)
|
|
259
|
+
existing_changes_count += 1
|
|
260
|
+
continue
|
|
261
|
+
elif line.strip() == "":
|
|
262
|
+
# Skip empty lines in changes section
|
|
263
|
+
continue
|
|
264
|
+
|
|
265
|
+
# Update last updated timestamp
|
|
266
|
+
if "**Last updated**:" in line or "*Last updated*:" in line:
|
|
267
|
+
# Replace date in format YYYY-MM-DD
|
|
268
|
+
line = re.sub(r'\d{4}-\d{2}-\d{2}', current_date, line)
|
|
269
|
+
|
|
270
|
+
new_lines.append(line)
|
|
271
|
+
|
|
272
|
+
# Post-loop: if still in tech section and haven't added entries
|
|
273
|
+
if in_tech_section and not tech_entries_added and new_tech_entries:
|
|
274
|
+
for entry in new_tech_entries:
|
|
275
|
+
new_lines.append(entry + "\n")
|
|
276
|
+
|
|
277
|
+
new_content = "".join(new_lines)
|
|
278
|
+
|
|
279
|
+
# Preserve manual additions
|
|
280
|
+
final_content = preserve_manual_additions(old_content, new_content)
|
|
281
|
+
|
|
282
|
+
# Write updated content
|
|
283
|
+
agent_file_path.write_text(final_content)
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
def get_supported_agent_types() -> List[str]:
|
|
287
|
+
"""Return list of supported agent types."""
|
|
288
|
+
return list(AGENT_CONFIGS.keys())
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def get_agent_file_path(agent_type: str, repo_root: Path) -> Path:
|
|
292
|
+
"""
|
|
293
|
+
Get the file path for a specific agent type.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
agent_type: One of the keys in AGENT_CONFIGS
|
|
297
|
+
repo_root: Repository root directory
|
|
298
|
+
|
|
299
|
+
Returns:
|
|
300
|
+
Path to the agent configuration file
|
|
301
|
+
|
|
302
|
+
Raises:
|
|
303
|
+
ValueError: If agent_type is not supported
|
|
304
|
+
"""
|
|
305
|
+
if agent_type not in AGENT_CONFIGS:
|
|
306
|
+
raise ValueError(
|
|
307
|
+
f"Unsupported agent type: {agent_type}. "
|
|
308
|
+
f"Supported types: {', '.join(AGENT_CONFIGS.keys())}"
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
return repo_root / AGENT_CONFIGS[agent_type]
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"""Configuration constants shared across the Spec Kitty CLI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
AI_CHOICES = {
|
|
6
|
+
"copilot": "GitHub Copilot",
|
|
7
|
+
"claude": "Claude Code",
|
|
8
|
+
"gemini": "Gemini CLI",
|
|
9
|
+
"cursor": "Cursor",
|
|
10
|
+
"qwen": "Qwen Code",
|
|
11
|
+
"opencode": "opencode",
|
|
12
|
+
"codex": "Codex CLI",
|
|
13
|
+
"windsurf": "Windsurf",
|
|
14
|
+
"kilocode": "Kilo Code",
|
|
15
|
+
"auggie": "Auggie CLI",
|
|
16
|
+
"roo": "Roo Code",
|
|
17
|
+
"q": "Amazon Q Developer CLI",
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
MISSION_CHOICES = {
|
|
21
|
+
"software-dev": "Software Dev Kitty",
|
|
22
|
+
"research": "Deep Research Kitty",
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
DEFAULT_MISSION_KEY = "software-dev"
|
|
26
|
+
|
|
27
|
+
AGENT_TOOL_REQUIREMENTS: dict[str, tuple[str, str]] = {
|
|
28
|
+
"claude": ("claude", "https://docs.anthropic.com/en/docs/claude-code/setup"),
|
|
29
|
+
"gemini": ("gemini", "https://github.com/google-gemini/gemini-cli"),
|
|
30
|
+
"qwen": ("qwen", "https://github.com/QwenLM/qwen-code"),
|
|
31
|
+
"opencode": ("opencode", "https://opencode.ai"),
|
|
32
|
+
"codex": ("codex", "https://github.com/openai/codex"),
|
|
33
|
+
"auggie": ("auggie", "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli"),
|
|
34
|
+
"q": ("q", "https://aws.amazon.com/developer/learning/q-developer-cli/"),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"}
|
|
38
|
+
|
|
39
|
+
DEFAULT_TEMPLATE_REPO = "spec-kitty/spec-kitty"
|
|
40
|
+
|
|
41
|
+
# IDE-integrated agents that don't require CLI installation
|
|
42
|
+
IDE_AGENTS = {"cursor", "windsurf", "copilot", "kilocode"}
|
|
43
|
+
|
|
44
|
+
AGENT_COMMAND_CONFIG: dict[str, dict[str, str]] = {
|
|
45
|
+
"claude": {"dir": ".claude/commands", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
46
|
+
"gemini": {"dir": ".gemini/commands", "ext": "toml", "arg_format": "{{args}}"},
|
|
47
|
+
"copilot": {"dir": ".github/prompts", "ext": "prompt.md", "arg_format": "$ARGUMENTS"},
|
|
48
|
+
"cursor": {"dir": ".cursor/commands", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
49
|
+
"qwen": {"dir": ".qwen/commands", "ext": "toml", "arg_format": "{{args}}"},
|
|
50
|
+
"opencode": {"dir": ".opencode/command", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
51
|
+
"windsurf": {"dir": ".windsurf/workflows", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
52
|
+
"codex": {"dir": ".codex/prompts", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
53
|
+
"kilocode": {"dir": ".kilocode/workflows", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
54
|
+
"auggie": {"dir": ".augment/commands", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
55
|
+
"roo": {"dir": ".roo/commands", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
56
|
+
"q": {"dir": ".amazonq/prompts", "ext": "md", "arg_format": "$ARGUMENTS"},
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
BANNER = """
|
|
60
|
+
`````````````````````````````````````````````````````````
|
|
61
|
+
|
|
62
|
+
▄█▄_ ╓▄█_
|
|
63
|
+
▐█ └▀█▄_ ▄█▀▀ ╙█
|
|
64
|
+
█" `▀█▄ ▄█▀ █▌
|
|
65
|
+
▐█ ▀█▄▄▄██████████▄▄▄█" ▐█
|
|
66
|
+
║█ "` ╟█ ╫▌ █" '" █
|
|
67
|
+
║█ ▀ ╚▀ ▀ J█
|
|
68
|
+
█ █▌
|
|
69
|
+
█▀ ,▄█████▄ ,▄█████▄_ █▌
|
|
70
|
+
█▌ ▄█" "██ ╓█▀ `▀█_ █▌
|
|
71
|
+
▐█__▐▌ ▄██▄ ╙█_____╒█ ▄██, '█__'█
|
|
72
|
+
█▀▀▀█M ████ █▀╙\"\"\"██ ▐████ █▀▀"█▌
|
|
73
|
+
█─ ╟█ ╙▀▀" ██ █╕ ╙▀▀ ╓█ ║▌
|
|
74
|
+
╓▄▄▄▄█▌,_ ╙█▄_ _▄█▀╒██████ ▀█╥ ▄█▀ __,██▄▄▄▄
|
|
75
|
+
╚█'`" `╙▀▀▀▀▀" `▀██▀ "▀▀▀▀▀" ""▐█
|
|
76
|
+
_,▄▄███▀ █▌ ▀▀███▄▄,_
|
|
77
|
+
▀"` ▀█_ '▀█▄▄█▀▀█▄▄█▀ ▄█" '"▀"
|
|
78
|
+
╙██_ ▄█▀
|
|
79
|
+
└▀█▄_ ,▓█▀
|
|
80
|
+
└▀▀██▄,__ __╓▄██▀▀
|
|
81
|
+
`"▀▀▀▀▀▀▀▀▀▀▀╙"`
|
|
82
|
+
|
|
83
|
+
`````````````````````````````````````````````````````````
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
__all__ = [
|
|
87
|
+
"AI_CHOICES",
|
|
88
|
+
"MISSION_CHOICES",
|
|
89
|
+
"DEFAULT_MISSION_KEY",
|
|
90
|
+
"AGENT_TOOL_REQUIREMENTS",
|
|
91
|
+
"SCRIPT_TYPE_CHOICES",
|
|
92
|
+
"DEFAULT_TEMPLATE_REPO",
|
|
93
|
+
"AGENT_COMMAND_CONFIG",
|
|
94
|
+
"IDE_AGENTS",
|
|
95
|
+
"BANNER",
|
|
96
|
+
]
|