Greptile - Codebase AI API
Czym jest Greptile?
Greptile to API, które pozwala na budowanie własnych narzędzi AI z pełnym kontekstem twojego codebase. W przeciwieństwie od gotowych narzędzi jak GitHub Copilot czy Cody, Greptile dostarcza infrastrukturę i API do tworzenia własnych rozwiązań - od chatbotów przez PR review boty po generatory dokumentacji.
Greptile indeksuje twoje repozytoria z GitHub lub GitLab, tworzy embeddings i udostępnia proste API do przeszukiwania i odpowiadania na pytania o kodzie. Dzięki temu możesz zbudować custom AI assistants, które rozumieją twój konkretny projekt, jego architekturę i konwencje.
Dlaczego Greptile?
Kluczowe zalety Greptile:
- Proste API - REST API, łatwe do integracji w kilka godzin
- Pełna customization - Zbuduj dokładnie to, czego potrzebujesz
- Codebase context - AI rozumie cały projekt, nie tylko pojedynczy plik
- Multi-repo support - Połącz wiedzę z wielu repozytoriów
- Privacy-first - Możliwość self-hostingu na Enterprise
- Model-agnostic - Używaj własnych modeli LLM lub Greptile default
- Real-time updates - Automatyczna aktualizacja indeksu przy zmianach
Greptile vs alternatywy
| Cecha | Greptile | OpenAI API + RAG | Sourcegraph API |
|---|---|---|---|
| Typ | API dla codebase | Ogólne LLM API | Code search API |
| Setup | Minuty | Dni/tygodnie | Godziny |
| Codebase context | ✅ Automatyczne | ⚠️ DIY | ✅ Tak |
| Multi-repo | ✅ Tak | ⚠️ Manual | ✅ Tak |
| Custom tooling | ✅ Główny use case | ✅ Tak | ⚠️ Ograniczone |
| Koszt wejścia | Niski | Wysoki (dev time) | Średni |
| Maintenance | Minimalne | Wysokie | Średnie |
Jak działa Greptile?
Architektura
┌─────────────────────────────────────────────────────────┐
│ Twoja aplikacja │
│ (Slack bot, CLI tool, Web app, VS Code extension) │
└─────────────────────────┬───────────────────────────────┘
│ REST API
▼
┌─────────────────────────────────────────────────────────┐
│ Greptile API │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Query │ │ Index │ │ Search │ │
│ │ Endpoint │ │ Endpoint │ │ Endpoint │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Codebase Index │ │
│ │ - Embeddings (semantic understanding) │ │
│ │ - Code graph (dependencies, definitions) │ │
│ │ - File relationships │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ GitHub / GitLab │
│ (Twoje repozytoria) │
└─────────────────────────────────────────────────────────┘Proces indeksowania
- Połącz repo - Greptile pobiera kod z GitHub/GitLab
- Parsowanie - Kod jest analizowany i dzielony na chunks
- Embeddings - Tworzenie wektorowych reprezentacji dla semantic search
- Indeksowanie - Budowanie indeksu dla szybkiego wyszukiwania
- Aktualizacja - Webhooks automatycznie aktualizują indeks
Instalacja i konfiguracja
Uzyskanie API key
- Zarejestruj się na greptile.com
- Przejdź do Dashboard → API Keys
- Kliknij "Create API Key"
- Skopiuj klucz (widoczny tylko raz)
Zmienne środowiskowe
# .env
GREPTILE_API_KEY=your_api_key_here
GITHUB_TOKEN=ghp_xxx # Opcjonalnie, dla prywatnych repoAPI Reference
Połączenie repozytorium
async function connectRepository(owner: string, repo: string) {
const response = await fetch('https://api.greptile.com/v2/repositories', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
remote: 'github',
repository: `${owner}/${repo}`,
branch: 'main',
}),
})
if (!response.ok) {
throw new Error(`Failed to connect repository: ${response.statusText}`)
}
return await response.json()
}
const result = await connectRepository('myorg', 'my-project')
console.log('Repository connected:', result)Sprawdzenie statusu indeksowania
async function getIndexStatus(owner: string, repo: string) {
const response = await fetch(
`https://api.greptile.com/v2/repositories/${owner}/${repo}/status`,
{
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
},
}
)
return await response.json()
}
const status = await getIndexStatus('myorg', 'my-project')
console.log('Index status:', status)
// { status: 'completed', files_indexed: 1234, last_updated: '...' }Query - Pytanie o codebase
interface QueryRequest {
messages: Array<{
role: 'user' | 'assistant'
content: string
}>
repositories: Array<{
remote: 'github' | 'gitlab'
repository: string
branch: string
}>
sessionId?: string
stream?: boolean
}
interface QueryResponse {
message: string
sources: Array<{
repository: string
filepath: string
linestart: number
lineend: number
summary: string
}>
}
async function queryCodebase(
question: string,
repos: string[]
): Promise<QueryResponse> {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: question,
},
],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
if (!response.ok) {
throw new Error(`Query failed: ${response.statusText}`)
}
return await response.json()
}
const answer = await queryCodebase(
'How does authentication work in this codebase?',
['myorg/backend', 'myorg/auth-service']
)
console.log('Answer:', answer.message)
console.log('Sources:', answer.sources)Search - Wyszukiwanie bez AI
interface SearchRequest {
query: string
repositories: string[]
options?: {
limit?: number
includeContent?: boolean
}
}
interface SearchResult {
repository: string
filepath: string
linestart: number
lineend: number
content: string
score: number
}
async function searchCode(
query: string,
repos: string[]
): Promise<SearchResult[]> {
const response = await fetch('https://api.greptile.com/v2/search', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
repositories: repos.map(repo => `github:${repo}`),
options: {
limit: 10,
includeContent: true,
},
}),
})
return await response.json()
}
const results = await searchCode(
'user authentication',
['myorg/backend']
)
results.forEach(result => {
console.log(`${result.filepath}:${result.linestart}`)
console.log(result.content)
})Streaming responses
async function streamQuery(question: string, repos: string[]) {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
stream: true,
}),
})
const reader = response.body?.getReader()
const decoder = new TextDecoder()
while (reader) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
process.stdout.write(chunk)
}
}Use Cases
1. Custom AI Chat
Zbuduj własnego chatbota, który zna twój codebase:
// app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server'
const GREPTILE_API_KEY = process.env.GREPTILE_API_KEY!
const REPOS = ['myorg/frontend', 'myorg/backend', 'myorg/docs']
export async function POST(request: NextRequest) {
const { message, history } = await request.json()
try {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
...history.map((msg: any) => ({
role: msg.role,
content: msg.content,
})),
{ role: 'user', content: message },
],
repositories: REPOS.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
const data = await response.json()
return NextResponse.json({
answer: data.message,
sources: data.sources,
})
} catch (error) {
console.error('Greptile error:', error)
return NextResponse.json(
{ error: 'Failed to process query' },
{ status: 500 }
)
}
}2. PR Review Bot
Automatyczny code review dla pull requestów:
// lib/pr-reviewer.ts
import { Octokit } from '@octokit/rest'
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
interface PRReviewResult {
summary: string
issues: Array<{
file: string
line: number
severity: 'error' | 'warning' | 'suggestion'
message: string
}>
}
async function reviewPR(
owner: string,
repo: string,
prNumber: number
): Promise<PRReviewResult> {
const { data: diff } = await octokit.pulls.get({
owner,
repo,
pull_number: prNumber,
mediaType: { format: 'diff' },
})
const { data: pr } = await octokit.pulls.get({
owner,
repo,
pull_number: prNumber,
})
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: `Review this pull request diff considering our codebase conventions and patterns.
PR Title: ${pr.title}
PR Description: ${pr.body || 'No description'}
Diff:
${diff}
Please identify:
1. Potential bugs or issues
2. Code style inconsistencies with our codebase
3. Missing error handling
4. Security concerns
5. Performance issues
Format each issue as:
FILE: [filename]
LINE: [line number]
SEVERITY: [error|warning|suggestion]
MESSAGE: [description]`,
},
],
repositories: [
{
remote: 'github',
repository: `${owner}/${repo}`,
branch: 'main',
},
],
}),
})
const data = await response.json()
return parseReviewResponse(data.message)
}
function parseReviewResponse(response: string): PRReviewResult {
const issues: PRReviewResult['issues'] = []
const lines = response.split('\n')
let currentIssue: Partial<PRReviewResult['issues'][0]> = {}
for (const line of lines) {
if (line.startsWith('FILE:')) {
currentIssue.file = line.replace('FILE:', '').trim()
} else if (line.startsWith('LINE:')) {
currentIssue.line = parseInt(line.replace('LINE:', '').trim())
} else if (line.startsWith('SEVERITY:')) {
currentIssue.severity = line.replace('SEVERITY:', '').trim() as any
} else if (line.startsWith('MESSAGE:')) {
currentIssue.message = line.replace('MESSAGE:', '').trim()
if (currentIssue.file && currentIssue.message) {
issues.push(currentIssue as PRReviewResult['issues'][0])
currentIssue = {}
}
}
}
return {
summary: response.split('\n\n')[0] || 'Review completed',
issues,
}
}
// app/api/webhooks/github/route.ts
export async function POST(request: NextRequest) {
const event = request.headers.get('x-github-event')
const payload = await request.json()
if (event === 'pull_request' && payload.action === 'opened') {
const { owner, repo } = payload.repository
const prNumber = payload.pull_request.number
const review = await reviewPR(owner.login, repo, prNumber)
await octokit.pulls.createReview({
owner: owner.login,
repo,
pull_number: prNumber,
body: `## AI Code Review\n\n${review.summary}`,
event: 'COMMENT',
comments: review.issues.map(issue => ({
path: issue.file,
line: issue.line,
body: `**${issue.severity.toUpperCase()}**: ${issue.message}`,
})),
})
}
return NextResponse.json({ received: true })
}3. Documentation Generator
Automatyczne generowanie dokumentacji z kodu:
// scripts/generate-docs.ts
interface DocSection {
title: string
content: string
codeExamples: string[]
}
async function generateDocumentation(
repo: string,
module: string
): Promise<DocSection[]> {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: `Generate comprehensive documentation for the ${module} module.
Include:
1. Overview - what this module does
2. Installation/Setup - how to set it up
3. API Reference - all public functions/classes with:
- Description
- Parameters
- Return values
- Example usage
4. Best Practices - recommended patterns
5. Common Issues - troubleshooting
Format as markdown with code examples.`,
},
],
repositories: [
{
remote: 'github',
repository: repo,
branch: 'main',
},
],
}),
})
const data = await response.json()
return parseMarkdownSections(data.message)
}
async function main() {
const docs = await generateDocumentation(
'myorg/backend',
'authentication'
)
const markdown = docs
.map(section => `## ${section.title}\n\n${section.content}`)
.join('\n\n')
fs.writeFileSync('docs/authentication.md', markdown)
}4. Slack Bot
Slack bot odpowiadający na pytania o kodzie:
// app/api/slack/route.ts
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
const SLACK_SIGNING_SECRET = process.env.SLACK_SIGNING_SECRET!
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN!
const GREPTILE_API_KEY = process.env.GREPTILE_API_KEY!
function verifySlackSignature(
signature: string,
timestamp: string,
body: string
): boolean {
const sigBasestring = `v0:${timestamp}:${body}`
const mySignature = 'v0=' + crypto
.createHmac('sha256', SLACK_SIGNING_SECRET)
.update(sigBasestring)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(mySignature),
Buffer.from(signature)
)
}
export async function POST(request: NextRequest) {
const body = await request.text()
const timestamp = request.headers.get('x-slack-request-timestamp')!
const signature = request.headers.get('x-slack-signature')!
if (!verifySlackSignature(signature, timestamp, body)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
const payload = JSON.parse(body)
if (payload.type === 'url_verification') {
return NextResponse.json({ challenge: payload.challenge })
}
if (payload.event?.type === 'app_mention') {
const { text, channel, thread_ts, ts } = payload.event
const question = text.replace(/<@[^>]+>/g, '').trim()
await handleQuestion(channel, thread_ts || ts, question)
}
return NextResponse.json({ ok: true })
}
async function handleQuestion(
channel: string,
threadTs: string,
question: string
) {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: [
{ remote: 'github', repository: 'myorg/backend', branch: 'main' },
{ remote: 'github', repository: 'myorg/frontend', branch: 'main' },
],
}),
})
const data = await response.json()
const sources = data.sources
.slice(0, 3)
.map((s: any) => `• \`${s.filepath}:${s.linestart}\``)
.join('\n')
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel,
thread_ts: threadTs,
text: data.message,
blocks: [
{
type: 'section',
text: { type: 'mrkdwn', text: data.message },
},
{
type: 'context',
elements: [
{
type: 'mrkdwn',
text: `*Sources:*\n${sources}`,
},
],
},
],
}),
})
}5. VS Code Extension
Własne rozszerzenie VS Code z Greptile:
// extension/src/extension.ts
import * as vscode from 'vscode'
const GREPTILE_API_KEY = process.env.GREPTILE_API_KEY!
export function activate(context: vscode.ExtensionContext) {
const explainCommand = vscode.commands.registerCommand(
'greptile.explain',
async () => {
const editor = vscode.window.activeTextEditor
if (!editor) return
const selection = editor.selection
const selectedText = editor.document.getText(selection)
if (!selectedText) {
vscode.window.showWarningMessage('Please select some code first')
return
}
const answer = await queryGreptile(
`Explain this code in the context of our codebase:\n\n${selectedText}`
)
const panel = vscode.window.createWebviewPanel(
'greptileExplanation',
'Code Explanation',
vscode.ViewColumn.Beside,
{}
)
panel.webview.html = `
<html>
<body>
<h2>Explanation</h2>
<pre>${answer.message}</pre>
<h3>Related files</h3>
<ul>
${answer.sources.map((s: any) =>
`<li>${s.filepath}:${s.linestart}</li>`
).join('')}
</ul>
</body>
</html>
`
}
)
const askCommand = vscode.commands.registerCommand(
'greptile.ask',
async () => {
const question = await vscode.window.showInputBox({
prompt: 'Ask about the codebase',
placeHolder: 'How does authentication work?',
})
if (!question) return
const answer = await queryGreptile(question)
vscode.window.showInformationMessage(
answer.message.substring(0, 200) + '...'
)
}
)
context.subscriptions.push(explainCommand, askCommand)
}
async function queryGreptile(question: string) {
const workspaceFolders = vscode.workspace.workspaceFolders || []
const repos = workspaceFolders.map(folder => {
return getGitRemote(folder.uri.fsPath)
}).filter(Boolean)
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
return await response.json()
}6. CLI Tool
Własne narzędzie CLI:
#!/usr/bin/env node
// cli/greptile-cli.ts
import { Command } from 'commander'
import inquirer from 'inquirer'
import chalk from 'chalk'
import ora from 'ora'
const program = new Command()
program
.name('greptile')
.description('Query your codebase using AI')
.version('1.0.0')
program
.command('ask <question>')
.description('Ask a question about the codebase')
.option('-r, --repo <repos...>', 'Repositories to search')
.action(async (question, options) => {
const spinner = ora('Querying codebase...').start()
try {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: options.repo.map((repo: string) => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
const data = await response.json()
spinner.stop()
console.log('\n' + chalk.green('Answer:'))
console.log(data.message)
console.log('\n' + chalk.blue('Sources:'))
data.sources.forEach((source: any) => {
console.log(chalk.gray(` ${source.filepath}:${source.linestart}`))
})
} catch (error) {
spinner.fail('Query failed')
console.error(error)
}
})
program
.command('chat')
.description('Start an interactive chat session')
.option('-r, --repo <repos...>', 'Repositories to search')
.action(async (options) => {
const history: any[] = []
console.log(chalk.blue('Starting Greptile chat. Type "exit" to quit.\n'))
while (true) {
const { question } = await inquirer.prompt([
{
type: 'input',
name: 'question',
message: chalk.green('You:'),
},
])
if (question.toLowerCase() === 'exit') break
const spinner = ora('Thinking...').start()
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
...history,
{ role: 'user', content: question },
],
repositories: options.repo.map((repo: string) => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
const data = await response.json()
spinner.stop()
history.push({ role: 'user', content: question })
history.push({ role: 'assistant', content: data.message })
console.log(chalk.yellow('\nGreptile:'))
console.log(data.message + '\n')
}
})
program
.command('search <query>')
.description('Search for code (without AI)')
.option('-r, --repo <repos...>', 'Repositories to search')
.option('-n, --limit <number>', 'Max results', '10')
.action(async (query, options) => {
const spinner = ora('Searching...').start()
try {
const response = await fetch('https://api.greptile.com/v2/search', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
repositories: options.repo.map((r: string) => `github:${r}`),
options: { limit: parseInt(options.limit) },
}),
})
const results = await response.json()
spinner.stop()
results.forEach((result: any, i: number) => {
console.log(chalk.blue(`\n${i + 1}. ${result.filepath}:${result.linestart}`))
console.log(chalk.gray(result.content))
})
} catch (error) {
spinner.fail('Search failed')
console.error(error)
}
})
program.parse()Integracje
GitHub App
// app/api/github/install/route.ts
export async function POST(request: NextRequest) {
const { action, repositories, installation } = await request.json()
if (action === 'created' || action === 'added') {
for (const repo of repositories) {
await fetch('https://api.greptile.com/v2/repositories', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
remote: 'github',
repository: repo.full_name,
branch: 'main',
githubInstallationId: installation.id,
}),
})
}
}
return NextResponse.json({ ok: true })
}Webhooks dla real-time updates
// app/api/webhooks/github/push/route.ts
export async function POST(request: NextRequest) {
const { repository, ref } = await request.json()
if (ref !== 'refs/heads/main') {
return NextResponse.json({ skipped: true })
}
await fetch(
`https://api.greptile.com/v2/repositories/${repository.full_name}/reindex`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
},
}
)
return NextResponse.json({ reindexed: true })
}Error handling
interface GreptileError {
error: string
code: string
details?: any
}
async function safeQuery(
question: string,
repos: string[]
): Promise<{ success: true; data: any } | { success: false; error: GreptileError }> {
try {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
if (!response.ok) {
const error = await response.json()
return { success: false, error }
}
const data = await response.json()
return { success: true, data }
} catch (error) {
return {
success: false,
error: {
error: 'Network error',
code: 'NETWORK_ERROR',
details: error,
},
}
}
}
const result = await safeQuery('How does auth work?', ['myorg/backend'])
if (result.success) {
console.log(result.data.message)
} else {
switch (result.error.code) {
case 'RATE_LIMIT':
console.log('Rate limited, try again later')
break
case 'NOT_INDEXED':
console.log('Repository not indexed yet')
break
case 'UNAUTHORIZED':
console.log('Invalid API key')
break
default:
console.log('Error:', result.error.error)
}
}Cennik
Plany Greptile
| Plan | Cena | Queries | Repos | Funkcje |
|---|---|---|---|---|
| Free | $0 | 25/dzień | 3 | Basic API |
| Pro | $20/mo | 1000/mo | 10 | + Priority, Webhooks |
| Team | $50/mo | 5000/mo | 50 | + Team sharing, Analytics |
| Enterprise | Custom | Unlimited | Unlimited | + Self-host, SSO, SLA |
Kluczowe limity
- Free: 25 queries/dzień, 3 repozytoria
- Pro: ~33 queries/dzień średnio, więcej przy burst
- Overage: $0.02 per dodatkowy query
Best practices
Optymalizacja queries
// ❌ Zbyt ogólne pytanie
"What does this code do?"
// ✅ Konkretne pytanie z kontekstem
"How does the UserService handle password reset flow? Include the email template used."Efektywne indeksowanie
// .greptileignore
node_modules/
dist/
build/
*.min.js
*.map
coverage/
__tests__/
*.test.tsCaching responses
import { LRUCache } from 'lru-cache'
const cache = new LRUCache<string, any>({
max: 100,
ttl: 1000 * 60 * 15, // 15 minutes
})
async function cachedQuery(question: string, repos: string[]) {
const cacheKey = `${question}:${repos.join(',')}`
if (cache.has(cacheKey)) {
return cache.get(cacheKey)
}
const result = await queryGreptile(question, repos)
cache.set(cacheKey, result)
return result
}FAQ - Najczęściej zadawane pytania
Czym Greptile różni się od używania OpenAI z własnym RAG?
Greptile oszczędza dziesiątki godzin setupu:
- Automatyczne parsowanie kodu
- Optymalne chunking dla kodu
- Code-aware embeddings
- Gotowy API endpoint
- Automatyczne updates
Czy Greptile wspiera prywatne repozytoria?
Tak, wystarczy przekazać GitHub/GitLab token z odpowiednimi uprawnieniami.
Jak często indeks jest aktualizowany?
- Automatycznie przez webhooks (przy każdym push)
- Możesz też wymusić reindeksowanie przez API
- Pełny reindex trwa kilka minut dla średnich repo
Czy mogę użyć własnego modelu LLM?
Na Enterprise - tak. Możesz podłączyć własne modele lub użyć Greptile tylko do context retrieval.
Jaki model LLM używa Greptile?
Domyślnie Claude (Anthropic). Na wyższych planach można wybierać między modelami.
Greptile - Codebase AI API
What is Greptile?
Greptile is an API that lets you build your own AI tools with full codebase context. Unlike ready-made tools like GitHub Copilot or Cody, Greptile provides the infrastructure and API for creating your own solutions - from chatbots through PR review bots to documentation generators.
Greptile indexes your repositories from GitHub or GitLab, creates embeddings, and exposes a simple API for searching and answering questions about code. This way you can build custom AI assistants that understand your specific project, its architecture, and conventions.
Why Greptile?
Key advantages of Greptile:
- Simple API - REST API, easy to integrate in a few hours
- Full customization - Build exactly what you need
- Codebase context - AI understands the entire project, not just a single file
- Multi-repo support - Combine knowledge from multiple repositories
- Privacy-first - Self-hosting option available on Enterprise
- Model-agnostic - Use your own LLM models or Greptile defaults
- Real-time updates - Automatic index updates on changes
Greptile vs alternatives
| Feature | Greptile | OpenAI API + RAG | Sourcegraph API |
|---|---|---|---|
| Type | API for codebase | General LLM API | Code search API |
| Setup | Minutes | Days/weeks | Hours |
| Codebase context | ✅ Automatic | ⚠️ DIY | ✅ Yes |
| Multi-repo | ✅ Yes | ⚠️ Manual | ✅ Yes |
| Custom tooling | ✅ Main use case | ✅ Yes | ⚠️ Limited |
| Entry cost | Low | High (dev time) | Medium |
| Maintenance | Minimal | High | Medium |
How does Greptile work?
Architecture
┌─────────────────────────────────────────────────────────┐
│ Your application │
│ (Slack bot, CLI tool, Web app, VS Code extension) │
└─────────────────────────┬───────────────────────────────┘
│ REST API
▼
┌─────────────────────────────────────────────────────────┐
│ Greptile API │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────────┐ │
│ │ Query │ │ Index │ │ Search │ │
│ │ Endpoint │ │ Endpoint │ │ Endpoint │ │
│ └─────────────┘ └─────────────┘ └─────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────┐ │
│ │ Codebase Index │ │
│ │ - Embeddings (semantic understanding) │ │
│ │ - Code graph (dependencies, definitions) │ │
│ │ - File relationships │ │
│ └───────────────────────────────────────────────────┘ │
└─────────────────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────┐
│ GitHub / GitLab │
│ (Your repositories) │
└─────────────────────────────────────────────────────────┘Indexing process
- Connect repo - Greptile pulls the code from GitHub/GitLab
- Parsing - The code is analyzed and split into chunks
- Embeddings - Creating vector representations for semantic search
- Indexing - Building an index for fast retrieval
- Updating - Webhooks automatically update the index
Installation and setup
Getting an API key
- Sign up at greptile.com
- Go to Dashboard → API Keys
- Click "Create API Key"
- Copy the key (visible only once)
Environment variables
# .env
GREPTILE_API_KEY=your_api_key_here
GITHUB_TOKEN=ghp_xxx # Optional, for private reposAPI reference
Connecting a repository
async function connectRepository(owner: string, repo: string) {
const response = await fetch('https://api.greptile.com/v2/repositories', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
remote: 'github',
repository: `${owner}/${repo}`,
branch: 'main',
}),
})
if (!response.ok) {
throw new Error(`Failed to connect repository: ${response.statusText}`)
}
return await response.json()
}
const result = await connectRepository('myorg', 'my-project')
console.log('Repository connected:', result)Checking index status
async function getIndexStatus(owner: string, repo: string) {
const response = await fetch(
`https://api.greptile.com/v2/repositories/${owner}/${repo}/status`,
{
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
},
}
)
return await response.json()
}
const status = await getIndexStatus('myorg', 'my-project')
console.log('Index status:', status)
// { status: 'completed', files_indexed: 1234, last_updated: '...' }Query - asking about the codebase
interface QueryRequest {
messages: Array<{
role: 'user' | 'assistant'
content: string
}>
repositories: Array<{
remote: 'github' | 'gitlab'
repository: string
branch: string
}>
sessionId?: string
stream?: boolean
}
interface QueryResponse {
message: string
sources: Array<{
repository: string
filepath: string
linestart: number
lineend: number
summary: string
}>
}
async function queryCodebase(
question: string,
repos: string[]
): Promise<QueryResponse> {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: question,
},
],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
if (!response.ok) {
throw new Error(`Query failed: ${response.statusText}`)
}
return await response.json()
}
const answer = await queryCodebase(
'How does authentication work in this codebase?',
['myorg/backend', 'myorg/auth-service']
)
console.log('Answer:', answer.message)
console.log('Sources:', answer.sources)Search - searching without AI
interface SearchRequest {
query: string
repositories: string[]
options?: {
limit?: number
includeContent?: boolean
}
}
interface SearchResult {
repository: string
filepath: string
linestart: number
lineend: number
content: string
score: number
}
async function searchCode(
query: string,
repos: string[]
): Promise<SearchResult[]> {
const response = await fetch('https://api.greptile.com/v2/search', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
repositories: repos.map(repo => `github:${repo}`),
options: {
limit: 10,
includeContent: true,
},
}),
})
return await response.json()
}
const results = await searchCode(
'user authentication',
['myorg/backend']
)
results.forEach(result => {
console.log(`${result.filepath}:${result.linestart}`)
console.log(result.content)
})Streaming responses
async function streamQuery(question: string, repos: string[]) {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
stream: true,
}),
})
const reader = response.body?.getReader()
const decoder = new TextDecoder()
while (reader) {
const { done, value } = await reader.read()
if (done) break
const chunk = decoder.decode(value)
process.stdout.write(chunk)
}
}Use cases
1. Custom AI chat
Build your own chatbot that knows your codebase:
// app/api/chat/route.ts
import { NextRequest, NextResponse } from 'next/server'
const GREPTILE_API_KEY = process.env.GREPTILE_API_KEY!
const REPOS = ['myorg/frontend', 'myorg/backend', 'myorg/docs']
export async function POST(request: NextRequest) {
const { message, history } = await request.json()
try {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
...history.map((msg: any) => ({
role: msg.role,
content: msg.content,
})),
{ role: 'user', content: message },
],
repositories: REPOS.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
const data = await response.json()
return NextResponse.json({
answer: data.message,
sources: data.sources,
})
} catch (error) {
console.error('Greptile error:', error)
return NextResponse.json(
{ error: 'Failed to process query' },
{ status: 500 }
)
}
}2. PR review bot
Automatic code review for pull requests:
// lib/pr-reviewer.ts
import { Octokit } from '@octokit/rest'
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })
interface PRReviewResult {
summary: string
issues: Array<{
file: string
line: number
severity: 'error' | 'warning' | 'suggestion'
message: string
}>
}
async function reviewPR(
owner: string,
repo: string,
prNumber: number
): Promise<PRReviewResult> {
const { data: diff } = await octokit.pulls.get({
owner,
repo,
pull_number: prNumber,
mediaType: { format: 'diff' },
})
const { data: pr } = await octokit.pulls.get({
owner,
repo,
pull_number: prNumber,
})
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: `Review this pull request diff considering our codebase conventions and patterns.
PR Title: ${pr.title}
PR Description: ${pr.body || 'No description'}
Diff:
${diff}
Please identify:
1. Potential bugs or issues
2. Code style inconsistencies with our codebase
3. Missing error handling
4. Security concerns
5. Performance issues
Format each issue as:
FILE: [filename]
LINE: [line number]
SEVERITY: [error|warning|suggestion]
MESSAGE: [description]`,
},
],
repositories: [
{
remote: 'github',
repository: `${owner}/${repo}`,
branch: 'main',
},
],
}),
})
const data = await response.json()
return parseReviewResponse(data.message)
}
function parseReviewResponse(response: string): PRReviewResult {
const issues: PRReviewResult['issues'] = []
const lines = response.split('\n')
let currentIssue: Partial<PRReviewResult['issues'][0]> = {}
for (const line of lines) {
if (line.startsWith('FILE:')) {
currentIssue.file = line.replace('FILE:', '').trim()
} else if (line.startsWith('LINE:')) {
currentIssue.line = parseInt(line.replace('LINE:', '').trim())
} else if (line.startsWith('SEVERITY:')) {
currentIssue.severity = line.replace('SEVERITY:', '').trim() as any
} else if (line.startsWith('MESSAGE:')) {
currentIssue.message = line.replace('MESSAGE:', '').trim()
if (currentIssue.file && currentIssue.message) {
issues.push(currentIssue as PRReviewResult['issues'][0])
currentIssue = {}
}
}
}
return {
summary: response.split('\n\n')[0] || 'Review completed',
issues,
}
}
// app/api/webhooks/github/route.ts
export async function POST(request: NextRequest) {
const event = request.headers.get('x-github-event')
const payload = await request.json()
if (event === 'pull_request' && payload.action === 'opened') {
const { owner, repo } = payload.repository
const prNumber = payload.pull_request.number
const review = await reviewPR(owner.login, repo, prNumber)
await octokit.pulls.createReview({
owner: owner.login,
repo,
pull_number: prNumber,
body: `## AI Code Review\n\n${review.summary}`,
event: 'COMMENT',
comments: review.issues.map(issue => ({
path: issue.file,
line: issue.line,
body: `**${issue.severity.toUpperCase()}**: ${issue.message}`,
})),
})
}
return NextResponse.json({ received: true })
}3. Documentation generator
Automatic documentation generation from code:
// scripts/generate-docs.ts
interface DocSection {
title: string
content: string
codeExamples: string[]
}
async function generateDocumentation(
repo: string,
module: string
): Promise<DocSection[]> {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
{
role: 'user',
content: `Generate comprehensive documentation for the ${module} module.
Include:
1. Overview - what this module does
2. Installation/Setup - how to set it up
3. API Reference - all public functions/classes with:
- Description
- Parameters
- Return values
- Example usage
4. Best Practices - recommended patterns
5. Common Issues - troubleshooting
Format as markdown with code examples.`,
},
],
repositories: [
{
remote: 'github',
repository: repo,
branch: 'main',
},
],
}),
})
const data = await response.json()
return parseMarkdownSections(data.message)
}
async function main() {
const docs = await generateDocumentation(
'myorg/backend',
'authentication'
)
const markdown = docs
.map(section => `## ${section.title}\n\n${section.content}`)
.join('\n\n')
fs.writeFileSync('docs/authentication.md', markdown)
}4. Slack bot
A Slack bot that answers questions about the code:
// app/api/slack/route.ts
import { NextRequest, NextResponse } from 'next/server'
import crypto from 'crypto'
const SLACK_SIGNING_SECRET = process.env.SLACK_SIGNING_SECRET!
const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN!
const GREPTILE_API_KEY = process.env.GREPTILE_API_KEY!
function verifySlackSignature(
signature: string,
timestamp: string,
body: string
): boolean {
const sigBasestring = `v0:${timestamp}:${body}`
const mySignature = 'v0=' + crypto
.createHmac('sha256', SLACK_SIGNING_SECRET)
.update(sigBasestring)
.digest('hex')
return crypto.timingSafeEqual(
Buffer.from(mySignature),
Buffer.from(signature)
)
}
export async function POST(request: NextRequest) {
const body = await request.text()
const timestamp = request.headers.get('x-slack-request-timestamp')!
const signature = request.headers.get('x-slack-signature')!
if (!verifySlackSignature(signature, timestamp, body)) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 })
}
const payload = JSON.parse(body)
if (payload.type === 'url_verification') {
return NextResponse.json({ challenge: payload.challenge })
}
if (payload.event?.type === 'app_mention') {
const { text, channel, thread_ts, ts } = payload.event
const question = text.replace(/<@[^>]+>/g, '').trim()
await handleQuestion(channel, thread_ts || ts, question)
}
return NextResponse.json({ ok: true })
}
async function handleQuestion(
channel: string,
threadTs: string,
question: string
) {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: [
{ remote: 'github', repository: 'myorg/backend', branch: 'main' },
{ remote: 'github', repository: 'myorg/frontend', branch: 'main' },
],
}),
})
const data = await response.json()
const sources = data.sources
.slice(0, 3)
.map((s: any) => `• \`${s.filepath}:${s.linestart}\``)
.join('\n')
await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${SLACK_BOT_TOKEN}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
channel,
thread_ts: threadTs,
text: data.message,
blocks: [
{
type: 'section',
text: { type: 'mrkdwn', text: data.message },
},
{
type: 'context',
elements: [
{
type: 'mrkdwn',
text: `*Sources:*\n${sources}`,
},
],
},
],
}),
})
}5. VS Code extension
A custom VS Code extension with Greptile:
// extension/src/extension.ts
import * as vscode from 'vscode'
const GREPTILE_API_KEY = process.env.GREPTILE_API_KEY!
export function activate(context: vscode.ExtensionContext) {
const explainCommand = vscode.commands.registerCommand(
'greptile.explain',
async () => {
const editor = vscode.window.activeTextEditor
if (!editor) return
const selection = editor.selection
const selectedText = editor.document.getText(selection)
if (!selectedText) {
vscode.window.showWarningMessage('Please select some code first')
return
}
const answer = await queryGreptile(
`Explain this code in the context of our codebase:\n\n${selectedText}`
)
const panel = vscode.window.createWebviewPanel(
'greptileExplanation',
'Code Explanation',
vscode.ViewColumn.Beside,
{}
)
panel.webview.html = `
<html>
<body>
<h2>Explanation</h2>
<pre>${answer.message}</pre>
<h3>Related files</h3>
<ul>
${answer.sources.map((s: any) =>
`<li>${s.filepath}:${s.linestart}</li>`
).join('')}
</ul>
</body>
</html>
`
}
)
const askCommand = vscode.commands.registerCommand(
'greptile.ask',
async () => {
const question = await vscode.window.showInputBox({
prompt: 'Ask about the codebase',
placeHolder: 'How does authentication work?',
})
if (!question) return
const answer = await queryGreptile(question)
vscode.window.showInformationMessage(
answer.message.substring(0, 200) + '...'
)
}
)
context.subscriptions.push(explainCommand, askCommand)
}
async function queryGreptile(question: string) {
const workspaceFolders = vscode.workspace.workspaceFolders || []
const repos = workspaceFolders.map(folder => {
return getGitRemote(folder.uri.fsPath)
}).filter(Boolean)
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
return await response.json()
}6. CLI tool
A custom CLI tool:
#!/usr/bin/env node
// cli/greptile-cli.ts
import { Command } from 'commander'
import inquirer from 'inquirer'
import chalk from 'chalk'
import ora from 'ora'
const program = new Command()
program
.name('greptile')
.description('Query your codebase using AI')
.version('1.0.0')
program
.command('ask <question>')
.description('Ask a question about the codebase')
.option('-r, --repo <repos...>', 'Repositories to search')
.action(async (question, options) => {
const spinner = ora('Querying codebase...').start()
try {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: options.repo.map((repo: string) => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
const data = await response.json()
spinner.stop()
console.log('\n' + chalk.green('Answer:'))
console.log(data.message)
console.log('\n' + chalk.blue('Sources:'))
data.sources.forEach((source: any) => {
console.log(chalk.gray(` ${source.filepath}:${source.linestart}`))
})
} catch (error) {
spinner.fail('Query failed')
console.error(error)
}
})
program
.command('chat')
.description('Start an interactive chat session')
.option('-r, --repo <repos...>', 'Repositories to search')
.action(async (options) => {
const history: any[] = []
console.log(chalk.blue('Starting Greptile chat. Type "exit" to quit.\n'))
while (true) {
const { question } = await inquirer.prompt([
{
type: 'input',
name: 'question',
message: chalk.green('You:'),
},
])
if (question.toLowerCase() === 'exit') break
const spinner = ora('Thinking...').start()
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [
...history,
{ role: 'user', content: question },
],
repositories: options.repo.map((repo: string) => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
const data = await response.json()
spinner.stop()
history.push({ role: 'user', content: question })
history.push({ role: 'assistant', content: data.message })
console.log(chalk.yellow('\nGreptile:'))
console.log(data.message + '\n')
}
})
program
.command('search <query>')
.description('Search for code (without AI)')
.option('-r, --repo <repos...>', 'Repositories to search')
.option('-n, --limit <number>', 'Max results', '10')
.action(async (query, options) => {
const spinner = ora('Searching...').start()
try {
const response = await fetch('https://api.greptile.com/v2/search', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
query,
repositories: options.repo.map((r: string) => `github:${r}`),
options: { limit: parseInt(options.limit) },
}),
})
const results = await response.json()
spinner.stop()
results.forEach((result: any, i: number) => {
console.log(chalk.blue(`\n${i + 1}. ${result.filepath}:${result.linestart}`))
console.log(chalk.gray(result.content))
})
} catch (error) {
spinner.fail('Search failed')
console.error(error)
}
})
program.parse()Integrations
GitHub App
// app/api/github/install/route.ts
export async function POST(request: NextRequest) {
const { action, repositories, installation } = await request.json()
if (action === 'created' || action === 'added') {
for (const repo of repositories) {
await fetch('https://api.greptile.com/v2/repositories', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
remote: 'github',
repository: repo.full_name,
branch: 'main',
githubInstallationId: installation.id,
}),
})
}
}
return NextResponse.json({ ok: true })
}Webhooks for real-time updates
// app/api/webhooks/github/push/route.ts
export async function POST(request: NextRequest) {
const { repository, ref } = await request.json()
if (ref !== 'refs/heads/main') {
return NextResponse.json({ skipped: true })
}
await fetch(
`https://api.greptile.com/v2/repositories/${repository.full_name}/reindex`,
{
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
},
}
)
return NextResponse.json({ reindexed: true })
}Error handling
interface GreptileError {
error: string
code: string
details?: any
}
async function safeQuery(
question: string,
repos: string[]
): Promise<{ success: true; data: any } | { success: false; error: GreptileError }> {
try {
const response = await fetch('https://api.greptile.com/v2/query', {
method: 'POST',
headers: {
'Authorization': `Bearer ${process.env.GREPTILE_API_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
messages: [{ role: 'user', content: question }],
repositories: repos.map(repo => ({
remote: 'github',
repository: repo,
branch: 'main',
})),
}),
})
if (!response.ok) {
const error = await response.json()
return { success: false, error }
}
const data = await response.json()
return { success: true, data }
} catch (error) {
return {
success: false,
error: {
error: 'Network error',
code: 'NETWORK_ERROR',
details: error,
},
}
}
}
const result = await safeQuery('How does auth work?', ['myorg/backend'])
if (result.success) {
console.log(result.data.message)
} else {
switch (result.error.code) {
case 'RATE_LIMIT':
console.log('Rate limited, try again later')
break
case 'NOT_INDEXED':
console.log('Repository not indexed yet')
break
case 'UNAUTHORIZED':
console.log('Invalid API key')
break
default:
console.log('Error:', result.error.error)
}
}Pricing
Greptile plans
| Plan | Price | Queries | Repos | Features |
|---|---|---|---|---|
| Free | $0 | 25/day | 3 | Basic API |
| Pro | $20/mo | 1000/mo | 10 | + Priority, Webhooks |
| Team | $50/mo | 5000/mo | 50 | + Team sharing, Analytics |
| Enterprise | Custom | Unlimited | Unlimited | + Self-host, SSO, SLA |
Key limits
- Free: 25 queries/day, 3 repositories
- Pro: ~33 queries/day on average, more with burst
- Overage: $0.02 per additional query
Best practices
Optimizing queries
// ❌ Too general question
"What does this code do?"
// ✅ Specific question with context
"How does the UserService handle password reset flow? Include the email template used."Efficient indexing
// .greptileignore
node_modules/
dist/
build/
*.min.js
*.map
coverage/
__tests__/
*.test.tsCaching responses
import { LRUCache } from 'lru-cache'
const cache = new LRUCache<string, any>({
max: 100,
ttl: 1000 * 60 * 15, // 15 minutes
})
async function cachedQuery(question: string, repos: string[]) {
const cacheKey = `${question}:${repos.join(',')}`
if (cache.has(cacheKey)) {
return cache.get(cacheKey)
}
const result = await queryGreptile(question, repos)
cache.set(cacheKey, result)
return result
}FAQ - frequently asked questions
How is Greptile different from using OpenAI with your own RAG?
Greptile saves dozens of hours of setup:
- Automatic code parsing
- Optimal chunking for code
- Code-aware embeddings
- Ready-to-use API endpoint
- Automatic updates
Does Greptile support private repositories?
Yes, you just need to provide a GitHub/GitLab token with the appropriate permissions.
How often is the index updated?
- Automatically via webhooks (on every push)
- You can also force reindexing through the API
- A full reindex takes a few minutes for medium-sized repos
Can I use my own LLM model?
On Enterprise - yes. You can connect your own models or use Greptile solely for context retrieval.
What LLM model does Greptile use?
Claude (Anthropic) by default. On higher plans you can choose between models.