mirror of
https://github.com/simstudioai/sim.git
synced 2026-01-07 22:24:06 -05:00
feat(tools): added greptile tools/block, updated copilot panel styling (#2618)
This commit is contained in:
@@ -4344,3 +4344,16 @@ export function CirclebackIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GreptileIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
clipRule='evenodd'
|
||||
fillRule='evenodd'
|
||||
fill='#44A775'
|
||||
d='M3.353.004a6.074 6.074 0 01-.265.045C2.63.12 2.092.348 1.71.633 1.426.846.717 1.575.557 1.819a3.359 3.359 0 00-.23 3.296c.154.322.35.59.71.972.187.198.434.486.55.64a6.629 6.629 0 011.305 3.546c.01.138.035 1.607.057 3.264.043 3.273.038 3.18.203 3.485.266.494.94.79 1.474.648.29-.077.463-.204 1.353-.986.957-.84 1.092-.932 1.446-.98.124-.017.631 0 1.66.053 1.513.08 1.622.079 1.85-.016.393-.164.539-.4.661-1.074.247-1.36 1.296-2.56 2.64-3.022.116-.04.373-.104.572-.144.198-.04.426-.102.506-.138.296-.136.515-.424.566-.744.017-.11-.007-.549-.089-1.602-.091-1.179-.107-1.483-.083-1.621.057-.342.139-.46 1.01-1.448.447-.506.85-.976.895-1.043.262-.39.288-.91.068-1.345a1.44 1.44 0 00-.822-.67c-.1-.029-.834-.037-3.544-.038H9.897l-.335-.063c-.958-.179-1.765-.49-2.484-.958-.362-.236-.583-.41-1.018-.804-.408-.37-.59-.502-.921-.67A3.018 3.018 0 003.744.005a3.942 3.942 0 00-.391 0zm15.728 5.858c-.132.049-.217.127-.48.44-.592.707-.74 1.336-.531 2.256.106.466.163.572.361.673.105.054.169.055 2.637.046l2.53-.009.118-.063a.551.551 0 00.095-.895 184.88 184.88 0 00-2.223-1.254c-2.293-1.282-2.281-1.276-2.507-1.194zm-3.216 6.71a9.258 9.258 0 00-1.364.696c-.844.557-1.454 1.36-1.923 2.53-.211.525-.202.75.04.935.111.087 6.478 3.14 6.667 3.198.153.047.27.027.43-.074a.538.538 0 00.24-.434c0-.06-.03-.18-.065-.264-.156-.368-3.098-6.467-3.158-6.545-.168-.222-.394-.232-.867-.042zm-8.48 5.457c-.453.102-.83.32-1.285.745-.296.277-.336.468-.167.798.053.103.61 1.104 1.236 2.224 1.297 2.317 1.22 2.206 1.558 2.202.152-.002.198-.015.296-.084a.662.662 0 00.173-.193c.058-.11.06-.152.08-2.595.018-1.93.015-2.51-.011-2.606a.569.569 0 00-.138-.227c-.09-.091-.14-.112-.406-.176-.582-.138-.992-.165-1.336-.088z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ import {
|
||||
GoogleVaultIcon,
|
||||
GrafanaIcon,
|
||||
GrainIcon,
|
||||
GreptileIcon,
|
||||
HubspotIcon,
|
||||
HuggingFaceIcon,
|
||||
HunterIOIcon,
|
||||
@@ -158,6 +159,7 @@ export const blockTypeToIconMap: Record<string, IconComponent> = {
|
||||
google_vault: GoogleVaultIcon,
|
||||
grafana: GrafanaIcon,
|
||||
grain: GrainIcon,
|
||||
greptile: GreptileIcon,
|
||||
hubspot: HubspotIcon,
|
||||
huggingface: HuggingFaceIcon,
|
||||
hunter: HunterIOIcon,
|
||||
|
||||
141
apps/docs/content/docs/en/tools/greptile.mdx
Normal file
141
apps/docs/content/docs/en/tools/greptile.mdx
Normal file
@@ -0,0 +1,141 @@
|
||||
---
|
||||
title: Greptile
|
||||
description: AI-powered codebase search and Q&A
|
||||
---
|
||||
|
||||
import { BlockInfoCard } from "@/components/ui/block-info-card"
|
||||
|
||||
<BlockInfoCard
|
||||
type="greptile"
|
||||
color="#e5e5e5"
|
||||
/>
|
||||
|
||||
{/* MANUAL-CONTENT-START:intro */}
|
||||
[Greptile](https://greptile.com/) is an AI-powered developer tool for searching and querying source code across one or more repositories. Greptile enables engineers to quickly answer complex codebase questions in natural language, locate relevant files or symbols, and gain insights into unfamiliar or legacy code.
|
||||
|
||||
With Greptile, you can:
|
||||
|
||||
- **Ask complex questions about your codebase in natural language**: Get AI-generated answers about architecture, usage patterns, or specific implementations.
|
||||
- **Find relevant code, files, or functions instantly**: Search using keywords or natural language queries and jump right to matching lines, files, or code blocks.
|
||||
- **Understand dependencies and relationships**: Uncover where functions are called, how modules are related, or where APIs are used across large codebases.
|
||||
- **Accelerate onboarding and code exploration**: Quickly ramp up on new projects or debug tricky issues without needing deep prior context.
|
||||
|
||||
The Sim Greptile integration allows your AI agents to:
|
||||
|
||||
- Query and search private and public repositories using Greptile’s advanced language models.
|
||||
- Retrieve contextually relevant code snippets, file references, and explanations to support code review, documentation, and development workflows.
|
||||
- Trigger automations in Sim workflows based on search/query results or embed code intelligence directly into your processes.
|
||||
|
||||
Whether you’re trying to accelerate developer productivity, automate documentation, or supercharge your team’s understanding of a complex codebase, Greptile and Sim provide seamless access to code intelligence and search—right where you need it.
|
||||
{/* MANUAL-CONTENT-END */}
|
||||
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
Query and search codebases using natural language with Greptile. Get AI-generated answers about your code, find relevant files, and understand complex codebases.
|
||||
|
||||
|
||||
|
||||
## Tools
|
||||
|
||||
### `greptile_query`
|
||||
|
||||
Query repositories in natural language and get answers with relevant code references. Greptile uses AI to understand your codebase and answer questions.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | Natural language question about the codebase |
|
||||
| `repositories` | string | Yes | Comma-separated list of repositories. Format: "github:branch:owner/repo" or just "owner/repo" \(defaults to github:main\) |
|
||||
| `sessionId` | string | No | Session ID for conversation continuity |
|
||||
| `genius` | boolean | No | Enable genius mode for more thorough analysis \(slower but more accurate\) |
|
||||
| `apiKey` | string | Yes | Greptile API key |
|
||||
| `githubToken` | string | Yes | GitHub Personal Access Token with repo read access |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `message` | string | AI-generated answer to the query |
|
||||
| `sources` | array | Relevant code references that support the answer |
|
||||
|
||||
### `greptile_search`
|
||||
|
||||
Search repositories in natural language and get relevant code references without generating an answer. Useful for finding specific code locations.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `query` | string | Yes | Natural language search query to find relevant code |
|
||||
| `repositories` | string | Yes | Comma-separated list of repositories. Format: "github:branch:owner/repo" or just "owner/repo" \(defaults to github:main\) |
|
||||
| `sessionId` | string | No | Session ID for conversation continuity |
|
||||
| `genius` | boolean | No | Enable genius mode for more thorough search \(slower but more accurate\) |
|
||||
| `apiKey` | string | Yes | Greptile API key |
|
||||
| `githubToken` | string | Yes | GitHub Personal Access Token with repo read access |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `sources` | array | Relevant code references matching the search query |
|
||||
|
||||
### `greptile_index_repo`
|
||||
|
||||
Submit a repository to be indexed by Greptile. Indexing must complete before the repository can be queried. Small repos take 3-5 minutes, larger ones can take over an hour.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `remote` | string | Yes | Git remote type: github or gitlab |
|
||||
| `repository` | string | Yes | Repository in owner/repo format \(e.g., "facebook/react"\) |
|
||||
| `branch` | string | Yes | Branch to index \(e.g., "main" or "master"\) |
|
||||
| `reload` | boolean | No | Force re-indexing even if already indexed |
|
||||
| `notify` | boolean | No | Send email notification when indexing completes |
|
||||
| `apiKey` | string | Yes | Greptile API key |
|
||||
| `githubToken` | string | Yes | GitHub Personal Access Token with repo read access |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `repositoryId` | string | Unique identifier for the indexed repository \(format: remote:branch:owner/repo\) |
|
||||
| `statusEndpoint` | string | URL endpoint to check indexing status |
|
||||
| `message` | string | Status message about the indexing operation |
|
||||
|
||||
### `greptile_status`
|
||||
|
||||
Check the indexing status of a repository. Use this to verify if a repository is ready to be queried or to monitor indexing progress.
|
||||
|
||||
#### Input
|
||||
|
||||
| Parameter | Type | Required | Description |
|
||||
| --------- | ---- | -------- | ----------- |
|
||||
| `remote` | string | Yes | Git remote type: github or gitlab |
|
||||
| `repository` | string | Yes | Repository in owner/repo format \(e.g., "facebook/react"\) |
|
||||
| `branch` | string | Yes | Branch name \(e.g., "main" or "master"\) |
|
||||
| `apiKey` | string | Yes | Greptile API key |
|
||||
| `githubToken` | string | Yes | GitHub Personal Access Token with repo read access |
|
||||
|
||||
#### Output
|
||||
|
||||
| Parameter | Type | Description |
|
||||
| --------- | ---- | ----------- |
|
||||
| `repository` | string | Repository name \(owner/repo\) |
|
||||
| `remote` | string | Git remote \(github/gitlab\) |
|
||||
| `branch` | string | Branch name |
|
||||
| `private` | boolean | Whether the repository is private |
|
||||
| `status` | string | Indexing status: submitted, cloning, processing, completed, or failed |
|
||||
| `filesProcessed` | number | Number of files processed so far |
|
||||
| `numFiles` | number | Total number of files in the repository |
|
||||
| `sampleQuestions` | array | Sample questions for the indexed repository |
|
||||
| `sha` | string | Git commit SHA of the indexed version |
|
||||
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
- Category: `tools`
|
||||
- Type: `greptile`
|
||||
@@ -37,6 +37,7 @@
|
||||
"google_vault",
|
||||
"grafana",
|
||||
"grain",
|
||||
"greptile",
|
||||
"hubspot",
|
||||
"huggingface",
|
||||
"hunter",
|
||||
|
||||
@@ -115,7 +115,6 @@ interface CopilotMarkdownRendererProps {
|
||||
export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRendererProps) {
|
||||
const [copiedCodeBlocks, setCopiedCodeBlocks] = useState<Record<string, boolean>>({})
|
||||
|
||||
// Reset copy success state after 2 seconds
|
||||
useEffect(() => {
|
||||
const timers: Record<string, NodeJS.Timeout> = {}
|
||||
|
||||
@@ -132,17 +131,14 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
}
|
||||
}, [copiedCodeBlocks])
|
||||
|
||||
// Custom components for react-markdown with current styling - memoized to prevent re-renders
|
||||
const markdownComponents = useMemo(
|
||||
() => ({
|
||||
// Paragraph
|
||||
p: ({ children }: React.HTMLAttributes<HTMLParagraphElement>) => (
|
||||
<p className='mb-1 font-base font-season text-[var(--text-primary)] text-sm leading-[1.25rem] last:mb-0 dark:font-[470]'>
|
||||
<p className='mb-2 font-base font-season text-[var(--text-primary)] text-sm leading-[1.25rem] last:mb-0 dark:font-[470]'>
|
||||
{children}
|
||||
</p>
|
||||
),
|
||||
|
||||
// Headings
|
||||
h1: ({ children }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h1 className='mt-3 mb-3 font-season font-semibold text-2xl text-[var(--text-primary)]'>
|
||||
{children}
|
||||
@@ -159,15 +155,14 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
</h3>
|
||||
),
|
||||
h4: ({ children }: React.HTMLAttributes<HTMLHeadingElement>) => (
|
||||
<h4 className='mt-5 mb-2 font-season font-semibold text-[var(--text-primary)] text-base'>
|
||||
<h4 className='mt-2 mb-2 font-season font-semibold text-[var(--text-primary)] text-base'>
|
||||
{children}
|
||||
</h4>
|
||||
),
|
||||
|
||||
// Lists
|
||||
ul: ({ children }: React.HTMLAttributes<HTMLUListElement>) => (
|
||||
<ul
|
||||
className='mt-1 mb-1 space-y-1 pl-6 font-base font-season text-[var(--text-primary)] dark:font-[470]'
|
||||
className='mt-1 mb-1 space-y-1.5 pl-6 font-base font-season text-[var(--text-primary)] dark:font-[470]'
|
||||
style={{ listStyleType: 'disc' }}
|
||||
>
|
||||
{children}
|
||||
@@ -175,7 +170,7 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
),
|
||||
ol: ({ children }: React.HTMLAttributes<HTMLOListElement>) => (
|
||||
<ol
|
||||
className='mt-1 mb-1 space-y-1 pl-6 font-base font-season text-[var(--text-primary)] dark:font-[470]'
|
||||
className='mt-1 mb-1 space-y-1.5 pl-6 font-base font-season text-[var(--text-primary)] dark:font-[470]'
|
||||
style={{ listStyleType: 'decimal' }}
|
||||
>
|
||||
{children}
|
||||
@@ -193,7 +188,6 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
</li>
|
||||
),
|
||||
|
||||
// Code blocks - render using shared Code.Viewer for consistent styling
|
||||
pre: ({ children }: React.HTMLAttributes<HTMLPreElement>) => {
|
||||
let codeContent: React.ReactNode = children
|
||||
let language = 'code'
|
||||
@@ -210,15 +204,12 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
language = childElement.props.className?.replace('language-', '') || 'code'
|
||||
}
|
||||
|
||||
// Extract actual text content
|
||||
let actualCodeText = ''
|
||||
if (typeof codeContent === 'string') {
|
||||
actualCodeText = codeContent
|
||||
} else if (React.isValidElement(codeContent)) {
|
||||
// If it's a React element, try to get its text content
|
||||
actualCodeText = getTextContent(codeContent)
|
||||
} else if (Array.isArray(codeContent)) {
|
||||
// If it's an array of elements, join their text content
|
||||
actualCodeText = codeContent
|
||||
.map((child) =>
|
||||
typeof child === 'string'
|
||||
@@ -232,7 +223,6 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
actualCodeText = String(codeContent || '')
|
||||
}
|
||||
|
||||
// Create a unique key for this code block based on content
|
||||
const codeText = actualCodeText || 'code'
|
||||
const codeBlockKey = `${language}-${codeText.substring(0, 30).replace(/\s/g, '-')}-${codeText.length}`
|
||||
|
||||
@@ -246,7 +236,6 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
}
|
||||
}
|
||||
|
||||
// Map markdown language tag to Code.Viewer supported languages
|
||||
const normalizedLanguage = (language || '').toLowerCase()
|
||||
const viewerLanguage: 'javascript' | 'json' | 'python' =
|
||||
normalizedLanguage === 'json'
|
||||
@@ -256,7 +245,7 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
: 'javascript'
|
||||
|
||||
return (
|
||||
<div className='my-6 w-0 min-w-full overflow-hidden rounded-md border border-[var(--border-1)] bg-[var(--surface-1)] text-sm'>
|
||||
<div className='mt-6 mb-6 w-0 min-w-full overflow-hidden rounded-md border border-[var(--border-1)] bg-[var(--surface-1)] text-sm'>
|
||||
<div className='flex items-center justify-between border-[var(--border-1)] border-b px-4 py-1.5'>
|
||||
<span className='font-season text-[var(--text-muted)] text-xs'>
|
||||
{language === 'code' ? viewerLanguage : language}
|
||||
@@ -274,16 +263,15 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
</button>
|
||||
</div>
|
||||
<Code.Viewer
|
||||
code={actualCodeText}
|
||||
code={actualCodeText.replace(/\n+$/, '')}
|
||||
showGutter
|
||||
language={viewerLanguage}
|
||||
className='[&_pre]:!pb-0 m-0 rounded-none border-0 bg-transparent'
|
||||
className='m-0 min-h-0 rounded-none border-0 bg-transparent'
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
|
||||
// Inline code
|
||||
code: ({
|
||||
inline,
|
||||
className,
|
||||
@@ -307,44 +295,36 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
)
|
||||
},
|
||||
|
||||
// Bold text
|
||||
strong: ({ children }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<strong className='font-semibold text-[var(--text-primary)]'>{children}</strong>
|
||||
),
|
||||
|
||||
// Bold text (alternative)
|
||||
b: ({ children }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<b className='font-semibold text-[var(--text-primary)]'>{children}</b>
|
||||
),
|
||||
|
||||
// Italic text
|
||||
em: ({ children }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<em className='text-[var(--text-primary)] italic'>{children}</em>
|
||||
),
|
||||
|
||||
// Italic text (alternative)
|
||||
i: ({ children }: React.HTMLAttributes<HTMLElement>) => (
|
||||
<i className='text-[var(--text-primary)] italic'>{children}</i>
|
||||
),
|
||||
|
||||
// Blockquotes
|
||||
blockquote: ({ children }: React.HTMLAttributes<HTMLQuoteElement>) => (
|
||||
<blockquote className='my-4 border-[var(--border-1)] border-l-4 py-1 pl-4 font-season text-[var(--text-secondary)] italic'>
|
||||
{children}
|
||||
</blockquote>
|
||||
),
|
||||
|
||||
// Horizontal rule
|
||||
hr: () => <hr className='my-8 border-[var(--divider)] border-t' />,
|
||||
|
||||
// Links
|
||||
a: ({ href, children, ...props }: React.AnchorHTMLAttributes<HTMLAnchorElement>) => (
|
||||
<LinkWithPreview href={href || '#'} {...props}>
|
||||
{children}
|
||||
</LinkWithPreview>
|
||||
),
|
||||
|
||||
// Tables
|
||||
table: ({ children }: React.TableHTMLAttributes<HTMLTableElement>) => (
|
||||
<div className='my-4 max-w-full overflow-x-auto'>
|
||||
<table className='min-w-full table-auto border border-[var(--border-1)] font-season text-sm'>
|
||||
@@ -376,7 +356,6 @@ export default function CopilotMarkdownRenderer({ content }: CopilotMarkdownRend
|
||||
</td>
|
||||
),
|
||||
|
||||
// Images
|
||||
img: ({ src, alt, ...props }: React.ImgHTMLAttributes<HTMLImageElement>) => (
|
||||
<img
|
||||
src={src}
|
||||
|
||||
223
apps/sim/blocks/blocks/greptile.ts
Normal file
223
apps/sim/blocks/blocks/greptile.ts
Normal file
@@ -0,0 +1,223 @@
|
||||
import { GreptileIcon } from '@/components/icons'
|
||||
import type { BlockConfig } from '@/blocks/types'
|
||||
import { AuthMode } from '@/blocks/types'
|
||||
import type { GreptileResponse } from '@/tools/greptile/types'
|
||||
|
||||
export const GreptileBlock: BlockConfig<GreptileResponse> = {
|
||||
type: 'greptile',
|
||||
name: 'Greptile',
|
||||
description: 'AI-powered codebase search and Q&A',
|
||||
authMode: AuthMode.ApiKey,
|
||||
longDescription:
|
||||
'Query and search codebases using natural language with Greptile. Get AI-generated answers about your code, find relevant files, and understand complex codebases.',
|
||||
docsLink: 'https://docs.sim.ai/tools/greptile',
|
||||
category: 'tools',
|
||||
bgColor: '#e5e5e5',
|
||||
icon: GreptileIcon,
|
||||
subBlocks: [
|
||||
{
|
||||
id: 'operation',
|
||||
title: 'Operation',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'Query', id: 'greptile_query' },
|
||||
// { label: 'Search', id: 'greptile_search' }, // Disabled: Greptile search endpoint returning v1 deprecation error
|
||||
{ label: 'Index Repository', id: 'greptile_index_repo' },
|
||||
{ label: 'Check Status', id: 'greptile_status' },
|
||||
],
|
||||
value: () => 'greptile_query',
|
||||
},
|
||||
// Query operation inputs
|
||||
{
|
||||
id: 'query',
|
||||
title: 'Query',
|
||||
type: 'long-input',
|
||||
placeholder: 'Ask a question about the codebase...',
|
||||
condition: { field: 'operation', value: 'greptile_query' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'repositories',
|
||||
title: 'Repositories',
|
||||
type: 'long-input',
|
||||
placeholder: 'owner/repo, github:main:owner/repo (comma-separated)',
|
||||
condition: { field: 'operation', value: 'greptile_query' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'sessionId',
|
||||
title: 'Session ID',
|
||||
type: 'short-input',
|
||||
placeholder: 'Optional session ID for conversation continuity',
|
||||
condition: { field: 'operation', value: 'greptile_query' },
|
||||
},
|
||||
{
|
||||
id: 'genius',
|
||||
title: 'Genius Mode',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'greptile_query' },
|
||||
},
|
||||
// Search operation inputs - Disabled: Greptile search endpoint returning v1 deprecation error
|
||||
// {
|
||||
// id: 'query',
|
||||
// title: 'Search Query',
|
||||
// type: 'long-input',
|
||||
// placeholder: 'Search for code patterns, functions, or concepts...',
|
||||
// condition: { field: 'operation', value: 'greptile_search' },
|
||||
// required: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'repositories',
|
||||
// title: 'Repositories',
|
||||
// type: 'long-input',
|
||||
// placeholder: 'owner/repo, github:main:owner/repo (comma-separated)',
|
||||
// condition: { field: 'operation', value: 'greptile_search' },
|
||||
// required: true,
|
||||
// },
|
||||
// {
|
||||
// id: 'sessionId',
|
||||
// title: 'Session ID',
|
||||
// type: 'short-input',
|
||||
// placeholder: 'Optional session ID for conversation continuity',
|
||||
// condition: { field: 'operation', value: 'greptile_search' },
|
||||
// },
|
||||
// {
|
||||
// id: 'genius',
|
||||
// title: 'Genius Mode',
|
||||
// type: 'switch',
|
||||
// condition: { field: 'operation', value: 'greptile_search' },
|
||||
// },
|
||||
// Index operation inputs
|
||||
{
|
||||
id: 'remote',
|
||||
title: 'Git Remote',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'GitHub', id: 'github' },
|
||||
{ label: 'GitLab', id: 'gitlab' },
|
||||
],
|
||||
value: () => 'github',
|
||||
condition: { field: 'operation', value: 'greptile_index_repo' },
|
||||
},
|
||||
{
|
||||
id: 'repository',
|
||||
title: 'Repository',
|
||||
type: 'short-input',
|
||||
placeholder: 'owner/repo',
|
||||
condition: { field: 'operation', value: 'greptile_index_repo' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'branch',
|
||||
title: 'Branch',
|
||||
type: 'short-input',
|
||||
placeholder: 'main',
|
||||
condition: { field: 'operation', value: 'greptile_index_repo' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'reload',
|
||||
title: 'Force Re-index',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'greptile_index_repo' },
|
||||
},
|
||||
{
|
||||
id: 'notify',
|
||||
title: 'Email Notification',
|
||||
type: 'switch',
|
||||
condition: { field: 'operation', value: 'greptile_index_repo' },
|
||||
},
|
||||
// Status operation inputs
|
||||
{
|
||||
id: 'remote',
|
||||
title: 'Git Remote',
|
||||
type: 'dropdown',
|
||||
options: [
|
||||
{ label: 'GitHub', id: 'github' },
|
||||
{ label: 'GitLab', id: 'gitlab' },
|
||||
],
|
||||
value: () => 'github',
|
||||
condition: { field: 'operation', value: 'greptile_status' },
|
||||
},
|
||||
{
|
||||
id: 'repository',
|
||||
title: 'Repository',
|
||||
type: 'short-input',
|
||||
placeholder: 'owner/repo',
|
||||
condition: { field: 'operation', value: 'greptile_status' },
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'branch',
|
||||
title: 'Branch',
|
||||
type: 'short-input',
|
||||
placeholder: 'main',
|
||||
condition: { field: 'operation', value: 'greptile_status' },
|
||||
required: true,
|
||||
},
|
||||
// API Keys (common)
|
||||
{
|
||||
id: 'apiKey',
|
||||
title: 'Greptile API Key',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your Greptile API key',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
id: 'githubToken',
|
||||
title: 'GitHub Token',
|
||||
type: 'short-input',
|
||||
placeholder: 'Enter your GitHub Personal Access Token',
|
||||
password: true,
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
access: ['greptile_query', /* 'greptile_search', */ 'greptile_index_repo', 'greptile_status'],
|
||||
config: {
|
||||
tool: (params) => params.operation,
|
||||
},
|
||||
},
|
||||
inputs: {
|
||||
operation: { type: 'string', description: 'Operation to perform' },
|
||||
apiKey: { type: 'string', description: 'Greptile API key' },
|
||||
githubToken: { type: 'string', description: 'GitHub Personal Access Token' },
|
||||
// Query/Search inputs
|
||||
query: { type: 'string', description: 'Natural language query or search term' },
|
||||
repositories: { type: 'string', description: 'Comma-separated list of repositories' },
|
||||
sessionId: { type: 'string', description: 'Session ID for conversation continuity' },
|
||||
genius: { type: 'boolean', description: 'Enable genius mode for more thorough analysis' },
|
||||
// Index/Status inputs
|
||||
remote: { type: 'string', description: 'Git remote type (github/gitlab)' },
|
||||
repository: { type: 'string', description: 'Repository in owner/repo format' },
|
||||
branch: { type: 'string', description: 'Branch name' },
|
||||
reload: { type: 'boolean', description: 'Force re-indexing' },
|
||||
notify: { type: 'boolean', description: 'Send email notification' },
|
||||
},
|
||||
outputs: {
|
||||
// Query output
|
||||
message: { type: 'string', description: 'AI-generated answer to the query' },
|
||||
// Query/Search output
|
||||
sources: {
|
||||
type: 'json',
|
||||
description: 'Relevant code references with filepath, line numbers, and summary',
|
||||
},
|
||||
// Index output
|
||||
repositoryId: {
|
||||
type: 'string',
|
||||
description: 'Repository identifier (format: remote:branch:owner/repo)',
|
||||
},
|
||||
statusEndpoint: { type: 'string', description: 'URL endpoint to check indexing status' },
|
||||
// Status output
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Indexing status: submitted, cloning, processing, completed, or failed',
|
||||
},
|
||||
private: { type: 'boolean', description: 'Whether the repository is private' },
|
||||
filesProcessed: { type: 'number', description: 'Number of files processed' },
|
||||
numFiles: { type: 'number', description: 'Total number of files' },
|
||||
sampleQuestions: { type: 'json', description: 'Sample questions for the indexed repository' },
|
||||
sha: { type: 'string', description: 'Git commit SHA' },
|
||||
},
|
||||
}
|
||||
@@ -43,6 +43,7 @@ import { GoogleSlidesBlock } from '@/blocks/blocks/google_slides'
|
||||
import { GoogleVaultBlock } from '@/blocks/blocks/google_vault'
|
||||
import { GrafanaBlock } from '@/blocks/blocks/grafana'
|
||||
import { GrainBlock } from '@/blocks/blocks/grain'
|
||||
import { GreptileBlock } from '@/blocks/blocks/greptile'
|
||||
import { GuardrailsBlock } from '@/blocks/blocks/guardrails'
|
||||
import { HubSpotBlock } from '@/blocks/blocks/hubspot'
|
||||
import { HuggingFaceBlock } from '@/blocks/blocks/huggingface'
|
||||
@@ -178,6 +179,7 @@ export const registry: Record<string, BlockConfig> = {
|
||||
gmail: GmailBlock,
|
||||
grain: GrainBlock,
|
||||
grafana: GrafanaBlock,
|
||||
greptile: GreptileBlock,
|
||||
guardrails: GuardrailsBlock,
|
||||
google_calendar: GoogleCalendarBlock,
|
||||
google_docs: GoogleDocsBlock,
|
||||
|
||||
@@ -4344,3 +4344,16 @@ export function CirclebackIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
export function GreptileIcon(props: SVGProps<SVGSVGElement>) {
|
||||
return (
|
||||
<svg {...props} viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path
|
||||
clipRule='evenodd'
|
||||
fillRule='evenodd'
|
||||
fill='#44A775'
|
||||
d='M3.353.004a6.074 6.074 0 01-.265.045C2.63.12 2.092.348 1.71.633 1.426.846.717 1.575.557 1.819a3.359 3.359 0 00-.23 3.296c.154.322.35.59.71.972.187.198.434.486.55.64a6.629 6.629 0 011.305 3.546c.01.138.035 1.607.057 3.264.043 3.273.038 3.18.203 3.485.266.494.94.79 1.474.648.29-.077.463-.204 1.353-.986.957-.84 1.092-.932 1.446-.98.124-.017.631 0 1.66.053 1.513.08 1.622.079 1.85-.016.393-.164.539-.4.661-1.074.247-1.36 1.296-2.56 2.64-3.022.116-.04.373-.104.572-.144.198-.04.426-.102.506-.138.296-.136.515-.424.566-.744.017-.11-.007-.549-.089-1.602-.091-1.179-.107-1.483-.083-1.621.057-.342.139-.46 1.01-1.448.447-.506.85-.976.895-1.043.262-.39.288-.91.068-1.345a1.44 1.44 0 00-.822-.67c-.1-.029-.834-.037-3.544-.038H9.897l-.335-.063c-.958-.179-1.765-.49-2.484-.958-.362-.236-.583-.41-1.018-.804-.408-.37-.59-.502-.921-.67A3.018 3.018 0 003.744.005a3.942 3.942 0 00-.391 0zm15.728 5.858c-.132.049-.217.127-.48.44-.592.707-.74 1.336-.531 2.256.106.466.163.572.361.673.105.054.169.055 2.637.046l2.53-.009.118-.063a.551.551 0 00.095-.895 184.88 184.88 0 00-2.223-1.254c-2.293-1.282-2.281-1.276-2.507-1.194zm-3.216 6.71a9.258 9.258 0 00-1.364.696c-.844.557-1.454 1.36-1.923 2.53-.211.525-.202.75.04.935.111.087 6.478 3.14 6.667 3.198.153.047.27.027.43-.074a.538.538 0 00.24-.434c0-.06-.03-.18-.065-.264-.156-.368-3.098-6.467-3.158-6.545-.168-.222-.394-.232-.867-.042zm-8.48 5.457c-.453.102-.83.32-1.285.745-.296.277-.336.468-.167.798.053.103.61 1.104 1.236 2.224 1.297 2.317 1.22 2.206 1.558 2.202.152-.002.198-.015.296-.084a.662.662 0 00.173-.193c.058-.11.06-.152.08-2.595.018-1.93.015-2.51-.011-2.606a.569.569 0 00-.138-.227c-.09-.091-.14-.112-.406-.176-.582-.138-.992-.165-1.336-.088z'
|
||||
/>
|
||||
</svg>
|
||||
)
|
||||
}
|
||||
|
||||
9
apps/sim/tools/greptile/index.ts
Normal file
9
apps/sim/tools/greptile/index.ts
Normal file
@@ -0,0 +1,9 @@
|
||||
import { indexRepoTool } from '@/tools/greptile/index_repo'
|
||||
import { queryTool } from '@/tools/greptile/query'
|
||||
import { searchTool } from '@/tools/greptile/search'
|
||||
import { statusTool } from '@/tools/greptile/status'
|
||||
|
||||
export const greptileQueryTool = queryTool
|
||||
export const greptileSearchTool = searchTool
|
||||
export const greptileIndexRepoTool = indexRepoTool
|
||||
export const greptileStatusTool = statusTool
|
||||
123
apps/sim/tools/greptile/index_repo.ts
Normal file
123
apps/sim/tools/greptile/index_repo.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
import type { GreptileIndexParams, GreptileIndexResponse } from '@/tools/greptile/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const indexRepoTool: ToolConfig<GreptileIndexParams, GreptileIndexResponse> = {
|
||||
id: 'greptile_index_repo',
|
||||
name: 'Greptile Index Repository',
|
||||
description:
|
||||
'Submit a repository to be indexed by Greptile. Indexing must complete before the repository can be queried. Small repos take 3-5 minutes, larger ones can take over an hour.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
remote: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Git remote type: github or gitlab',
|
||||
},
|
||||
repository: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Repository in owner/repo format (e.g., "facebook/react")',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Branch to index (e.g., "main" or "master")',
|
||||
},
|
||||
reload: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Force re-indexing even if already indexed',
|
||||
},
|
||||
notify: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Send email notification when indexing completes',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Greptile API key',
|
||||
},
|
||||
githubToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with repo read access',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.greptile.com/v2/repositories',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-Github-Token': params.githubToken,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
remote: params.remote,
|
||||
repository: params.repository,
|
||||
branch: params.branch,
|
||||
}
|
||||
|
||||
if (params.reload != null) {
|
||||
body.reload = params.reload
|
||||
}
|
||||
|
||||
if (params.notify != null) {
|
||||
body.notify = params.notify
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response, params) => {
|
||||
const data = await response.json()
|
||||
|
||||
let repositoryId = ''
|
||||
if (data.statusEndpoint) {
|
||||
const match = data.statusEndpoint.match(/\/repositories\/(.+)$/)
|
||||
if (match) {
|
||||
repositoryId = decodeURIComponent(match[1])
|
||||
}
|
||||
}
|
||||
|
||||
if (!repositoryId && params) {
|
||||
repositoryId = `${params.remote}:${params.branch}:${params.repository}`
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
repositoryId,
|
||||
statusEndpoint: data.statusEndpoint || '',
|
||||
message: data.message || 'Repository submitted for indexing',
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
repositoryId: {
|
||||
type: 'string',
|
||||
description:
|
||||
'Unique identifier for the indexed repository (format: remote:branch:owner/repo)',
|
||||
},
|
||||
statusEndpoint: {
|
||||
type: 'string',
|
||||
description: 'URL endpoint to check indexing status',
|
||||
},
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'Status message about the indexing operation',
|
||||
},
|
||||
},
|
||||
}
|
||||
128
apps/sim/tools/greptile/query.ts
Normal file
128
apps/sim/tools/greptile/query.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import type { GreptileQueryParams, GreptileQueryResponse } from '@/tools/greptile/types'
|
||||
import { parseRepositories } from '@/tools/greptile/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const queryTool: ToolConfig<GreptileQueryParams, GreptileQueryResponse> = {
|
||||
id: 'greptile_query',
|
||||
name: 'Greptile Query',
|
||||
description:
|
||||
'Query repositories in natural language and get answers with relevant code references. Greptile uses AI to understand your codebase and answer questions.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Natural language question about the codebase',
|
||||
},
|
||||
repositories: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Comma-separated list of repositories. Format: "github:branch:owner/repo" or just "owner/repo" (defaults to github:main)',
|
||||
},
|
||||
sessionId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Session ID for conversation continuity',
|
||||
},
|
||||
genius: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Enable genius mode for more thorough analysis (slower but more accurate)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Greptile API key',
|
||||
},
|
||||
githubToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with repo read access',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.greptile.com/v2/query',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-Github-Token': params.githubToken,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: params.query,
|
||||
},
|
||||
],
|
||||
repositories: parseRepositories(params.repositories),
|
||||
stream: false,
|
||||
}
|
||||
|
||||
if (params.sessionId) {
|
||||
body.sessionId = params.sessionId
|
||||
}
|
||||
|
||||
if (params.genius != null) {
|
||||
body.genius = params.genius
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
message: data.message || '',
|
||||
sources: (data.sources || []).map((source: Record<string, unknown>) => ({
|
||||
repository: source.repository || '',
|
||||
remote: source.remote || '',
|
||||
branch: source.branch || '',
|
||||
filepath: source.filepath || '',
|
||||
linestart: source.linestart,
|
||||
lineend: source.lineend,
|
||||
summary: source.summary,
|
||||
distance: source.distance,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: 'AI-generated answer to the query',
|
||||
},
|
||||
sources: {
|
||||
type: 'array',
|
||||
description: 'Relevant code references that support the answer',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repository: { type: 'string', description: 'Repository name (owner/repo)' },
|
||||
remote: { type: 'string', description: 'Git remote (github/gitlab)' },
|
||||
branch: { type: 'string', description: 'Branch name' },
|
||||
filepath: { type: 'string', description: 'Path to the file' },
|
||||
linestart: { type: 'number', description: 'Starting line number' },
|
||||
lineend: { type: 'number', description: 'Ending line number' },
|
||||
summary: { type: 'string', description: 'Summary of the code section' },
|
||||
distance: { type: 'number', description: 'Similarity score (lower = more relevant)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
117
apps/sim/tools/greptile/search.ts
Normal file
117
apps/sim/tools/greptile/search.ts
Normal file
@@ -0,0 +1,117 @@
|
||||
import type { GreptileSearchParams, GreptileSearchResponse } from '@/tools/greptile/types'
|
||||
import { parseRepositories } from '@/tools/greptile/utils'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const searchTool: ToolConfig<GreptileSearchParams, GreptileSearchResponse> = {
|
||||
id: 'greptile_search',
|
||||
name: 'Greptile Search',
|
||||
description:
|
||||
'Search repositories in natural language and get relevant code references without generating an answer. Useful for finding specific code locations.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
query: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-or-llm',
|
||||
description: 'Natural language search query to find relevant code',
|
||||
},
|
||||
repositories: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description:
|
||||
'Comma-separated list of repositories. Format: "github:branch:owner/repo" or just "owner/repo" (defaults to github:main)',
|
||||
},
|
||||
sessionId: {
|
||||
type: 'string',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Session ID for conversation continuity',
|
||||
},
|
||||
genius: {
|
||||
type: 'boolean',
|
||||
required: false,
|
||||
visibility: 'user-only',
|
||||
description: 'Enable genius mode for more thorough search (slower but more accurate)',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Greptile API key',
|
||||
},
|
||||
githubToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with repo read access',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: 'https://api.greptile.com/v2/search',
|
||||
method: 'POST',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-Github-Token': params.githubToken,
|
||||
}),
|
||||
body: (params) => {
|
||||
const body: Record<string, unknown> = {
|
||||
query: params.query,
|
||||
repositories: parseRepositories(params.repositories),
|
||||
}
|
||||
|
||||
if (params.sessionId) {
|
||||
body.sessionId = params.sessionId
|
||||
}
|
||||
|
||||
if (params.genius != null) {
|
||||
body.genius = params.genius
|
||||
}
|
||||
|
||||
return body
|
||||
},
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
sources: (data.sources || data || []).map((source: Record<string, unknown>) => ({
|
||||
repository: source.repository || '',
|
||||
remote: source.remote || '',
|
||||
branch: source.branch || '',
|
||||
filepath: source.filepath || '',
|
||||
linestart: source.linestart,
|
||||
lineend: source.lineend,
|
||||
summary: source.summary,
|
||||
distance: source.distance,
|
||||
})),
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
sources: {
|
||||
type: 'array',
|
||||
description: 'Relevant code references matching the search query',
|
||||
items: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
repository: { type: 'string', description: 'Repository name (owner/repo)' },
|
||||
remote: { type: 'string', description: 'Git remote (github/gitlab)' },
|
||||
branch: { type: 'string', description: 'Branch name' },
|
||||
filepath: { type: 'string', description: 'Path to the file' },
|
||||
linestart: { type: 'number', description: 'Starting line number' },
|
||||
lineend: { type: 'number', description: 'Ending line number' },
|
||||
summary: { type: 'string', description: 'Summary of the code section' },
|
||||
distance: { type: 'number', description: 'Similarity score (lower = more relevant)' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
115
apps/sim/tools/greptile/status.ts
Normal file
115
apps/sim/tools/greptile/status.ts
Normal file
@@ -0,0 +1,115 @@
|
||||
import type { GreptileStatusParams, GreptileStatusResponse } from '@/tools/greptile/types'
|
||||
import type { ToolConfig } from '@/tools/types'
|
||||
|
||||
export const statusTool: ToolConfig<GreptileStatusParams, GreptileStatusResponse> = {
|
||||
id: 'greptile_status',
|
||||
name: 'Greptile Repository Status',
|
||||
description:
|
||||
'Check the indexing status of a repository. Use this to verify if a repository is ready to be queried or to monitor indexing progress.',
|
||||
version: '1.0.0',
|
||||
|
||||
params: {
|
||||
remote: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Git remote type: github or gitlab',
|
||||
},
|
||||
repository: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Repository in owner/repo format (e.g., "facebook/react")',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Branch name (e.g., "main" or "master")',
|
||||
},
|
||||
apiKey: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'Greptile API key',
|
||||
},
|
||||
githubToken: {
|
||||
type: 'string',
|
||||
required: true,
|
||||
visibility: 'user-only',
|
||||
description: 'GitHub Personal Access Token with repo read access',
|
||||
},
|
||||
},
|
||||
|
||||
request: {
|
||||
url: (params) => {
|
||||
// Repository ID format: remote:branch:owner/repo (URL encoded)
|
||||
const repositoryId = `${params.remote}:${params.branch}:${params.repository}`
|
||||
return `https://api.greptile.com/v2/repositories/${encodeURIComponent(repositoryId)}`
|
||||
},
|
||||
method: 'GET',
|
||||
headers: (params) => ({
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${params.apiKey}`,
|
||||
'X-Github-Token': params.githubToken,
|
||||
}),
|
||||
},
|
||||
|
||||
transformResponse: async (response: Response) => {
|
||||
const data = await response.json()
|
||||
|
||||
return {
|
||||
success: true,
|
||||
output: {
|
||||
repository: data.repository || '',
|
||||
remote: data.remote || '',
|
||||
branch: data.branch || '',
|
||||
private: data.private || false,
|
||||
status: data.status || 'unknown',
|
||||
filesProcessed: data.filesProcessed,
|
||||
numFiles: data.numFiles,
|
||||
sampleQuestions: data.sampleQuestions || [],
|
||||
sha: data.sha,
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
outputs: {
|
||||
repository: {
|
||||
type: 'string',
|
||||
description: 'Repository name (owner/repo)',
|
||||
},
|
||||
remote: {
|
||||
type: 'string',
|
||||
description: 'Git remote (github/gitlab)',
|
||||
},
|
||||
branch: {
|
||||
type: 'string',
|
||||
description: 'Branch name',
|
||||
},
|
||||
private: {
|
||||
type: 'boolean',
|
||||
description: 'Whether the repository is private',
|
||||
},
|
||||
status: {
|
||||
type: 'string',
|
||||
description: 'Indexing status: submitted, cloning, processing, completed, or failed',
|
||||
},
|
||||
filesProcessed: {
|
||||
type: 'number',
|
||||
description: 'Number of files processed so far',
|
||||
},
|
||||
numFiles: {
|
||||
type: 'number',
|
||||
description: 'Total number of files in the repository',
|
||||
},
|
||||
sampleQuestions: {
|
||||
type: 'array',
|
||||
description: 'Sample questions for the indexed repository',
|
||||
},
|
||||
sha: {
|
||||
type: 'string',
|
||||
description: 'Git commit SHA of the indexed version',
|
||||
},
|
||||
},
|
||||
}
|
||||
129
apps/sim/tools/greptile/types.ts
Normal file
129
apps/sim/tools/greptile/types.ts
Normal file
@@ -0,0 +1,129 @@
|
||||
import type { ToolResponse } from '@/tools/types'
|
||||
|
||||
/**
|
||||
* Common parameters for all Greptile tools
|
||||
*/
|
||||
export interface GreptileBaseParams {
|
||||
apiKey: string
|
||||
githubToken: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository identifier format
|
||||
*/
|
||||
export interface GreptileRepository {
|
||||
remote: 'github' | 'gitlab'
|
||||
branch: string
|
||||
repository: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Query tool parameters
|
||||
*/
|
||||
export interface GreptileQueryParams extends GreptileBaseParams {
|
||||
query: string
|
||||
repositories: string
|
||||
sessionId?: string
|
||||
stream?: boolean
|
||||
genius?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Source reference in query/search results
|
||||
*/
|
||||
export interface GreptileSource {
|
||||
repository: string
|
||||
remote: string
|
||||
branch: string
|
||||
filepath: string
|
||||
linestart?: number
|
||||
lineend?: number
|
||||
summary?: string
|
||||
distance?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Query response
|
||||
*/
|
||||
export interface GreptileQueryResponse extends ToolResponse {
|
||||
output: {
|
||||
message: string
|
||||
sources: GreptileSource[]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search tool parameters
|
||||
*/
|
||||
export interface GreptileSearchParams extends GreptileBaseParams {
|
||||
query: string
|
||||
repositories: string
|
||||
sessionId?: string
|
||||
genius?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Search response
|
||||
*/
|
||||
export interface GreptileSearchResponse extends ToolResponse {
|
||||
output: {
|
||||
sources: GreptileSource[]
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Index repository tool parameters
|
||||
*/
|
||||
export interface GreptileIndexParams extends GreptileBaseParams {
|
||||
remote: 'github' | 'gitlab'
|
||||
repository: string
|
||||
branch: string
|
||||
reload?: boolean
|
||||
notify?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Index repository response
|
||||
*/
|
||||
export interface GreptileIndexResponse extends ToolResponse {
|
||||
output: {
|
||||
repositoryId: string
|
||||
statusEndpoint: string
|
||||
message: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repository status tool parameters
|
||||
*/
|
||||
export interface GreptileStatusParams extends GreptileBaseParams {
|
||||
remote: 'github' | 'gitlab'
|
||||
repository: string
|
||||
branch: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Repository status response
|
||||
*/
|
||||
export interface GreptileStatusResponse extends ToolResponse {
|
||||
output: {
|
||||
repository: string
|
||||
remote: string
|
||||
branch: string
|
||||
private: boolean
|
||||
status: 'submitted' | 'cloning' | 'processing' | 'completed' | 'failed'
|
||||
filesProcessed?: number
|
||||
numFiles?: number
|
||||
sampleQuestions?: string[]
|
||||
sha?: string
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Union type for all Greptile responses
|
||||
*/
|
||||
export type GreptileResponse =
|
||||
| GreptileQueryResponse
|
||||
| GreptileSearchResponse
|
||||
| GreptileIndexResponse
|
||||
| GreptileStatusResponse
|
||||
29
apps/sim/tools/greptile/utils.ts
Normal file
29
apps/sim/tools/greptile/utils.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Parse repository string into structured format for Greptile API
|
||||
* Accepts formats like "github:main:owner/repo" or "owner/repo" (defaults to github:main)
|
||||
*/
|
||||
export function parseRepositories(repoString: string): Array<{
|
||||
remote: string
|
||||
branch: string
|
||||
repository: string
|
||||
}> {
|
||||
return repoString
|
||||
.split(',')
|
||||
.map((r) => r.trim())
|
||||
.filter((r) => r.length > 0)
|
||||
.map((repo) => {
|
||||
const parts = repo.split(':')
|
||||
if (parts.length === 3) {
|
||||
return {
|
||||
remote: parts[0],
|
||||
branch: parts[1],
|
||||
repository: parts[2],
|
||||
}
|
||||
}
|
||||
return {
|
||||
remote: 'github',
|
||||
branch: 'main',
|
||||
repository: repo,
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -369,6 +369,12 @@ import {
|
||||
grainListRecordingsTool,
|
||||
grainListTeamsTool,
|
||||
} from '@/tools/grain'
|
||||
import {
|
||||
greptileIndexRepoTool,
|
||||
greptileQueryTool,
|
||||
greptileSearchTool,
|
||||
greptileStatusTool,
|
||||
} from '@/tools/greptile'
|
||||
import { guardrailsValidateTool } from '@/tools/guardrails'
|
||||
import { httpRequestTool } from '@/tools/http'
|
||||
import {
|
||||
@@ -1761,6 +1767,10 @@ export const tools: Record<string, ToolConfig> = {
|
||||
grain_create_hook: grainCreateHookTool,
|
||||
grain_list_hooks: grainListHooksTool,
|
||||
grain_delete_hook: grainDeleteHookTool,
|
||||
greptile_query: greptileQueryTool,
|
||||
greptile_search: greptileSearchTool,
|
||||
greptile_index_repo: greptileIndexRepoTool,
|
||||
greptile_status: greptileStatusTool,
|
||||
elasticsearch_search: elasticsearchSearchTool,
|
||||
elasticsearch_index_document: elasticsearchIndexDocumentTool,
|
||||
elasticsearch_get_document: elasticsearchGetDocumentTool,
|
||||
|
||||
Reference in New Issue
Block a user