Skip to content
10 changes: 5 additions & 5 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1974,7 +1974,7 @@ def init(
console.print("[yellow]Use:[/yellow] --integration for the new integration system, or --ai for the legacy path")
raise typer.Exit(1)

# Auto-promote: --ai copilot → integration path with a nudge
# Auto-promote: --ai <key> → integration path with a nudge (if registered)
use_integration = False
if integration:
from .integrations import INTEGRATION_REGISTRY, get_integration
Expand All @@ -1987,14 +1987,14 @@ def init(
use_integration = True
# Map integration key to the ai_assistant variable for downstream compatibility
ai_assistant = integration
elif ai_assistant == "copilot":
elif ai_assistant:
from .integrations import get_integration
resolved_integration = get_integration("copilot")
resolved_integration = get_integration(ai_assistant)
if resolved_integration:
use_integration = True
console.print(
"[dim]Tip: Use [bold]--integration copilot[/bold] instead of "
"--ai copilot. The --ai flag will be deprecated in a future release.[/dim]"
f"[dim]Tip: Use [bold]--integration {ai_assistant}[/bold] instead of "
f"--ai {ai_assistant}. The --ai flag will be deprecated in a future release.[/dim]"
)

if project_name == ".":
Expand Down
14 changes: 10 additions & 4 deletions src/specify_cli/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class CommandRegistrar:
"args": "$ARGUMENTS",
"extension": ".agent.md"
},
"cursor": {
"cursor-agent": {
"dir": ".cursor/commands",
"format": "markdown",
"args": "$ARGUMENTS",
Expand Down Expand Up @@ -162,6 +162,12 @@ class CommandRegistrar:
"format": "markdown",
"args": "$ARGUMENTS",
"extension": ".md"
},
"vibe": {
"dir": ".vibe/prompts",
"format": "markdown",
"args": "$ARGUMENTS",
"extension": ".md"
}
}

Expand Down Expand Up @@ -235,11 +241,11 @@ def _adjust_script_paths(self, frontmatter: dict) -> dict:

for key, script_path in scripts.items():
if isinstance(script_path, str):
scripts[key] = self._rewrite_project_relative_paths(script_path)
scripts[key] = self.rewrite_project_relative_paths(script_path)
return frontmatter

@staticmethod
def _rewrite_project_relative_paths(text: str) -> str:
def rewrite_project_relative_paths(text: str) -> str:
"""Rewrite repo-relative paths to their generated project locations."""
if not isinstance(text, str) or not text:
return text
Expand Down Expand Up @@ -422,7 +428,7 @@ def resolve_skill_placeholders(agent_name: str, frontmatter: dict, body: str, pr
body = body.replace("{AGENT_SCRIPT}", agent_script_command)

body = body.replace("{ARGS}", "$ARGUMENTS").replace("__AGENT__", agent_name)
return CommandRegistrar._rewrite_project_relative_paths(body)
return CommandRegistrar.rewrite_project_relative_paths(body)

def _convert_argument_placeholder(self, content: str, from_placeholder: str, to_placeholder: str) -> str:
"""Convert argument placeholder format.
Expand Down
51 changes: 49 additions & 2 deletions src/specify_cli/integrations/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,57 @@ def get_integration(key: str) -> IntegrationBase | None:
# -- Register built-in integrations --------------------------------------

def _register_builtins() -> None:
"""Register all built-in integrations."""
from .copilot import CopilotIntegration
"""Register all built-in integrations.
Package directories use Python-safe identifiers (e.g. ``kiro_cli``,
``cursor_agent``). The user-facing integration key stored in
``IntegrationBase.key`` stays hyphenated (``"kiro-cli"``,
``"cursor-agent"``) to match the actual CLI tool / binary name that
users install and invoke.
"""
# -- Imports (alphabetical) -------------------------------------------
from .amp import AmpIntegration
from .auggie import AuggieIntegration
from .bob import BobIntegration
from .claude import ClaudeIntegration
from .codebuddy import CodebuddyIntegration
from .copilot import CopilotIntegration
from .cursor_agent import CursorAgentIntegration
from .iflow import IflowIntegration
from .junie import JunieIntegration
from .kilocode import KilocodeIntegration
from .kiro_cli import KiroCliIntegration
from .opencode import OpencodeIntegration
from .pi import PiIntegration
from .qodercli import QodercliIntegration
from .qwen import QwenIntegration
from .roo import RooIntegration
from .shai import ShaiIntegration
from .trae import TraeIntegration
from .vibe import VibeIntegration
from .windsurf import WindsurfIntegration

# -- Registration (alphabetical) --------------------------------------
_register(AmpIntegration())
_register(AuggieIntegration())
_register(BobIntegration())
_register(ClaudeIntegration())
_register(CodebuddyIntegration())
_register(CopilotIntegration())
_register(CursorAgentIntegration())
_register(IflowIntegration())
_register(JunieIntegration())
_register(KilocodeIntegration())
_register(KiroCliIntegration())
_register(OpencodeIntegration())
_register(PiIntegration())
_register(QodercliIntegration())
_register(QwenIntegration())
_register(RooIntegration())
_register(ShaiIntegration())
_register(TraeIntegration())
_register(VibeIntegration())
_register(WindsurfIntegration())


_register_builtins()
21 changes: 21 additions & 0 deletions src/specify_cli/integrations/amp/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Amp CLI integration."""

from ..base import MarkdownIntegration


class AmpIntegration(MarkdownIntegration):
key = "amp"
config = {
"name": "Amp",
"folder": ".agents/",
"commands_subdir": "commands",
"install_url": "https://ampcode.com/manual#install",
"requires_cli": True,
}
registrar_config = {
"dir": ".agents/commands",
"format": "markdown",
"args": "$ARGUMENTS",
"extension": ".md",
}
context_file = "AGENTS.md"
23 changes: 23 additions & 0 deletions src/specify_cli/integrations/amp/scripts/update-context.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# update-context.ps1 — Amp integration: create/update AGENTS.md
#
# Thin wrapper that delegates to the shared update-agent-context script.
# Activated in Stage 7 when the shared script uses integration.json dispatch.
#
# Until then, this delegates to the shared script as a subprocess.

$ErrorActionPreference = 'Stop'

# Derive repo root from script location (walks up to find .specify/)
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$repoRoot = try { git rev-parse --show-toplevel 2>$null } catch { $null }
# If git did not return a repo root, or the git root does not contain .specify,
# fall back to walking up from the script directory to find the initialized project root.
if (-not $repoRoot -or -not (Test-Path (Join-Path $repoRoot '.specify'))) {
$repoRoot = $scriptDir
$fsRoot = [System.IO.Path]::GetPathRoot($repoRoot)
while ($repoRoot -and $repoRoot -ne $fsRoot -and -not (Test-Path (Join-Path $repoRoot '.specify'))) {
$repoRoot = Split-Path -Parent $repoRoot
}
}

& "$repoRoot/.specify/scripts/powershell/update-agent-context.ps1" -AgentType amp
28 changes: 28 additions & 0 deletions src/specify_cli/integrations/amp/scripts/update-context.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# update-context.sh — Amp integration: create/update AGENTS.md
#
# Thin wrapper that delegates to the shared update-agent-context script.
# Activated in Stage 7 when the shared script uses integration.json dispatch.
#
# Until then, this delegates to the shared script as a subprocess.

set -euo pipefail

# Derive repo root from script location (walks up to find .specify/)
_script_dir="$(cd "$(dirname "$0")" && pwd)"
_root="$_script_dir"
while [ "$_root" != "/" ] && [ ! -d "$_root/.specify" ]; do _root="$(dirname "$_root")"; done
if [ -z "${REPO_ROOT:-}" ]; then
if [ -d "$_root/.specify" ]; then
REPO_ROOT="$_root"
else
git_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -n "$git_root" ] && [ -d "$git_root/.specify" ]; then
REPO_ROOT="$git_root"
else
REPO_ROOT="$_root"
fi
fi
fi

exec "$REPO_ROOT/.specify/scripts/bash/update-agent-context.sh" amp
21 changes: 21 additions & 0 deletions src/specify_cli/integrations/auggie/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Auggie CLI integration."""

from ..base import MarkdownIntegration


class AuggieIntegration(MarkdownIntegration):
key = "auggie"
config = {
"name": "Auggie CLI",
"folder": ".augment/",
"commands_subdir": "commands",
"install_url": "https://docs.augmentcode.com/cli/setup-auggie/install-auggie-cli",
"requires_cli": True,
}
registrar_config = {
"dir": ".augment/commands",
"format": "markdown",
"args": "$ARGUMENTS",
"extension": ".md",
}
context_file = ".augment/rules/specify-rules.md"
23 changes: 23 additions & 0 deletions src/specify_cli/integrations/auggie/scripts/update-context.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# update-context.ps1 — Auggie CLI integration: create/update .augment/rules/specify-rules.md
#
# Thin wrapper that delegates to the shared update-agent-context script.
# Activated in Stage 7 when the shared script uses integration.json dispatch.
#
# Until then, this delegates to the shared script as a subprocess.

$ErrorActionPreference = 'Stop'

# Derive repo root from script location (walks up to find .specify/)
$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Definition
$repoRoot = try { git rev-parse --show-toplevel 2>$null } catch { $null }
# If git did not return a repo root, or the git root does not contain .specify,
# fall back to walking up from the script directory to find the initialized project root.
if (-not $repoRoot -or -not (Test-Path (Join-Path $repoRoot '.specify'))) {
$repoRoot = $scriptDir
$fsRoot = [System.IO.Path]::GetPathRoot($repoRoot)
while ($repoRoot -and $repoRoot -ne $fsRoot -and -not (Test-Path (Join-Path $repoRoot '.specify'))) {
$repoRoot = Split-Path -Parent $repoRoot
}
}

& "$repoRoot/.specify/scripts/powershell/update-agent-context.ps1" -AgentType auggie
28 changes: 28 additions & 0 deletions src/specify_cli/integrations/auggie/scripts/update-context.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# update-context.sh — Auggie CLI integration: create/update .augment/rules/specify-rules.md
#
# Thin wrapper that delegates to the shared update-agent-context script.
# Activated in Stage 7 when the shared script uses integration.json dispatch.
#
# Until then, this delegates to the shared script as a subprocess.

set -euo pipefail

# Derive repo root from script location (walks up to find .specify/)
_script_dir="$(cd "$(dirname "$0")" && pwd)"
_root="$_script_dir"
while [ "$_root" != "/" ] && [ ! -d "$_root/.specify" ]; do _root="$(dirname "$_root")"; done
if [ -z "${REPO_ROOT:-}" ]; then
if [ -d "$_root/.specify" ]; then
REPO_ROOT="$_root"
else
git_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [ -n "$git_root" ] && [ -d "$git_root/.specify" ]; then
REPO_ROOT="$git_root"
else
REPO_ROOT="$_root"
fi
fi
fi

exec "$REPO_ROOT/.specify/scripts/bash/update-agent-context.sh" auggie
Loading
Loading