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.