From 6fed8bad514cc69b4507af19ad48455b945f0bfe Mon Sep 17 00:00:00 2001 From: Pavel-tabnine Date: Wed, 21 Jan 2026 10:18:54 +0200 Subject: [PATCH 1/2] Create tabnine-cli-cr.yml --- .github/workflows/tabnine-cli-cr.yml | 178 +++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 .github/workflows/tabnine-cli-cr.yml diff --git a/.github/workflows/tabnine-cli-cr.yml b/.github/workflows/tabnine-cli-cr.yml new file mode 100644 index 0000000000..62afbc4bfa --- /dev/null +++ b/.github/workflows/tabnine-cli-cr.yml @@ -0,0 +1,178 @@ +name: "Tabnine: Code Review" +on: + pull_request: + types: [opened, synchronize, ready_for_review] + +jobs: + code-review: + runs-on: ubuntu-latest + timeout-minutes: 30 # Prevent runaway jobs + # Only run on certain conditions + if: github.actor != 'dependabot[bot]' && github.actor != 'github-actions[bot]' && github.actor != 'TabnineCLI@tabnine.com' + permissions: + contents: read + pull-requests: write + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ github.event.pull_request.head.sha }} + + - name: Install Tabnine CLI + timeout-minutes: 10 + run: | + export TABNINE_HOST=https://console.tabnine.com + + # Download installer first, verify, then execute + curl -fsSL "$TABNINE_HOST/update/cli/installer.mjs" -o installer.mjs + + node installer.mjs "$TABNINE_HOST" + + # Verify installation succeeded + if [ ! -f ~/.local/bin/tabnine ]; then + echo "Error: Tabnine CLI installation failed" + exit 1 + fi + + - name: Configure git + run: | + git config user.name "Tabnine CLI Agent" + git config user.email "TabnineCLI@tabnine.com" + + - name: Configure Tabnine Auth & Settings + env: + TABNINE_KEY: ${{ secrets.TABNINE_KEY }} + run: | + # Validate TABNINE_KEY secret + if [ -z "$TABNINE_KEY" ]; then + echo "Error: Required authentication secret is not configured" + exit 1 + fi + + mkdir -p ~/.tabnine/agent + + # Write Settings + cat << 'EOF' > ~/.tabnine/agent/settings.json + { + "general": { + "tabnineHost": "https://console.tabnine.com" + }, + "security": { + "auth": { + "selectedType": "tabnine-personal" + } + } + } + EOF + + # Write Credentials securely with error handling + if ! printf "%s" "$TABNINE_KEY" > ~/.tabnine/agent/tabnine_creds.json; then + echo "Error: Failed to write credentials" + exit 1 + fi + + chmod 600 ~/.tabnine/agent/tabnine_creds.json + + - name: Authenticate GitHub CLI + run: | + gh auth login --with-token <<< ${{ github.token }} + + - name: Code Review + timeout-minutes: 25 + run: | + ~/.local/bin/tabnine -y -p "## 1. Persona & Context + You are a Senior Software Engineer performing a rigorous code review for the this project. + Your goal is to ensure the codebase remains high-quality, maintainable, and secure. + + ## 2. Environment & Tools + You are running in a GitHub Actions CI environment with full access to the shell and the pre-authenticated 'gh' CLI. + - Repository: ${{ github.repository }} + - PR Number: ${{ github.event.pull_request.number }} + - PR Head SHA: ${{ github.event.pull_request.head.sha }} + - PR Base SHA: ${{ github.event.pull_request.base.sha }} + + ## 3. Operational Procedure + + ### Phase A: Understanding the Change + 1. Execute 'gh pr view --json title,body,comments' to understand the intent. + 2. Execute 'gh pr diff' to examine the implementation. + + ### Phase B: Check Existing Comments + 1. Get existing comments: 'gh pr view --json comments' + 2. If a previously reported issue appears fixed, reply with a resolved comment. + + ### Phase C: Engineering Audit + Evaluate the code against these Software Engineering pillars: + + **Correctness & Logic**: Does the code solve the problem? Are there edge cases, off-by-one errors, or race conditions? + + **Maintainability & Readability**: Are names self-explanatory? Is the code simple and modular? Are complex parts documented? + + **Security & Reliability**: Are inputs sanitized? Are secrets exposed? Does error handling fail safely? + + **Performance**: Are there unnecessary allocations or blocking operations? + + **Project Specifics**: Cross-platform paths (Windows/Linux), backward compatibility with config files. + + ## 4. Comment Value Threshold (CRITICAL FILTER) + + Before posting ANY comment, it MUST pass ALL of these criteria: + + **DO comment if the issue:** + - Introduces a bug, security vulnerability, or data loss risk + - Breaks backward compatibility or cross-platform support + - Causes performance regression or memory leak + - Violates critical project patterns (e.g., error handling, path handling) + - Makes the code significantly harder to maintain or debug + + **DO NOT comment on:** + - Style preferences unless they harm readability (e.g., variable naming is fine unless truly confusing) + - Minor optimizations that don't impact real-world performance + - Suggestions to 'improve' code that is already clear and working + - Nitpicks about formatting, spacing, or trivial refactoring opportunities + - Personal preferences about code structure when existing approach is valid + - Educational comments explaining what the code does (assume the author understands their code) + + **Golden Rule**: If removing your comment would NOT increase the risk of bugs, security issues, or maintenance problems, DO NOT POST IT. + + ### Phase D: Inline Comments + + For each potential issue from Phase C: + 1. Apply the Comment Value Threshold filter above + 2. If it passes the filter, verify: + - The file exists in the diff output + - The line number is within changed lines + - No duplicate feedback exists on nearby lines + 3. Only then submit the comment + + Submit inline comments using: + + For SINGLE-LINE comments: + gh api --method POST -H 'Accept: application/vnd.github+json' /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='YOUR_COMMENT' -f commit_id='${{ github.event.pull_request.head.sha }}' -f path='FILE_PATH' -F line=LINE_NUMBER -f side='RIGHT' + + For MULTI-LINE comments: + gh api --method POST -H 'Accept: application/vnd.github+json' /repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/comments -f body='YOUR_COMMENT' -f commit_id='${{ github.event.pull_request.head.sha }}' -f path='FILE_PATH' -F start_line=START_LINE_NUMBER -f start_side='RIGHT' -F line=END_LINE_NUMBER -f side='RIGHT' + + **Code Suggestions**: Use GitHub's suggestion syntax ONLY when the fix is clear, unambiguous, and replaces 10 or fewer lines: + ```suggestion + // Your replacement code + ``` + + For larger changes or context-dependent fixes, provide guidance as a regular comment without the suggestion block. + + ### Phase E: Final Summary + After submitting inline comments (or if zero comments were posted), post a holistic summary: + + gh pr review --comment -b 'YOUR_SUMMARY' + + Your summary must: + - Acknowledge what the PR accomplishes + - Note any critical issues found (or state 'No critical issues identified') + - Be concise + + ## 5. Tone Guidelines + - Constructive: Suggest improvements or alternatives. + - Objective: Focus on code, not author. + - Professional: Use clear, concise language. + - High Signal: Only comment when it genuinely helps prevent problems." From dea8d542d87012bca7a1cf68411353cb3b1ac030 Mon Sep 17 00:00:00 2001 From: Pavel Date: Wed, 21 Jan 2026 11:10:08 +0200 Subject: [PATCH 2/2] Add Tabnine CLI agent support - Add Tabnine CLI to AGENT_CONFIG in specify CLI - Update README.md to list Tabnine CLI in supported agents table - Update AGENTS.md documentation with Tabnine CLI details - Add Tabnine template generation to release package scripts (both bash and PowerShell) - Include Tabnine packages in GitHub release script - Update agent context update scripts (bash and PowerShell) to support Tabnine - Configure Tabnine as TOML-based agent (similar to Gemini/Qwen) - Set up .tabnine/commands/ directory structure with {{args}} argument format --- .../workflows/scripts/create-github-release.sh | 2 ++ .../workflows/scripts/create-release-packages.ps1 | 8 ++++++-- .../workflows/scripts/create-release-packages.sh | 5 ++++- AGENTS.md | 4 +++- README.md | 1 + scripts/bash/update-agent-context.sh | 15 ++++++++++++--- scripts/powershell/update-agent-context.ps1 | 9 ++++++--- src/specify_cli/__init__.py | 8 +++++++- 8 files changed, 41 insertions(+), 11 deletions(-) diff --git a/.github/workflows/scripts/create-github-release.sh b/.github/workflows/scripts/create-github-release.sh index 1030bbef4c..d782836100 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-q-sh-"$VERSION".zip \ .genreleases/spec-kit-template-q-ps-"$VERSION".zip \ .genreleases/spec-kit-template-bob-sh-"$VERSION".zip \ diff --git a/.github/workflows/scripts/create-release-packages.ps1 b/.github/workflows/scripts/create-release-packages.ps1 index a59df6e13f..1c6eb9a5a0 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, q, bob, qoder + Valid agents: claude, gemini, copilot, cursor-agent, qwen, opencode, windsurf, codex, kilocode, auggie, roo, codebuddy, amp, q, bob, qoder, tabnine .PARAMETER Scripts Comma or space separated subset of script types to build (default: both) @@ -347,6 +347,10 @@ function Build-Variant { $cmdDir = Join-Path $baseDir ".qoder/commands" Generate-Commands -Agent 'qoder' -Extension 'md' -ArgFormat '$ARGUMENTS' -OutputDir $cmdDir -ScriptVariant $Script } + 'tabnine' { + $cmdDir = Join-Path $baseDir ".tabnine/commands" + Generate-Commands -Agent 'tabnine' -Extension 'toml' -ArgFormat '{{args}}' -OutputDir $cmdDir -ScriptVariant $Script + } } # Create zip archive @@ -356,7 +360,7 @@ function Build-Variant { } # Define all agents and scripts -$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'q', 'bob', 'qoder') +$AllAgents = @('claude', 'gemini', 'copilot', 'cursor-agent', 'qwen', 'opencode', 'windsurf', 'codex', 'kilocode', 'auggie', 'roo', 'codebuddy', 'amp', 'q', 'bob', 'qoder', 'tabnine') $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 48678282e1..034aa1102f 100755 --- a/.github/workflows/scripts/create-release-packages.sh +++ b/.github/workflows/scripts/create-release-packages.sh @@ -217,13 +217,16 @@ build_variant() { bob) mkdir -p "$base_dir/.bob/commands" generate_commands bob md "\$ARGUMENTS" "$base_dir/.bob/commands" "$script" ;; + tabnine) + mkdir -p "$base_dir/.tabnine/commands" + generate_commands tabnine toml "{{args}}" "$base_dir/.tabnine/commands" "$script" ;; esac ( cd "$base_dir" && zip -r "../spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" . ) echo "Created $GENRELEASES_DIR/spec-kit-template-${agent}-${script}-${NEW_VERSION}.zip" } # Determine agent list -ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai q bob qoder) +ALL_AGENTS=(claude gemini copilot cursor-agent qwen opencode windsurf codex kilocode auggie roo codebuddy amp shai q bob qoder tabnine) ALL_SCRIPTS=(sh ps) norm_list() { diff --git a/AGENTS.md b/AGENTS.md index d7360487b8..dd14e64268 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,6 +47,7 @@ Specify supports multiple AI agents by generating agent-specific command files a | **Amazon Q Developer CLI** | `.amazonq/prompts/` | Markdown | `q` | Amazon Q Developer CLI | | **Amp** | `.agents/commands/` | Markdown | `amp` | Amp CLI | | **SHAI** | `.shai/commands/` | Markdown | `shai` | SHAI CLI | +| **Tabnine CLI** | `.tabnine/commands/` | TOML | `tabnine` | Tabnine CLI | | **IBM Bob** | `.bob/commands/` | Markdown | N/A (IDE-based) | IBM Bob IDE | ### Step-by-Step Integration Guide @@ -316,6 +317,7 @@ Require a command-line tool to be installed: - **Qoder CLI**: `qoder` CLI - **Amp**: `amp` CLI - **SHAI**: `shai` CLI +- **Tabnine CLI**: `tabnine` CLI ### IDE-Based Agents @@ -354,7 +356,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/README.md b/README.md index 76149512f6..5a0b9c4668 100644 --- a/README.md +++ b/README.md @@ -161,6 +161,7 @@ Want to see Spec Kit in action? Watch our [video overview](https://www.youtube.c | [Qwen Code](https://github.com/QwenLM/qwen-code) | ✅ | | | [Roo Code](https://roocode.com/) | ✅ | | | [SHAI (OVHcloud)](https://github.com/ovh/shai) | ✅ | | +| [Tabnine CLI](https://github.com/codota/tabnine-cli) | ✅ | | | [Windsurf](https://windsurf.com/) | ✅ | | ## 🔧 Specify CLI Reference diff --git a/scripts/bash/update-agent-context.sh b/scripts/bash/update-agent-context.sh index 6d3e0b37ab..9e2137e5b0 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, or Amazon Q Developer CLI +# - Supports: Claude, Gemini, Copilot, Cursor, Qwen, opencode, Codex, Windsurf, Kilo Code, Auggie CLI, Roo Code, CodeBuddy CLI, Qoder CLI, Amp, SHAI, Tabnine CLI, or Amazon Q Developer CLI # - 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|shai|q|bob|qoder +# Agent types: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|shai|tabnine|q|bob|qoder # 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" Q_FILE="$REPO_ROOT/AGENTS.md" BOB_FILE="$REPO_ROOT/AGENTS.md" @@ -627,6 +628,9 @@ update_specific_agent() { shai) update_agent_file "$SHAI_FILE" "SHAI" ;; + tabnine) + update_agent_file "$TABNINE_FILE" "Tabnine CLI" + ;; q) update_agent_file "$Q_FILE" "Amazon Q Developer CLI" ;; @@ -635,7 +639,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|amp|shai|q|bob|qoder" + log_error "Expected: claude|gemini|copilot|cursor-agent|qwen|opencode|codex|windsurf|kilocode|auggie|roo|amp|shai|tabnine|q|bob|qoder" exit 1 ;; esac @@ -705,6 +709,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 diff --git a/scripts/powershell/update-agent-context.ps1 b/scripts/powershell/update-agent-context.ps1 index ffdab4bd62..cb2c099b71 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, q, bob, qoder) + 5. Multi-Agent Support (claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, roo, codebuddy, amp, shai, tabnine, q, bob, qoder) .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','q','bob','qoder')] + [ValidateSet('claude','gemini','copilot','cursor-agent','qwen','opencode','codex','windsurf','kilocode','auggie','roo','codebuddy','amp','shai','tabnine','q','bob','qoder')] [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' $Q_FILE = Join-Path $REPO_ROOT 'AGENTS.md' $BOB_FILE = Join-Path $REPO_ROOT 'AGENTS.md' @@ -386,9 +387,10 @@ function Update-SpecificAgent { 'qoder' { 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' } 'q' { Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI' } 'bob' { Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob' } - 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|q|bob|qoder'; 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|q|bob|qoder'; return $false } } } @@ -408,6 +410,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 $Q_FILE) { if (-not (Update-AgentFile -TargetFile $Q_FILE -AgentName 'Amazon Q Developer CLI')) { $ok = $false }; $found = $true } if (Test-Path $BOB_FILE) { if (-not (Update-AgentFile -TargetFile $BOB_FILE -AgentName 'IBM Bob')) { $ok = $false }; $found = $true } if (-not $found) { diff --git a/src/specify_cli/__init__.py b/src/specify_cli/__init__.py index 1dedb31949..f6044443b6 100644 --- a/src/specify_cli/__init__.py +++ b/src/specify_cli/__init__.py @@ -226,6 +226,12 @@ def _format_rate_limit_error(status_code: int, headers: httpx.Headers, url: str) "install_url": None, # IDE-based "requires_cli": False, }, + "tabnine": { + "name": "Tabnine CLI", + "folder": ".tabnine/", + "install_url": "https://github.com/codota/tabnine-cli", + "requires_cli": True, + }, } SCRIPT_TYPE_CHOICES = {"sh": "POSIX Shell (bash/zsh)", "ps": "PowerShell"} @@ -945,7 +951,7 @@ def ensure_executable_scripts(project_path: Path, tracker: StepTracker | 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)"), - ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, amp, shai, q, bob, or qoder "), + ai_assistant: str = typer.Option(None, "--ai", help="AI assistant to use: claude, gemini, copilot, cursor-agent, qwen, opencode, codex, windsurf, kilocode, auggie, codebuddy, amp, shai, q, bob, qoder, or tabnine"), 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 AI agent tools like Claude Code"), no_git: bool = typer.Option(False, "--no-git", help="Skip git repository initialization"),