Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Our research and experimentation focus on:
## 🔧 Prerequisites

- **Linux/macOS** (or WSL2 on Windows)
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), or [Gemini CLI](https://github.com/google-gemini/gemini-cli)
- AI coding agent: [Claude Code](https://www.anthropic.com/claude-code), [GitHub Copilot](https://code.visualstudio.com/), [Gemini CLI](https://github.com/google-gemini/gemini-cli), or [Rovodev CLI](https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device/)
- [uv](https://docs.astral.sh/uv/) for package management
- [Python 3.11+](https://www.python.org/downloads/)
- [Git](https://git-scm.com/downloads)
Expand Down Expand Up @@ -147,11 +147,12 @@ You will be prompted to select the AI agent you are using. You can also proactiv
specify init <project_name> --ai claude
specify init <project_name> --ai gemini
specify init <project_name> --ai copilot
specify init <project_name> --ai rovodev
# Or in current directory:
specify init --here --ai claude
```

The CLI will check if you have Claude Code or Gemini CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:
The CLI will check if you have Claude Code, Gemini CLI, Rovodev CLI installed. If you do not, or you prefer to get the templates without checking for the right tools, use `--ignore-agent-tools` with your command:

```bash
specify init <project_name> --ai claude --ignore-agent-tools
Expand Down
16 changes: 11 additions & 5 deletions scripts/update-agent-context.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Incrementally update agent context files based on new feature plan
# Supports: CLAUDE.md, GEMINI.md, and .github/copilot-instructions.md
# Supports: CLAUDE.md, GEMINI.md, agent.md (Rovodev CLI) and .github/copilot-instructions.md
# O(1) operation - only reads current context file and new plan.md

set -e
Expand All @@ -14,6 +14,7 @@ NEW_PLAN="$FEATURE_DIR/plan.md"
CLAUDE_FILE="$REPO_ROOT/CLAUDE.md"
GEMINI_FILE="$REPO_ROOT/GEMINI.md"
COPILOT_FILE="$REPO_ROOT/.github/copilot-instructions.md"
ROVODEV_FILE="$REPO_ROOT/agent.md"

# Allow override via argument
AGENT_TYPE="$1"
Expand Down Expand Up @@ -197,20 +198,24 @@ case "$AGENT_TYPE" in
"copilot")
update_agent_file "$COPILOT_FILE" "GitHub Copilot"
;;
"rovodev")
update_agent_file "$ROVODEV_FILE" "Rovodev CLI"
;;
"")
# Update all existing files
[ -f "$CLAUDE_FILE" ] && update_agent_file "$CLAUDE_FILE" "Claude Code"
[ -f "$GEMINI_FILE" ] && update_agent_file "$GEMINI_FILE" "Gemini CLI"
[ -f "$COPILOT_FILE" ] && update_agent_file "$COPILOT_FILE" "GitHub Copilot"
[ -f "$ROVODEV_FILE" ] && update_agent_file "$ROVODEV_FILE" "Rovodev CLI"

# If no files exist, create based on current directory or ask user
if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ]; then
if [ ! -f "$CLAUDE_FILE" ] && [ ! -f "$GEMINI_FILE" ] && [ ! -f "$COPILOT_FILE" ] && [ ! -f "$ROVODEV_FILE" ]; then
echo "No agent context files found. Creating Claude Code context file by default."
update_agent_file "$CLAUDE_FILE" "Claude Code"
fi
;;
*)
echo "ERROR: Unknown agent type '$AGENT_TYPE'. Use: claude, gemini, copilot, or leave empty for all."
echo "ERROR: Unknown agent type '$AGENT_TYPE'. Use: claude, gemini, copilot, rovodev or leave empty for all."
exit 1
;;
esac
Expand All @@ -227,8 +232,9 @@ if [ ! -z "$NEW_DB" ] && [ "$NEW_DB" != "N/A" ]; then
fi

echo ""
echo "Usage: $0 [claude|gemini|copilot]"
echo "Usage: $0 [claude|gemini|copilot|rovodev]"
echo " - No argument: Update all existing agent context files"
echo " - claude: Update only CLAUDE.md"
echo " - gemini: Update only GEMINI.md"
echo " - copilot: Update only .github/copilot-instructions.md"
echo " - copilot: Update only .github/copilot-instructions.md"
echo " - rovodev: Update only agent.md"
52 changes: 37 additions & 15 deletions src/specify_cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
AI_CHOICES = {
"copilot": "GitHub Copilot",
"claude": "Claude Code",
"gemini": "Gemini CLI"
"gemini": "Gemini CLI",
"rovodev": "Rovodev CLI",
}

