2026-05-26 21:06:35 +08:00
"""specify init command."""
from __future__ import annotations
import os
import shutil
import sys
from pathlib import Path
from typing import Any
import typer
from rich.live import Live
from rich.panel import Panel
from .._agent_config import (
AGENT_CONFIG ,
DEFAULT_INIT_INTEGRATION ,
SCRIPT_TYPE_CHOICES ,
)
from .._assets import (
_locate_bundled_extension ,
_locate_bundled_preset ,
_locate_bundled_workflow ,
get_speckit_version ,
)
from .._console import StepTracker , console , select_with_arrows , show_banner
from .._utils import check_tool , init_git_repo , is_git_repo
def _stdin_is_interactive () -> bool :
return sys . stdin . isatty ()
def ensure_constitution_from_template (
project_path : Path , tracker : StepTracker | None = None
) -> None :
"""Copy constitution template to memory if it doesn't exist."""
memory_constitution = project_path / ".specify" / "memory" / "constitution.md"
template_constitution = project_path / ".specify" / "templates" / "constitution-template.md"
if memory_constitution . exists ():
if tracker :
tracker . add ( "constitution" , "Constitution setup" )
tracker . skip ( "constitution" , "existing file preserved" )
return
if not template_constitution . exists ():
if tracker :
tracker . add ( "constitution" , "Constitution setup" )
tracker . error ( "constitution" , "template not found" )
return
try :
memory_constitution . parent . mkdir ( parents = True , exist_ok = True )
shutil . copy2 ( template_constitution , memory_constitution )
if tracker :
tracker . add ( "constitution" , "Constitution setup" )
tracker . complete ( "constitution" , "copied from template" )
else :
console . print ( "[cyan]Initialized constitution from template[/cyan]" )
except Exception as e :
if tracker :
tracker . add ( "constitution" , "Constitution setup" )
tracker . error ( "constitution" , str ( e ))
else :
console . print ( f "[yellow]Warning: Could not initialize constitution: { e } [/yellow]" )
def register ( app : typer . Typer ) -> None :
@app.command ()
def init (
project_name : str = typer . Argument ( None , help = "Name for your new project directory (optional if using --here, or use '.' for current directory)" ),
script_type : str = typer . Option ( None , "--script" , help = "Script type to use: sh or ps" ),
ignore_agent_tools : bool = typer . Option ( False , "--ignore-agent-tools" , help = "Skip checks for coding agent tools like Claude Code" ),
no_git : bool = typer . Option ( False , "--no-git" , help = "Skip git repository initialization" ),
here : bool = typer . Option ( False , "--here" , help = "Initialize project in the current directory instead of creating a new one" ),
force : bool = typer . Option ( False , "--force" , help = "Force merge/overwrite when using --here (skip confirmation)" ),
skip_tls : bool = typer . Option ( False , "--skip-tls" , help = "Deprecated (no-op). Previously: skip SSL/TLS verification." , hidden = True ),
debug : bool = typer . Option ( False , "--debug" , help = "Deprecated. Previously: show verbose diagnostic output; currently only prints additional diagnostic details on failure." , hidden = True ),
github_token : str = typer . Option ( None , "--github-token" , help = "Deprecated (no-op). Previously: GitHub token for API requests." , hidden = True ),
offline : bool = typer . Option ( False , "--offline" , help = "Deprecated (no-op). All scaffolding now uses bundled assets." , hidden = True ),
preset : str = typer . Option ( None , "--preset" , help = "Install a preset during initialization (by preset ID)" ),
branch_numbering : str = typer . Option ( None , "--branch-numbering" , help = "Branch numbering strategy: 'sequential' (001, 002, …, 1000, … — expands past 999 automatically) or 'timestamp' (YYYYMMDD-HHMMSS)" ),
2026-06-05 14:56:28 -05:00
integration : str = typer . Option ( None , "--integration" , help = "AI coding agent integration to use (e.g. --integration copilot). See 'specify check' for available integrations." ),
2026-05-26 21:06:35 +08:00
integration_options : str = typer . Option ( None , "--integration-options" , help = 'Options for the integration (e.g. --integration-options="--commands-dir .myagent/cmds")' ),
):
"""
Initialize a new Specify project.
Project files are scaffolded from assets bundled inside the specify-cli
package, so initialization does not need network access and templates
match the installed CLI version.
This command will:
1. Check that required tools are installed (git is optional)
2. Let you choose your coding agent integration, or default to Copilot
in non-interactive sessions
3. Install bundled Spec Kit templates, scripts, workflow, and shared
project infrastructure
4. Initialize a fresh git repository (if not --no-git and no existing repo)
5. Set up coding agent integration commands and optional presets
Examples:
specify init my-project
specify init my-project --integration claude
specify init my-project --integration copilot --no-git
specify init --ignore-agent-tools my-project
specify init . --integration claude # Initialize in current directory
specify init . # Initialize in current directory (interactive integration selection)
specify init --here --integration claude # Alternative syntax for current directory
specify init --here --integration codex --integration-options="--skills"
specify init --here --integration codebuddy
specify init --here --integration vibe # Initialize with Mistral Vibe support
specify init --here
specify init --here --force # Skip confirmation when current directory not empty
specify init my-project --integration claude # Claude installs skills by default
specify init --here --integration gemini
specify init my-project --integration generic --integration-options="--commands-dir .myagent/commands/" # Bring your own agent; requires --commands-dir
specify init my-project --integration claude --preset healthcare-compliance # With preset
"""
# Lazy imports to avoid circular dependency — __init__.py imports this module
from .. import (
_install_shared_infra_or_exit ,
_print_cli_warning ,
2026-05-30 06:37:18 -05:00
_update_agent_context_config_file ,
2026-05-26 21:06:35 +08:00
ensure_executable_scripts ,
save_init_options ,
)
2026-06-03 01:21:19 +08:00
from ..integrations._commands import (
_parse_integration_options ,
_write_integration_json ,
)
2026-05-26 21:06:35 +08:00
from ..integration_runtime import with_integration_setting as _with_integration_setting
show_banner ()
from ..integrations import INTEGRATION_REGISTRY , get_integration
if integration :
resolved_integration = get_integration ( integration )
if not resolved_integration :
console . print ( f "[red]Error:[/red] Unknown integration: ' { integration } '" )
available = ", " . join ( sorted ( INTEGRATION_REGISTRY ))
console . print ( f "[yellow]Available integrations:[/yellow] { available } " )
raise typer . Exit ( 1 )
if no_git :
console . print (
"[yellow]⚠️ --no-git is deprecated and will be removed in v0.10.0.[/yellow] \n "
"[yellow]The git extension will no longer be enabled by default "
"— use the [bold]specify extension[/bold] commands to install or enable the git extension if needed.[/yellow]"
)
if project_name == "." :
here = True
project_name = None
if here and project_name :
console . print ( "[red]Error:[/red] Cannot specify both project name and --here flag" )
raise typer . Exit ( 1 )
if not here and not project_name :
console . print ( "[red]Error:[/red] Must specify either a project name, use '.' for current directory, or use --here flag" )
raise typer . Exit ( 1 )
BRANCH_NUMBERING_CHOICES = { "sequential" , "timestamp" }
if branch_numbering and branch_numbering not in BRANCH_NUMBERING_CHOICES :
console . print ( f "[red]Error:[/red] Invalid --branch-numbering value ' { branch_numbering } '. Choose from: { ', ' . join ( sorted ( BRANCH_NUMBERING_CHOICES )) } " )
raise typer . Exit ( 1 )
dir_existed_before = False
if here :
project_name = Path . cwd () . name
project_path = Path . cwd ()
dir_existed_before = True
existing_items = list ( project_path . iterdir ())
if existing_items :
console . print ( f "[yellow]Warning:[/yellow] Current directory is not empty ( { len ( existing_items ) } items)" )
console . print ( "[yellow]Template files will be merged with existing content and may overwrite existing files[/yellow]" )
if force :
console . print ( "[cyan]--force supplied: skipping confirmation and proceeding with merge[/cyan]" )
else :
response = typer . confirm ( "Do you want to continue?" )
if not response :
console . print ( "[yellow]Operation cancelled[/yellow]" )
raise typer . Exit ( 0 )
else :
project_path = Path ( project_name ) . resolve ()
dir_existed_before = project_path . exists ()
if project_path . exists ():
if not project_path . is_dir ():
console . print ( f "[red]Error:[/red] ' { project_name } ' exists but is not a directory." )
raise typer . Exit ( 1 )
existing_items = list ( project_path . iterdir ())
if force :
if existing_items :
console . print ( f "[yellow]Warning:[/yellow] Directory ' { project_name } ' is not empty ( { len ( existing_items ) } items)" )
console . print ( "[yellow]Template files will be merged with existing content and may overwrite existing files[/yellow]" )
console . print ( f "[cyan]--force supplied: merging into existing directory '[cyan] { project_name } [/cyan]'[/cyan]" )
else :
error_panel = Panel (
f "Directory already exists: '[cyan] { project_name } [/cyan]' \n "
"Please choose a different project name or remove the existing directory. \n "
"Use [bold]--force[/bold] to merge into the existing directory." ,
title = "[red]Directory Conflict[/red]" ,
border_style = "red" ,
padding = ( 1 , 2 )
)
console . print ()
console . print ( error_panel )
raise typer . Exit ( 1 )
2026-06-05 14:56:28 -05:00
if integration :
if integration not in AGENT_CONFIG :
console . print ( f "[red]Error:[/red] Invalid integration ' { integration } '. Choose from: { ', ' . join ( AGENT_CONFIG . keys ()) } " )
2026-05-26 21:06:35 +08:00
raise typer . Exit ( 1 )
2026-06-05 14:56:28 -05:00
selected_ai = integration
2026-05-26 21:06:35 +08:00
elif not _stdin_is_interactive ():
console . print (
f "[dim]Non-interactive session detected: defaulting to ' { DEFAULT_INIT_INTEGRATION } '. "
"Use --integration to choose a different agent.[/dim]"
)
selected_ai = DEFAULT_INIT_INTEGRATION
else :
ai_choices = { key : config [ "name" ] for key , config in AGENT_CONFIG . items ()}
selected_ai = select_with_arrows (
ai_choices ,
"Choose your coding agent integration:" ,
DEFAULT_INIT_INTEGRATION ,
)
2026-06-05 14:56:28 -05:00
if not integration :
2026-05-26 21:06:35 +08:00
resolved_integration = get_integration ( selected_ai )
if not resolved_integration :
console . print ( f "[red]Error:[/red] Unknown agent ' { selected_ai } '" )
raise typer . Exit ( 1 )
if selected_ai == "generic" and not integration_options :
2026-06-05 14:56:28 -05:00
console . print ( "[red]Error:[/red] --integration generic requires --integration-options with --commands-dir" )
console . print ( '[dim]Example: specify init my-project --integration generic --integration-options="--commands-dir .myagent/commands/"[/dim]' )
raise typer . Exit ( 1 )
2026-05-26 21:06:35 +08:00
current_dir = Path . cwd ()
setup_lines = [
"[cyan]Specify Project Setup[/cyan]" ,
"" ,
f " { 'Project' : <15 } [green] { project_path . name } [/green]" ,
f " { 'Working Path' : <15 } [dim] { current_dir } [/dim]" ,
]
if not here :
setup_lines . append ( f " { 'Target Path' : <15 } [dim] { project_path } [/dim]" )
console . print ( Panel ( " \n " . join ( setup_lines ), border_style = "cyan" , padding = ( 1 , 2 )))
should_init_git = False
if not no_git :
should_init_git = check_tool ( "git" )
if not should_init_git :
console . print ( "[yellow]Git not found - will skip repository initialization[/yellow]" )
if not ignore_agent_tools :
agent_config = AGENT_CONFIG . get ( selected_ai )
if agent_config and agent_config [ "requires_cli" ]:
install_url = agent_config [ "install_url" ]
if not check_tool ( selected_ai ):
error_panel = Panel (
f "[cyan] { selected_ai } [/cyan] not found \n "
f "Install from: [cyan] { install_url } [/cyan] \n "
f " { agent_config [ 'name' ] } is required to continue with this project type. \n\n "
"Tip: Use [cyan]--ignore-agent-tools[/cyan] to skip this check" ,
title = "[red]Agent Detection Error[/red]" ,
border_style = "red" ,
padding = ( 1 , 2 )
)
console . print ()
console . print ( error_panel )
raise typer . Exit ( 1 )
if script_type :
if script_type not in SCRIPT_TYPE_CHOICES :
console . print ( f "[red]Error:[/red] Invalid script type ' { script_type } '. Choose from: { ', ' . join ( SCRIPT_TYPE_CHOICES . keys ()) } " )
raise typer . Exit ( 1 )
selected_script = script_type
else :
default_script = "ps" if os . name == "nt" else "sh"
if _stdin_is_interactive ():
selected_script = select_with_arrows ( SCRIPT_TYPE_CHOICES , "Choose script type (or press Enter)" , default_script )
else :
selected_script = default_script
console . print ( f "[cyan]Selected coding agent integration:[/cyan] { selected_ai } " )
console . print ( f "[cyan]Selected script type:[/cyan] { selected_script } " )
tracker = StepTracker ( "Initialize Specify Project" )
tracker . add ( "precheck" , "Check required tools" )
tracker . complete ( "precheck" , "ok" )
tracker . add ( "ai-select" , "Select coding agent integration" )
tracker . complete ( "ai-select" , f " { selected_ai } " )
tracker . add ( "script-select" , "Select script type" )
tracker . complete ( "script-select" , selected_script )
tracker . add ( "integration" , "Install integration" )
tracker . add ( "shared-infra" , "Install shared infrastructure" )
for key , label in [
( "chmod" , "Ensure scripts executable" ),
( "constitution" , "Constitution setup" ),
( "git" , "Install git extension" ),
( "workflow" , "Install bundled workflow" ),
2026-05-30 06:37:18 -05:00
( "agent-context" , "Install agent-context extension" ),
2026-05-26 21:06:35 +08:00
( "final" , "Finalize" ),
]:
tracker . add ( key , label )
git_default_notice = False
with Live ( tracker . render (), console = console , refresh_per_second = 8 , transient = True ) as live :
tracker . attach_refresh ( lambda : live . update ( tracker . render ()))
try :
from ..integrations.manifest import IntegrationManifest
tracker . start ( "integration" )
manifest = IntegrationManifest (
resolved_integration . key , project_path , version = get_speckit_version ()
)
integration_parsed_options : dict [ str , Any ] = {}
if integration_options :
extra = _parse_integration_options ( resolved_integration , integration_options )
if extra :
integration_parsed_options . update ( extra )
resolved_integration . setup (
project_path , manifest ,
parsed_options = integration_parsed_options or None ,
script_type = selected_script ,
raw_options = integration_options ,
)
manifest . save ()
integration_settings = _with_integration_setting (
{},
resolved_integration . key ,
resolved_integration ,
script_type = selected_script ,
raw_options = integration_options ,
parsed_options = integration_parsed_options or None ,
)
_write_integration_json (
project_path ,
resolved_integration . key ,
[ resolved_integration . key ],
integration_settings ,
)
tracker . complete ( "integration" , resolved_integration . config . get ( "name" , resolved_integration . key ))
tracker . start ( "shared-infra" )
_install_shared_infra_or_exit (
project_path ,
selected_script ,
tracker = tracker ,
force = force ,
invoke_separator = resolved_integration . effective_invoke_separator ( integration_parsed_options ),
)
tracker . complete ( "shared-infra" , f "scripts ( { selected_script } ) + templates" )
ensure_constitution_from_template ( project_path , tracker = tracker )
if not no_git :
tracker . start ( "git" )
git_messages = []
git_has_error = False
if is_git_repo ( project_path ):
git_messages . append ( "existing repo detected" )
elif should_init_git :
success , error_msg = init_git_repo ( project_path , quiet = True )
if success :
git_messages . append ( "initialized" )
else :
git_has_error = True
if error_msg :
sanitized = error_msg . replace ( ' \n ' , ' ' ) . strip ()
git_messages . append ( f "init failed: { sanitized [: 120 ] } " )
else :
git_messages . append ( "init failed" )
else :
git_messages . append ( "git not available" )
try :
from ..extensions import ExtensionManager
bundled_path = _locate_bundled_extension ( "git" )
if bundled_path :
manager = ExtensionManager ( project_path )
if manager . registry . is_installed ( "git" ):
git_messages . append ( "extension already installed" )
else :
manager . install_from_directory (
bundled_path , get_speckit_version ()
)
git_default_notice = True
git_messages . append ( "extension installed" )
else :
git_has_error = True
git_messages . append ( "bundled extension not found" )
except Exception as ext_err :
git_has_error = True
sanitized_ext = str ( ext_err ) . replace ( ' \n ' , ' ' ) . strip ()
git_messages . append (
f "extension install failed: { sanitized_ext [: 120 ] } "
)
summary = "; " . join ( git_messages )
if git_has_error :
tracker . error ( "git" , summary )
else :
tracker . complete ( "git" , summary )
else :
tracker . skip ( "git" , "--no-git flag" )
try :
bundled_wf = _locate_bundled_workflow ( "speckit" )
if bundled_wf :
from ..workflows.catalog import WorkflowRegistry
from ..workflows.engine import WorkflowDefinition
wf_registry = WorkflowRegistry ( project_path )
if wf_registry . is_installed ( "speckit" ):
tracker . complete ( "workflow" , "already installed" )
else :
import shutil as _shutil
dest_wf = project_path / ".specify" / "workflows" / "speckit"
dest_wf . mkdir ( parents = True , exist_ok = True )
_shutil . copy2 (
bundled_wf / "workflow.yml" ,
dest_wf / "workflow.yml" ,
)
definition = WorkflowDefinition . from_yaml ( dest_wf / "workflow.yml" )
wf_registry . add ( "speckit" , {
"name" : definition . name ,
"version" : definition . version ,
"description" : definition . description ,
"source" : "bundled" ,
})
tracker . complete ( "workflow" , "speckit installed" )
else :
tracker . skip ( "workflow" , "bundled workflow not found" )
except Exception as wf_err :
sanitized_wf = str ( wf_err ) . replace ( ' \n ' , ' ' ) . strip ()
tracker . error ( "workflow" , f "install failed: { sanitized_wf [: 120 ] } " )
init_opts = {
"ai" : selected_ai ,
"integration" : resolved_integration . key ,
"branch_numbering" : branch_numbering or "sequential" ,
"here" : here ,
"script" : selected_script ,
"speckit_version" : get_speckit_version (),
}
from ..integrations.base import SkillsIntegration as _SkillsPersist
if isinstance ( resolved_integration , _SkillsPersist ) or getattr ( resolved_integration , "_skills_mode" , False ):
init_opts [ "ai_skills" ] = True
save_init_options ( project_path , init_opts )
2026-05-30 06:37:18 -05:00
# --- agent-context extension (bundled, auto-installed) ---
# Installed after init-options.json is written so that skill
# registration can read ai_skills + integration key.
try :
from ..extensions import ExtensionManager as _ExtMgr
bundled_ac = _locate_bundled_extension ( "agent-context" )
if bundled_ac :
ac_mgr = _ExtMgr ( project_path )
if ac_mgr . registry . is_installed ( "agent-context" ):
tracker . complete ( "agent-context" , "already installed" )
else :
ac_mgr . install_from_directory (
bundled_ac , get_speckit_version ()
)
tracker . complete ( "agent-context" , "extension installed" )
else :
from ..extensions import REINSTALL_COMMAND as _ac_reinstall
tracker . error (
"agent-context" ,
f "bundled extension not found — installation may be "
f "incomplete. Run: { _ac_reinstall } " ,
)
except Exception as ac_err :
sanitized_ac = str ( ac_err ) . replace ( ' \n ' , ' ' ) . strip ()
tracker . error (
"agent-context" ,
f "extension install failed: { sanitized_ac [: 120 ] } " ,
)
# Write context_file to the agent-context extension config
# AFTER the extension install (which copies the template config
# with an empty context_file).
if resolved_integration . context_file :
_update_agent_context_config_file (
project_path ,
resolved_integration . context_file ,
preserve_markers = True ,
)
ensure_executable_scripts ( project_path , tracker = tracker )
2026-05-26 21:06:35 +08:00
if preset :
try :
from ..presets import PresetManager , PresetCatalog , PresetError
preset_manager = PresetManager ( project_path )
speckit_ver = get_speckit_version ()
local_path = Path ( preset ) . resolve ()
if local_path . is_dir () and ( local_path / "preset.yml" ) . exists ():
preset_manager . install_from_directory ( local_path , speckit_ver )
else :
bundled_path = _locate_bundled_preset ( preset )
if bundled_path :
preset_manager . install_from_directory ( bundled_path , speckit_ver )
else :
preset_catalog = PresetCatalog ( project_path )
pack_info = preset_catalog . get_pack_info ( preset )
if not pack_info :
console . print ( f "[yellow]Warning:[/yellow] Preset ' { preset } ' not found in catalog. Skipping." )
elif pack_info . get ( "bundled" ) and not pack_info . get ( "download_url" ):
from ..extensions import REINSTALL_COMMAND
console . print (
f "[yellow]Warning:[/yellow] Preset ' { preset } ' is bundled with spec-kit "
f "but could not be found in the installed package."
)
console . print (
"This usually means the spec-kit installation is incomplete or corrupted."
)
console . print ( f "Try reinstalling: { REINSTALL_COMMAND } " )
else :
zip_path = None
try :
zip_path = preset_catalog . download_pack ( preset )
preset_manager . install_from_zip ( zip_path , speckit_ver )
except PresetError as preset_err :
_print_cli_warning (
"install" ,
"preset" ,
preset ,
preset_err ,
continuing = "Continuing without the optional preset." ,
)
finally :
if zip_path is not None :
try :
zip_path . unlink ( missing_ok = True )
except OSError :
pass
except Exception as preset_err :
_print_cli_warning (
"install" ,
"preset" ,
preset ,
preset_err ,
continuing = "Continuing without the optional preset." ,
)
tracker . complete ( "final" , "project ready" )
except ( typer . Exit , SystemExit ):
raise
except Exception as e :
tracker . error ( "final" , str ( e ))
console . print ( Panel ( f "Initialization failed: { e } " , title = "Failure" , border_style = "red" ))
if debug :
_env_pairs = [
( "Python" , sys . version . split ()[ 0 ]),
( "Platform" , sys . platform ),
( "CWD" , str ( Path . cwd ())),
]
_label_width = max ( len ( k ) for k , _ in _env_pairs )
env_lines = [ f " { k . ljust ( _label_width ) } → [bright_black] { v } [/bright_black]" for k , v in _env_pairs ]
console . print ( Panel ( " \n " . join ( env_lines ), title = "Debug Environment" , border_style = "magenta" ))
if not here and project_path . exists () and not dir_existed_before :
shutil . rmtree ( project_path )
raise typer . Exit ( 1 )
finally :
pass
console . print ( tracker . render ())
console . print ( " \n [bold green]Project ready.[/bold green]" )
agent_config = AGENT_CONFIG . get ( selected_ai )
if agent_config :
2026-06-05 14:56:28 -05:00
agent_folder = agent_config [ "folder" ] or integration_parsed_options . get ( "commands_dir" )
2026-05-26 21:06:35 +08:00
if agent_folder :
security_notice = Panel (
f "Some agents may store credentials, auth tokens, or other identifying and private artifacts in the agent folder within your project. \n "
f "Consider adding [cyan] { agent_folder } [/cyan] (or parts of it) to [cyan].gitignore[/cyan] to prevent accidental credential leakage." ,
title = "[yellow]Agent Folder Security[/yellow]" ,
border_style = "yellow" ,
padding = ( 1 , 2 )
)
console . print ()
console . print ( security_notice )
if git_default_notice :
default_change_notice = Panel (
"The git extension is currently enabled by default during [bold]specify init[/bold]. \n "
"Starting in [bold]v0.10.0[/bold], this will require explicit opt-in. \n "
"Use [bold]specify extension add git[/bold] after init when needed." ,
title = "[yellow]Notice: Git Default Changing[/yellow]" ,
border_style = "yellow" ,
padding = ( 1 , 2 ),
)
console . print ()
console . print ( default_change_notice )
steps_lines = []
if not here :
steps_lines . append ( f "1. Go to the project folder: [cyan]cd { project_name } [/cyan]" )
step_num = 2
else :
steps_lines . append ( "1. You're already in the project directory!" )
step_num = 2
from ..integrations.base import SkillsIntegration as _SkillsInt
_is_skills_integration = isinstance ( resolved_integration , _SkillsInt ) or getattr ( resolved_integration , "_skills_mode" , False )
2026-06-05 14:56:28 -05:00
codex_skill_mode = selected_ai == "codex" and _is_skills_integration
claude_skill_mode = selected_ai == "claude" and _is_skills_integration
2026-05-26 21:06:35 +08:00
kimi_skill_mode = selected_ai == "kimi"
agy_skill_mode = selected_ai == "agy" and _is_skills_integration
trae_skill_mode = selected_ai == "trae"
2026-06-05 14:56:28 -05:00
cursor_agent_skill_mode = selected_ai == "cursor-agent" and _is_skills_integration
2026-05-26 21:06:35 +08:00
copilot_skill_mode = selected_ai == "copilot" and _is_skills_integration
devin_skill_mode = selected_ai == "devin"
2026-06-01 13:20:48 -03:00
cline_skill_mode = selected_ai == "cline"
2026-05-26 21:06:35 +08:00
native_skill_mode = codex_skill_mode or claude_skill_mode or kimi_skill_mode or agy_skill_mode or trae_skill_mode or cursor_agent_skill_mode or copilot_skill_mode or devin_skill_mode
2026-06-05 14:56:28 -05:00
if codex_skill_mode :
2026-05-26 21:06:35 +08:00
steps_lines . append ( f " { step_num } . Start Codex in this project directory; spec-kit skills were installed to [cyan].agents/skills[/cyan]" )
step_num += 1
2026-06-05 14:56:28 -05:00
if claude_skill_mode :
2026-05-26 21:06:35 +08:00
steps_lines . append ( f " { step_num } . Start Claude in this project directory; spec-kit skills were installed to [cyan].claude/skills[/cyan]" )
step_num += 1
2026-06-05 14:56:28 -05:00
if cursor_agent_skill_mode :
2026-05-26 21:06:35 +08:00
steps_lines . append ( f " { step_num } . Start Cursor Agent in this project directory; spec-kit skills were installed to [cyan].cursor/skills[/cyan]" )
step_num += 1
if devin_skill_mode :
steps_lines . append ( f " { step_num } . Start Devin in this project directory; spec-kit skills were installed to [cyan].devin/skills[/cyan]" )
step_num += 1
usage_label = "skills" if native_skill_mode else "slash commands"
def _display_cmd ( name : str ) -> str :
if codex_skill_mode or agy_skill_mode or trae_skill_mode :
return f "$speckit- { name } "
if claude_skill_mode :
return f "/speckit- { name } "
if kimi_skill_mode :
return f "/skill:speckit- { name } "
2026-06-01 13:20:48 -03:00
if cursor_agent_skill_mode or copilot_skill_mode or devin_skill_mode or cline_skill_mode :
2026-05-26 21:06:35 +08:00
return f "/speckit- { name } "
return f "/speckit. { name } "
steps_lines . append ( f " { step_num } . Start using { usage_label } with your coding agent:" )
steps_lines . append ( f " { step_num } .1 [cyan] { _display_cmd ( 'constitution' ) } [/] - Establish project principles" )
steps_lines . append ( f " { step_num } .2 [cyan] { _display_cmd ( 'specify' ) } [/] - Create baseline specification" )
steps_lines . append ( f " { step_num } .3 [cyan] { _display_cmd ( 'plan' ) } [/] - Create implementation plan" )
steps_lines . append ( f " { step_num } .4 [cyan] { _display_cmd ( 'tasks' ) } [/] - Generate actionable tasks" )
steps_lines . append ( f " { step_num } .5 [cyan] { _display_cmd ( 'implement' ) } [/] - Execute implementation" )
steps_panel = Panel ( " \n " . join ( steps_lines ), title = "Next Steps" , border_style = "cyan" , padding = ( 1 , 2 ))
console . print ()
console . print ( steps_panel )
enhancement_intro = (
"Optional skills that you can use for your specs [bright_black](improve quality & confidence)[/bright_black]"
if native_skill_mode
else "Optional commands that you can use for your specs [bright_black](improve quality & confidence)[/bright_black]"
)
enhancement_lines = [
enhancement_intro ,
"" ,
f "○ [cyan] { _display_cmd ( 'clarify' ) } [/] [bright_black](optional)[/bright_black] - Ask structured questions to de-risk ambiguous areas before planning (run before [cyan] { _display_cmd ( 'plan' ) } [/] if used)" ,
f "○ [cyan] { _display_cmd ( 'analyze' ) } [/] [bright_black](optional)[/bright_black] - Cross-artifact consistency & alignment report (after [cyan] { _display_cmd ( 'tasks' ) } [/], before [cyan] { _display_cmd ( 'implement' ) } [/])" ,
f "○ [cyan] { _display_cmd ( 'checklist' ) } [/] [bright_black](optional)[/bright_black] - Generate quality checklists to validate requirements completeness, clarity, and consistency (after [cyan] { _display_cmd ( 'plan' ) } [/])"
]
enhancements_title = "Enhancement Skills" if native_skill_mode else "Enhancement Commands"
enhancements_panel = Panel ( " \n " . join ( enhancement_lines ), title = enhancements_title , border_style = "cyan" , padding = ( 1 , 2 ))
console . print ()
console . print ( enhancements_panel )