From 83d201ab142fdd5adfa802e708272e7ff5a53046 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sat, 7 Dec 2024 21:24:31 +0000 Subject: [PATCH 1/3] chore: update commit hash to 0a9f04fe3d6001efb863eee7bd2210b5a889e04e --- app/commit.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/commit.json b/app/commit.json index 299ddd5b..308721bb 100644 --- a/app/commit.json +++ b/app/commit.json @@ -1 +1 @@ -{ "commit": "31e7b48e057d12008a9790810433179bf88b9a32" } +{ "commit": "0a9f04fe3d6001efb863eee7bd2210b5a889e04e" } From bb83bb414de0c84f570d7ac4a78423f24f220c8e Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sun, 8 Dec 2024 03:48:44 +0530 Subject: [PATCH 2/3] feat(git): import from url --- app/components/git/GitUrlImport.client.tsx | 106 +++++++++++++++++++++ app/routes/git.tsx | 23 +++++ 2 files changed, 129 insertions(+) create mode 100644 app/components/git/GitUrlImport.client.tsx create mode 100644 app/routes/git.tsx diff --git a/app/components/git/GitUrlImport.client.tsx b/app/components/git/GitUrlImport.client.tsx new file mode 100644 index 00000000..47ed7080 --- /dev/null +++ b/app/components/git/GitUrlImport.client.tsx @@ -0,0 +1,106 @@ +import { useSearchParams } from '@remix-run/react'; +import { generateId, type Message } from 'ai'; +import ignore from 'ignore'; +import { useEffect, useState } from 'react'; +import { ClientOnly } from 'remix-utils/client-only'; +import { BaseChat } from '~/components/chat/BaseChat'; +import { Chat } from '~/components/chat/Chat.client'; +import { useGit } from '~/lib/hooks/useGit'; +import { useChatHistory } from '~/lib/persistence'; + +const IGNORE_PATTERNS = [ + 'node_modules/**', + '.git/**', + '.github/**', + '.vscode/**', + '**/*.jpg', + '**/*.jpeg', + '**/*.png', + 'dist/**', + 'build/**', + '.next/**', + 'coverage/**', + '.cache/**', + '.vscode/**', + '.idea/**', + '**/*.log', + '**/.DS_Store', + '**/npm-debug.log*', + '**/yarn-debug.log*', + '**/yarn-error.log*', + '**/*lock.json', + '**/*lock.yaml', +]; + +export function GitUrlImport() { + const [searchParams] = useSearchParams(); + const { ready: historyReady, importChat } = useChatHistory(); + const { ready: gitReady, gitClone } = useGit(); + const [imported, setImported] = useState(false); + + const importRepo = async (repoUrl?: string) => { + if (!gitReady && !historyReady) { + return; + } + + if (repoUrl) { + const ig = ignore().add(IGNORE_PATTERNS); + const { workdir, data } = await gitClone(repoUrl); + + if (importChat) { + const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath)); + console.log(filePaths); + + const textDecoder = new TextDecoder('utf-8'); + const message: Message = { + role: 'assistant', + content: `Cloning the repo ${repoUrl} into ${workdir} + + ${filePaths + .map((filePath) => { + const { data: content, encoding } = data[filePath]; + + if (encoding === 'utf8') { + return ` +${content} +`; + } else if (content instanceof Uint8Array) { + return ` +${textDecoder.decode(content)} +`; + } else { + return ''; + } + }) + .join('\n')} + `, + id: generateId(), + createdAt: new Date(), + }; + console.log(JSON.stringify(message)); + + importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, [message]); + + // console.log(files); + } + } + }; + + useEffect(() => { + if (!historyReady || !gitReady || imported) { + return; + } + + const url = searchParams.get('url'); + + if (!url) { + window.location.href = '/'; + return; + } + + importRepo(url); + setImported(true); + }, [searchParams, historyReady, gitReady, imported]); + + return }>{() => }; +} diff --git a/app/routes/git.tsx b/app/routes/git.tsx new file mode 100644 index 00000000..aa1689a4 --- /dev/null +++ b/app/routes/git.tsx @@ -0,0 +1,23 @@ +import type { LoaderFunctionArgs } from '@remix-run/cloudflare'; +import { json, type MetaFunction } from '@remix-run/cloudflare'; +import { ClientOnly } from 'remix-utils/client-only'; +import { BaseChat } from '~/components/chat/BaseChat'; +import { GitUrlImport } from '~/components/git/GitUrlImport.client'; +import { Header } from '~/components/header/Header'; + +export const meta: MetaFunction = () => { + return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }]; +}; + +export async function loader(args: LoaderFunctionArgs) { + return json({ url: args.params.url }); +} + +export default function Index() { + return ( +
+
+ }>{() => } +
+ ); +} From 15ab5ba4791f0e511ff69c5f8a5c67f949eea059 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sun, 8 Dec 2024 18:48:11 +0530 Subject: [PATCH 3/3] added setup command --- app/components/git/GitUrlImport.client.tsx | 59 +++++++++++++--------- 1 file changed, 35 insertions(+), 24 deletions(-) diff --git a/app/components/git/GitUrlImport.client.tsx b/app/components/git/GitUrlImport.client.tsx index 47ed7080..cbdeaa5c 100644 --- a/app/components/git/GitUrlImport.client.tsx +++ b/app/components/git/GitUrlImport.client.tsx @@ -7,6 +7,7 @@ import { BaseChat } from '~/components/chat/BaseChat'; import { Chat } from '~/components/chat/Chat.client'; import { useGit } from '~/lib/hooks/useGit'; import { useChatHistory } from '~/lib/persistence'; +import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands'; const IGNORE_PATTERNS = [ 'node_modules/**', @@ -49,39 +50,49 @@ export function GitUrlImport() { if (importChat) { const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath)); - console.log(filePaths); const textDecoder = new TextDecoder('utf-8'); - const message: Message = { + + // Convert files to common format for command detection + const fileContents = filePaths + .map((filePath) => { + const { data: content, encoding } = data[filePath]; + return { + path: filePath, + content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '', + }; + }) + .filter((f) => f.content); + + // Detect and create commands message + const commands = await detectProjectCommands(fileContents); + const commandsMessage = createCommandsMessage(commands); + + // Create files message + const filesMessage: Message = { role: 'assistant', content: `Cloning the repo ${repoUrl} into ${workdir} - - ${filePaths - .map((filePath) => { - const { data: content, encoding } = data[filePath]; - - if (encoding === 'utf8') { - return ` -${content} -`; - } else if (content instanceof Uint8Array) { - return ` -${textDecoder.decode(content)} -`; - } else { - return ''; - } - }) - .join('\n')} - `, + +${fileContents + .map( + (file) => + ` +${file.content} +`, + ) + .join('\n')} +`, id: generateId(), createdAt: new Date(), }; - console.log(JSON.stringify(message)); - importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, [message]); + const messages = [filesMessage]; - // console.log(files); + if (commandsMessage) { + messages.push(commandsMessage); + } + + await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages); } } };