From 1df24f1953576aa15503c0eaf48d860327991026 Mon Sep 17 00:00:00 2001 From: Pavel-tabnine Date: Mon, 9 Mar 2026 21:04:02 +0200 Subject: [PATCH] Pavel/add tabnine cli support (#1503) * feat: add Tabnine CLI agent support Tabnine CLI is a Gemini fork that uses TOML commands with the .tabnine/agent/ directory structure and TABNINE.md context files. Changes: - Add 'tabnine' to AGENT_CONFIG in __init__.py - Update release scripts (bash + PowerShell) for TOML command generation - Update agent context scripts (bash + PowerShell) - Add to GitHub release packages - Update README.md and AGENTS.md documentation - Bump version to 0.1.14 - Add 8 new tests for cross-file consistency * fix: add missing generic to agent-context script usage string --- .../scripts/create-github-release.sh | 2 + .../scripts/create-release-packages.ps1 | 10 ++- .../scripts/create-release-packages.sh | 10 ++- AGENTS.md | 4 +- CHANGELOG.md | 6 ++ README.md | 3 +- pyproject.toml | 2 +- scripts/bash/update-agent-context.sh | 17 +++-- scripts/powershell/update-agent-context.ps1 | 11 +-- src/specify_cli/__init__.py | 9 ++- src/specify_cli/extensions.py | 6 ++ tests/test_agent_config_consistency.py | 67 +++++++++++++++++++ tests/test_ai_skills.py | 5 ++ 13 files changed, 135 insertions(+), 17 deletions(-) diff --git a/.github/workflows/scripts/create-github-release.sh b/.github/workflows/scripts/create-github-release.sh index ba074a3b..29851a14 100644 --- a/.github/workflows/scripts/create-github-release.sh +++ b/.github/workflows/scripts/create-github-release.sh @@ -46,6 +46,8 @@ gh release create "$VERSION" \ .genreleases/spec-kit-template-amp-ps-"$VERSION".zip \ .genreleases/spec-kit-template-shai-sh-"$VERSION".zip \ .genreleases/spec-kit-template-shai-ps-"$VERSION".zip \ + .genreleases/spec-kit-template-tabnine-sh-"$VERSION".zip \ + .genreleases/spec-kit-template-tabnine-ps-"$VERSION".zip \ .genreleases/spec-kit-template-kiro-cli-sh-"$VERSION".zip \ .genreleases/spec-kit-template-kiro-cli-ps-"$VERSION".zip \ .genreleases/spec-kit-template-agy-sh-"$VERSION".zip \ diff --git a/.github/workflows/scripts/create-release-packages.ps1 b/.github/workflows/scripts/create-release-packages.ps1 index fc6e1033..894ab38e 100644 --- a/.github/workflows/scripts/create-release-packages.ps1 +++ b/.github/workflows/scripts/create-release-packages.ps1 @@ -14,7 +14,7 @@ .PARAMETER Agents Comma or space separated subset of agents to build (default: all) - Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, kiro-cli, bob, qodercli, shai, agy, generic + Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, kiro-cli, bob, qodercli, shai, tabnine, agy, vibe, generic .PARAMETER Scripts Comma or space separated subset of script types to build (default: both) @@ -351,6 +351,12 @@ function Build-Variant { $cmdDir = Join-Path $baseDir ".shai/commands" Generate-Commands -Agent 'shai' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script } + 'tabnine' { + $cmdDir = Join-Path $baseDir ".tabnine/agent/commands" + Generate-Commands -Agent 'tabnine' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script + $tabnineTemplate = Join-Path 'agent_templates' 'tabnine/TABNINE.md' + if (Test-Path $tabnineTemplate) { Copy-Item $tabnineTemplate (Join-Path $baseDir 'TABNINE.md') } + } 'agy' { $cmdDir = Join-Path $baseDir ".agent/workflows" Generate-Commands -Agent 'agy' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script @@ -375,7 +381,7 @@ function Build-Variant { } # Define all agents and scripts -$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'kiro-cli', 'bob', 'qodercli', 'shai', 'agy', 'vibe', 'generic') +$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'kiro-cli', 'bob', 'qodercli', 'shai', 'tabnine', 'agy', 'vibe', 'generic') $AllScripts = @('sh', 'ps') function Normalize-List { diff --git a/.github/workflows/scripts/create-release-packages.sh b/.github/workflows/scripts/create-release-packages.sh index 3cda56c0..af9880b6 100755 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -6,7 +6,7 @@ set -euo pipefail # Usage: .github/workflows/scripts/create-release-packages.sh # Version argument should include leading 'v'. # Optionally set AGENTS and/or SCRIPTS env vars to limit what gets built. -# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai kiro-cli agy bob qodercli generic (default: all) +# AGENTS : space or comma separated subset of: claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli generic (default: all) # SCRIPTS : space or comma separated subset of: sh ps (default: both) # Examples: # AGENTS=claude SCRIPTS=sh $0 v0.2.0 @@ -155,7 +155,7 @@ build_variant() { # NOTE: We substitute {ARGS} internally. Outward tokens differ intentionally: # * Markdown/prompt (claude, copilot, cursor-agent, opencode): $ARGUMENTS - # * TOML (gemini, qwen): {{args}} + # * TOML (gemini, qwen, tabnine): {{args}} # This keeps formats readable without extra abstraction. case $agent in @@ -212,6 +212,10 @@ build_variant() { shai) mkdir -p "$base_dir/.shai/commands" generate_commands shai md "\$ARGUMENTS" "$base_dir/.shai/commands" "$script" ;; + tabnine) + mkdir -p "$base_dir/.tabnine/agent/commands" + generate_commands tabnine toml "{{args}}" "$base_dir/.tabnine/agent/commands" "$script" + [[ -f agent_templates/tabnine/TABNINE.md ]] && cp agent_templates/tabnine/TABNINE.md "$base_dir/TABNINE.md" ;; kiro-cli) mkdir -p "$base_dir/.kiro/prompts" generate_commands kiro-cli md "\$ARGUMENTS" "$base_dir/.kiro/prompts" "$script" ;; @@ -233,7 +237,7 @@ build_variant() { } # Determine agent list -ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai kiro-cli agy bob vibe qodercli generic) +ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai tabnine kiro-cli agy bob vibe qodercli generic) ALL_SCRIPTS=(sh ps) norm_list() { diff --git a/AGENTS.md b/AGENTS.md index 4cafa7de..8e7631b1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,6 +47,7 @@ Specify supports multiple AI agents by generating agent-specific command files a | **Kiro CLI** | `.kiro/prompts/` | Markdown | `kiro-cli` | Kiro CLI | | **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI | | **SHAI** | `.shai/commands/` | Markdown | `shai` | SHAI CLI | +| **Tabnine CLI** | `.tabnine/agent/commands/` | TOML | `tabnine` | Tabnine CLI | | **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE | | **Generic** | User-specified via `--ai-commands-dir` | Markdown | N/A | Bring your own agent | @@ -322,6 +323,7 @@ Require a command-line tool to be installed: - **Qoder CLI**: `qodercli` CLI - **Amp**: `amp` CLI - **SHAI**: `shai` CLI +- **Tabnine CLI**: `tabnine` CLI ### IDE-Based Agents @@ -360,7 +362,7 @@ Command content with {SCRIPT} and $ARGUMENTS placeholders. ### TOML Format -Used by: Gemini, Qwen +Used by: Gemini, Qwen, Tabnine ```toml description = "Command description" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7debdfc6..08452ee6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ Recent changes to the Specify CLI and templates are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.1.14] - 2026-03-09 + +### Added + +- feat: add Tabnine CLI agent support + ## [0.1.13] - 2026-03-03 ### Changed diff --git a/README.md b/README.md index c974c054..ba84c9d4 100644 --- a/README.md +++ b/README.md @@ -177,6 +177,7 @@ See Spec-Driven Development in action across different scenarios with these comm | [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | | | [Roo Code](https://roocode.com/) | ✅ | | | [SHAI (OVHcloud)](https://github.com/ovh/shai) | ✅ | | +| [Tabnine CLI](https://docs.tabnine.com/main/getting-started/tabnine-cli) | ✅ | | | [Mistral Vibe](https://github.com/mistralai/mistral-vibe) | ✅ | | | [Windsurf](https://windsurf.com/) | ✅ | | | [Antigravity (agy)](https://antigravity.google/) | ✅ | | @@ -420,7 +421,7 @@ specify init . --force --ai claude specify init --here --force --ai claude ``` -The CLI will check if you have Claude Code, Gemini CLI, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, or Kiro 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, Cursor CLI, Qwen CLI, opencode, Codex CLI, Qoder CLI, Tabnine CLI, or Kiro 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 --ai claude --ignore-agent-tools diff --git a/pyproject.toml b/pyproject.toml index 3ae57119..12dc0e46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "specify-cli" -version = "0.1.13" +version = "0.1.14" description = "Specify CLI, part of GitHub Spec Kit. A tool to bootstrap your projects for Spec-Driven Development (SDD)." requires-python = ">=3.11" dependencies = [ diff --git a/scripts/bash/update-agent-context.sh b/scripts/bash/update-agent-context.sh index d254baf0..b64c6c8b 100644 --- a/scripts/bash/update-agent-context.sh +++ b/scripts/bash/update-agent-context.sh @@ -30,12 +30,12 @@ # # 5. Multi-Agent Support # - Handles agent-specific file paths and naming conventions -# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Kiro CLI, Mistral Vibe or Antigravity +# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Tabnine CLI, Kiro CLI, Mistral Vibe or Antigravity # - Can update single agents or all existing agent files # - Creates default Claude file if no agent files exist # # Usage: ./update-agent-context.sh [agent_type] -# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli +# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|generic # Leave empty to update all existing agent files set -e @@ -73,6 +73,7 @@ CODEBUDDY_FILE="$REPO_ROOT/CODEBUDDY.md" QODER_FILE="$REPO_ROOT/QODER.md" AMP_FILE="$REPO_ROOT/AGENTS.md" SHAI_FILE="$REPO_ROOT/SHAI.md" +TABNINE_FILE="$REPO_ROOT/TABNINE.md" KIRO_FILE="$REPO_ROOT/AGENTS.md" AGY_FILE="$REPO_ROOT/.agent/rules/specify-rules.md" BOB_FILE="$REPO_ROOT/AGENTS.md" @@ -649,6 +650,9 @@ update_specific_agent() { shai) update_agent_file "$SHAI_FILE" "SHAI" ;; + tabnine) + update_agent_file "$TABNINE_FILE" "Tabnine CLI" + ;; kiro-cli) update_agent_file "$KIRO_FILE" "Kiro CLI" ;; @@ -666,7 +670,7 @@ update_specific_agent() { ;; *) log_error "Unknown agent type '$agent_type'" - log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|vibe|qodercli|generic" + log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|generic" exit 1 ;; esac @@ -736,6 +740,11 @@ update_all_existing_agents() { found_agent=true fi + if [[ -f "$TABNINE_FILE" ]]; then + update_agent_file "$TABNINE_FILE" "Tabnine CLI" + found_agent=true + fi + if [[ -f "$QODER_FILE" ]]; then update_agent_file "$QODER_FILE" "Qoder CLI" found_agent=true @@ -783,7 +792,7 @@ print_summary() { fi echo - log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli|vibe]" + log_info "Usage: $0 [claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|generic]" } #============================================================================== diff --git a/scripts/powershell/update-agent-context.ps1 b/scripts/powershell/update-agent-context.ps1 index fee7ba6c..29ccac32 100644 --- a/scripts/powershell/update-agent-context.ps1 +++ b/scripts/powershell/update-agent-context.ps1 @@ -9,7 +9,7 @@ Mirrors the behavior of scripts/bash/update-agent-context.sh: 2. Plan Data Extraction 3. Agent File Management (create from template or update existing) 4. Content Generation (technology stack, recent changes, timestamp) - 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, kiro-cli, agy, bob, qodercli, vibe) + 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, tabnine, kiro-cli, agy, bob, vibe, qodercli) .PARAMETER AgentType Optional agent key to update a single agent. If omitted, updates all existing agent files (creating a default Claude file if none exist). @@ -25,7 +25,7 @@ Relies on common helper functions in common.ps1 #> param( [Parameter(Position=0)] - [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','kiro-cli','agy','bob','qodercli','vibe','generic')] + [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','tabnine','kiro-cli','agy','bob','qodercli','vibe','generic')] [string]$AgentType ) @@ -58,6 +58,7 @@ $CODEBUDDY_FILE = Join-Path $REPO_ROOT 'CODEBUDDY.md' $QODER_FILE = Join-Path $REPO_ROOT 'QODER.md' $AMP_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $SHAI_FILE = Join-Path $REPO_ROOT 'SHAI.md' +$TABNINE_FILE = Join-Path $REPO_ROOT 'TABNINE.md' $KIRO_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $AGY_FILE = Join-Path $REPO_ROOT '.agent/rules/specify-rules.md' $BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md' @@ -400,12 +401,13 @@ function Update-SpecificAgent { 'qodercli' { Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI' } 'amp' { Update-AgentFile -TargetFile $AMP_FILE -AgentName 'Amp' } 'shai' { Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI' } + 'tabnine' { Update-AgentFile -TargetFile $TABNINE_FILE -AgentName 'Tabnine CLI' } 'kiro-cli' { Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI' } 'agy' { Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity' } 'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' } 'vibe' { Update-AgentFile -TargetFile $VIBE_FILE -AgentName 'Mistral Vibe' } 'generic' { Write-Info 'Generic agent: no predefined context file. Use the agent-specific update script for your agent.' } - default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli|vibe|generic'; return $false } + default { Write-Err "Unknown agent type '$Type'"; Write-Err 'Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|generic'; return $false } } } @@ -425,6 +427,7 @@ function Update-AllExistingAgents { if (Test-Path $CODEBUDDY_FILE) { if (-not (Update-AgentFile -TargetFile $CODEBUDDY_FILE -AgentName 'CodeBuddy CLI')) { $ok = $false }; $found = $true } if (Test-Path $QODER_FILE) { if (-not (Update-AgentFile -TargetFile $QODER_FILE -AgentName 'Qoder CLI')) { $ok = $false }; $found = $true } if (Test-Path $SHAI_FILE) { if (-not (Update-AgentFile -TargetFile $SHAI_FILE -AgentName 'SHAI')) { $ok = $false }; $found = $true } + if (Test-Path $TABNINE_FILE) { if (-not (Update-AgentFile -TargetFile $TABNINE_FILE -AgentName 'Tabnine CLI')) { $ok = $false }; $found = $true } if (Test-Path $KIRO_FILE) { if (-not (Update-AgentFile -TargetFile $KIRO_FILE -AgentName 'Kiro CLI')) { $ok = $false }; $found = $true } if (Test-Path $AGY_FILE) { if (-not (Update-AgentFile -TargetFile $AGY_FILE -AgentName 'Antigravity')) { $ok = $false }; $found = $true } if (Test-Path $BOB_FILE) { if (-not (Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob')) { $ok = $false }; $found = $true } @@ -443,7 +446,7 @@ function Print-Summary { if ($NEW_FRAMEWORK) { Write-Host " - Added framework: $NEW_FRAMEWORK" } if ($NEW_DB -and $NEW_DB -ne 'N/A') { Write-Host " - Added database: $NEW_DB" } Write-Host '' - Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|kiro-cli|agy|bob|qodercli|vibe|generic]' + Write-Info 'Usage: ./update-agent-context.ps1 [-AgentType claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|codebuddy|amp|shai|tabnine|kiro-cli|agy|bob|vibe|qodercli|generic]' } function Main { diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index 0add6d72..e76e240d 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -237,6 +237,13 @@ AGENT_CONFIG = { "install_url": "https://github.com/ovh/shai", "requires_cli": True, }, + "tabnine": { + "name": "Tabnine CLI", + "folder": ".tabnine/agent/", + "commands_subdir": "commands", + "install_url": "https://docs.tabnine.com/main/getting-started/tabnine-cli", + "requires_cli": True, + }, "agy": { "name": "Antigravity", "folder": ".agent/", @@ -1124,7 +1131,7 @@ def install_ai_skills(project_path: Path, selected_ai: str, tracker: StepTracker if not templates_dir.exists() or not any(templates_dir.glob("*.md")): # Fallback: try the repo-relative path (for running from source checkout) # This also covers agents whose extracted commands are in a different - # format (e.g. gemini uses .toml, not .md). + # format (e.g. gemini/tabnine use .toml, not .md). script_dir = Path(__file__).parent.parent.parent # up from src/specify_cli/ fallback_dir = script_dir / "templates" / "commands" if fallback_dir.exists() and any(fallback_dir.glob("*.md")): diff --git a/src/specify_cli/extensions.py b/src/specify_cli/extensions.py index 4d5bd808..ad6b0514 100644 --- a/src/specify_cli/extensions.py +++ b/src/specify_cli/extensions.py @@ -677,6 +677,12 @@ class CommandRegistrar: "args": "$ARGUMENTS", "extension": ".md" }, + "tabnine": { + "dir": ".tabnine/agent/commands", + "format": "toml", + "args": "{{args}}", + "extension": ".toml" + }, "bob": { "dir": ".bob/commands", "format": "markdown", diff --git a/tests/test_agent_config_consistency.py b/tests/test_agent_config_consistency.py index cad112cf..607d491d 100644 --- a/tests/test_agent_config_consistency.py +++ b/tests/test_agent_config_consistency.py @@ -97,3 +97,70 @@ class TestAgentConfigConsistency: assert "kiro-cli" in pwsh_text assert "Amazon Q Developer CLI" not in bash_text assert "Amazon Q Developer CLI" not in pwsh_text + + # --- Tabnine CLI consistency checks --- + + def test_runtime_config_includes_tabnine(self): + """AGENT_CONFIG should include tabnine with correct folder and subdir.""" + assert "tabnine" in AGENT_CONFIG + assert AGENT_CONFIG["tabnine"]["folder"] == ".tabnine/agent/" + assert AGENT_CONFIG["tabnine"]["commands_subdir"] == "commands" + assert AGENT_CONFIG["tabnine"]["requires_cli"] is True + assert AGENT_CONFIG["tabnine"]["install_url"] is not None + + def test_extension_registrar_includes_tabnine(self): + """CommandRegistrar.AGENT_CONFIGS should include tabnine with correct TOML config.""" + from specify_cli.extensions import CommandRegistrar + + assert "tabnine" in CommandRegistrar.AGENT_CONFIGS + cfg = CommandRegistrar.AGENT_CONFIGS["tabnine"] + assert cfg["dir"] == ".tabnine/agent/commands" + assert cfg["format"] == "toml" + assert cfg["args"] == "{{args}}" + assert cfg["extension"] == ".toml" + + def test_release_agent_lists_include_tabnine(self): + """Bash and PowerShell release scripts should include tabnine in agent lists.""" + sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8") + ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") + + sh_match = re.search(r"ALL_AGENTS=\(([^)]*)\)", sh_text) + assert sh_match is not None + sh_agents = sh_match.group(1).split() + + ps_match = re.search(r"\$AllAgents = @\(([^)]*)\)", ps_text) + assert ps_match is not None + ps_agents = re.findall(r"'([^']+)'", ps_match.group(1)) + + assert "tabnine" in sh_agents + assert "tabnine" in ps_agents + + def test_release_scripts_generate_tabnine_toml_commands(self): + """Release scripts should generate TOML commands for tabnine in .tabnine/agent/commands.""" + sh_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.sh").read_text(encoding="utf-8") + ps_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-release-packages.ps1").read_text(encoding="utf-8") + + assert ".tabnine/agent/commands" in sh_text + assert ".tabnine/agent/commands" in ps_text + assert re.search(r"'tabnine'\s*\{.*?\.tabnine/agent/commands", ps_text, re.S) is not None + + def test_github_release_includes_tabnine_packages(self): + """GitHub release script should include tabnine template packages.""" + gh_release_text = (REPO_ROOT / ".github" / "workflows" / "scripts" / "create-github-release.sh").read_text(encoding="utf-8") + + assert "spec-kit-template-tabnine-sh-" in gh_release_text + assert "spec-kit-template-tabnine-ps-" in gh_release_text + + def test_agent_context_scripts_include_tabnine(self): + """Agent context scripts should support tabnine agent type.""" + bash_text = (REPO_ROOT / "scripts" / "bash" / "update-agent-context.sh").read_text(encoding="utf-8") + pwsh_text = (REPO_ROOT / "scripts" / "powershell" / "update-agent-context.ps1").read_text(encoding="utf-8") + + assert "tabnine" in bash_text + assert "TABNINE_FILE" in bash_text + assert "tabnine" in pwsh_text + assert "TABNINE_FILE" in pwsh_text + + def test_ai_help_includes_tabnine(self): + """CLI help text for --ai should include tabnine.""" + assert "tabnine" in AI_ASSISTANT_HELP diff --git a/tests/test_ai_skills.py b/tests/test_ai_skills.py index 59f13bd5..a040b4bd 100644 --- a/tests/test_ai_skills.py +++ b/tests/test_ai_skills.py @@ -147,6 +147,11 @@ class TestGetSkillsDir: result = _get_skills_dir(project_dir, "gemini") assert result == project_dir / ".gemini" / "skills" + def test_tabnine_skills_dir(self, project_dir): + """Tabnine should use .tabnine/agent/skills/.""" + result = _get_skills_dir(project_dir, "tabnine") + assert result == project_dir / ".tabnine" / "agent" / "skills" + def test_copilot_skills_dir(self, project_dir): """Copilot should use .github/skills/.""" result = _get_skills_dir(project_dir, "copilot")