# ASCII Art Banner
Expand Down Expand Up @@ -405,23 +406,30 @@ def download_template_from_github(ai_assistant: str, download_dir: Path, *, verb
console.print(f"[red]Error fetching release information:[/red] {e}")
raise typer.Exit(1)

# Find the template asset for the specified AI assistant
# Find the template asset for the specified AI assistant, with fallback for 'rovodev' -> 'copilot'
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not actually sure this is needed here. Can you explain why this fallback is implemented?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to what honjo-hiroaki-gtt mentioned in his PR here https://github.com/github/spec-kit/pull/14/files#diff-8e3e8dfe8740d05f4d06dd457ea293ec88ac0ecc9bba6eb182f0f767db67ce9eR453

Without it the Rovo Dev CLI templates return nothing to my understanding. The templates published are here and there's none for Rovo Dev CLI
Screenshot 2025-09-10 at 6 38 52 PM

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait lol. After some thought this is a smoke signal. I'll add .github workflows to populate the templates.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm keeping the fallback for now as I'm not the owner of this repo, so the pointer to the releases is returning empty. I like using speckit with rovodev. If it's required to merge I'll do it but to keep it functional I'll keep the fallback.

pattern = f"spec-kit-template-{ai_assistant}"
matching_assets = [
asset for asset in release_data.get("assets", [])
if pattern in asset["name"] and asset["name"].endswith(".zip")
]
assets = release_data.get("assets", [])
matching_assets = [a for a in assets if pattern in a["name"] and a["name"].endswith(".zip")]

asset = None
if matching_assets:
asset = matching_assets[0]
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fallback logic for rovodevcli -> copilot template mentioned in the comment is not implemented. The code should attempt to find the copilot template when rovodevcli template is not found.

Suggested change
asset = matching_assets[0]
asset = matching_assets[0]
# Fallback: if rovodevcli not found, try copilot
if asset is None and ai_assistant == "rovodevcli":
fallback_pattern = "spec-kit-template-copilot"
fallback_assets = [a for a in assets if fallback_pattern in a["name"] and a["name"].endswith(".zip")]
if fallback_assets:
asset = fallback_assets[0]
if verbose:
console.print("[yellow]Falling back to 'copilot' template as 'rovodevcli' template was not found.[/yellow]")

Copilot uses AI. Check for mistakes.
elif ai_assistant == "rovodev":
# Fallback to copilot template if rovodev-specific template is not published yet
fallback_pattern = "spec-kit-template-copilot"
fallback_assets = [a for a in assets if fallback_pattern in a["name"] and a["name"].endswith(".zip")]
if fallback_assets:
asset = fallback_assets[0]
if verbose:
console.print("[yellow]No 'rovodev' template found; falling back to 'copilot' template.[/yellow]")
Copy link

Copilot AI Sep 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition checks for "rovodev" but the AI_CHOICES dictionary uses "rovodevcli" as the key. This condition will never be true, causing the fallback logic to never execute.

Suggested change
elif ai_assistant == "rovodev":
# Fallback to copilot template if rovodev-specific template is not published yet
fallback_pattern = "spec-kit-template-copilot"
fallback_assets = [a for a in assets if fallback_pattern in a["name"] and a["name"].endswith(".zip")]
if fallback_assets:
asset = fallback_assets[0]
if verbose:
console.print("[yellow]No 'rovodev' template found; falling back to 'copilot' template.[/yellow]")
elif ai_assistant == "rovodevcli":
# Fallback to copilot template if rovodevcli-specific template is not published yet
fallback_pattern = "spec-kit-template-copilot"
fallback_assets = [a for a in assets if fallback_pattern in a["name"] and a["name"].endswith(".zip")]
if fallback_assets:
asset = fallback_assets[0]
if verbose:
console.print("[yellow]No 'rovodevcli' template found; falling back to 'copilot' template.[/yellow]")

Copilot uses AI. Check for mistakes.

if not matching_assets:
if asset is None:
Copy link

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The fallback logic is hardcoded specifically for 'rovodev' -> 'copilot'. Consider implementing a more flexible fallback mapping that could be extended for other AI assistants in the future.

Copilot uses AI. Check for mistakes.
if verbose:
console.print(f"[red]Error:[/red] No template found for AI assistant '{ai_assistant}'")
console.print(f"[yellow]Available assets:[/yellow]")
for asset in release_data.get("assets", []):
console.print(f" - {asset['name']}")
for a in assets:
console.print(f" - {a['name']}")
raise typer.Exit(1)

# Use the first matching asset
asset = matching_assets[0]
download_url = asset["browser_download_url"]
filename = asset["name"]
file_size = asset["size"]
Expand Down Expand Up @@ -638,7 +646,7 @@ def download_and_extract_template(project_path: Path, ai_assistant: str, is_curr
@app.command()
def init(
project_name: str = typer.Argument(None, help="Name for your new project directory (optional if using --here)"),
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, or copilot"),
ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, or rovodev"),
ignore_agent_tools: bool = typer.Option(False, "--ignore-agent-tools", help="Skip checks for AI 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"),
Expand All @@ -648,7 +656,7 @@ def init(

This command will:
1. Check that required tools are installed (git is optional)
2. Let you choose your AI assistant (Claude Code, Gemini CLI, or GitHub Copilot)
2. Let you choose your AI assistant (Claude Code, Gemini CLI, GitHub Copilot, or Rovodev CLI)
3. Download the appropriate template from GitHub
4. Extract the template to a new project directory or current directory
5. Initialize a fresh git repository (if not --no-git and no existing repo)
Expand All @@ -659,8 +667,10 @@ def init(
specify init my-project --ai claude
specify init my-project --ai gemini
specify init my-project --ai copilot --no-git
specify init my-project --ai rovodev
specify init --ignore-agent-tools my-project
specify init --here --ai claude
specify init --here --ai rovodev
specify init --here
"""
# Show banner first
Expand Down Expand Up @@ -737,6 +747,10 @@ def init(
if not check_tool("gemini", "Install from: https://github.com/google-gemini/gemini-cli"):
console.print("[red]Error:[/red] Gemini CLI is required for Gemini projects")
agent_tool_missing = True
elif selected_ai == "rovodev":
if not check_tool("acli", "Install from: https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device"):
Copy link

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The URL is quite long and makes the line exceed reasonable length limits. Consider storing the installation URL in a constant or variable for better readability.

Copilot uses AI. Check for mistakes.
console.print("[red]Error:[/red] Rovodev CLI is required for Rovodev projects")
agent_tool_missing = True
# GitHub Copilot check is not needed as it's typically available in supported IDEs

if agent_tool_missing:
Expand Down Expand Up @@ -823,6 +837,12 @@ def init(
steps_lines.append(" - See GEMINI.md for all available commands")
elif selected_ai == "copilot":
steps_lines.append(f"{step_num}. Open in Visual Studio Code and use [bold cyan]/specify[/], [bold cyan]/plan[/], [bold cyan]/tasks[/] commands with GitHub Copilot")
elif selected_ai == "rovodev":
steps_lines.append(f"{step_num}. Use / commands with Rovodev CLI")
steps_lines.append(" - Use /specify to create specifications")
steps_lines.append(" - Use /plan to create implementation plans")
steps_lines.append(" - Use /tasks to generate tasks")
steps_lines.append(" - See agent.md for all available commands")

step_num += 1
steps_lines.append(f"{step_num}. Update [bold magenta]CONSTITUTION.md[/bold magenta] with your project's non-negotiable principles")
Expand Down Expand Up @@ -855,11 +875,13 @@ def check():
console.print("\n[cyan]Optional AI tools:[/cyan]")
claude_ok = check_tool("claude", "Install from: https://docs.anthropic.com/en/docs/claude-code/setup")
gemini_ok = check_tool("gemini", "Install from: https://github.com/google-gemini/gemini-cli")
rovodev_ok = check_tool("acli", "Install from: https://support.atlassian.com/rovo/docs/install-and-run-rovo-dev-cli-on-your-device")
Copy link

Copilot AI Sep 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] Same long URL as in the agent tools check. Consider extracting this to a constant for consistency and maintainability.

Copilot uses AI. Check for mistakes.


console.print("\n[green]✓ Specify CLI is ready to use![/green]")
if not git_ok:
console.print("[yellow]Consider installing git for repository management[/yellow]")
if not (claude_ok or gemini_ok):
if not (claude_ok or gemini_ok or rovodev_ok):
console.print("[yellow]Consider installing an AI assistant for the best experience[/yellow]")


Expand Down