Skip to content

Commit 33d87a1

Browse files
authored
Merge pull request #585 from thecodacus/git-import-from-url
Git import from url
2 parents 6921c15 + 15ab5ba commit 33d87a1

File tree

2 files changed

+140
-0
lines changed

2 files changed

+140
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
import { useSearchParams } from '@remix-run/react';
2+
import { generateId, type Message } from 'ai';
3+
import ignore from 'ignore';
4+
import { useEffect, useState } from 'react';
5+
import { ClientOnly } from 'remix-utils/client-only';
6+
import { BaseChat } from '~/components/chat/BaseChat';
7+
import { Chat } from '~/components/chat/Chat.client';
8+
import { useGit } from '~/lib/hooks/useGit';
9+
import { useChatHistory } from '~/lib/persistence';
10+
import { createCommandsMessage, detectProjectCommands } from '~/utils/projectCommands';
11+
12+
const IGNORE_PATTERNS = [
13+
'node_modules/**',
14+
'.git/**',
15+
'.github/**',
16+
'.vscode/**',
17+
'**/*.jpg',
18+
'**/*.jpeg',
19+
'**/*.png',
20+
'dist/**',
21+
'build/**',
22+
'.next/**',
23+
'coverage/**',
24+
'.cache/**',
25+
'.vscode/**',
26+
'.idea/**',
27+
'**/*.log',
28+
'**/.DS_Store',
29+
'**/npm-debug.log*',
30+
'**/yarn-debug.log*',
31+
'**/yarn-error.log*',
32+
'**/*lock.json',
33+
'**/*lock.yaml',
34+
];
35+
36+
export function GitUrlImport() {
37+
const [searchParams] = useSearchParams();
38+
const { ready: historyReady, importChat } = useChatHistory();
39+
const { ready: gitReady, gitClone } = useGit();
40+
const [imported, setImported] = useState(false);
41+
42+
const importRepo = async (repoUrl?: string) => {
43+
if (!gitReady && !historyReady) {
44+
return;
45+
}
46+
47+
if (repoUrl) {
48+
const ig = ignore().add(IGNORE_PATTERNS);
49+
const { workdir, data } = await gitClone(repoUrl);
50+
51+
if (importChat) {
52+
const filePaths = Object.keys(data).filter((filePath) => !ig.ignores(filePath));
53+
54+
const textDecoder = new TextDecoder('utf-8');
55+
56+
// Convert files to common format for command detection
57+
const fileContents = filePaths
58+
.map((filePath) => {
59+
const { data: content, encoding } = data[filePath];
60+
return {
61+
path: filePath,
62+
content: encoding === 'utf8' ? content : content instanceof Uint8Array ? textDecoder.decode(content) : '',
63+
};
64+
})
65+
.filter((f) => f.content);
66+
67+
// Detect and create commands message
68+
const commands = await detectProjectCommands(fileContents);
69+
const commandsMessage = createCommandsMessage(commands);
70+
71+
// Create files message
72+
const filesMessage: Message = {
73+
role: 'assistant',
74+
content: `Cloning the repo ${repoUrl} into ${workdir}
75+
<boltArtifact id="imported-files" title="Git Cloned Files" type="bundled">
76+
${fileContents
77+
.map(
78+
(file) =>
79+
`<boltAction type="file" filePath="${file.path}">
80+
${file.content}
81+
</boltAction>`,
82+
)
83+
.join('\n')}
84+
</boltArtifact>`,
85+
id: generateId(),
86+
createdAt: new Date(),
87+
};
88+
89+
const messages = [filesMessage];
90+
91+
if (commandsMessage) {
92+
messages.push(commandsMessage);
93+
}
94+
95+
await importChat(`Git Project:${repoUrl.split('/').slice(-1)[0]}`, messages);
96+
}
97+
}
98+
};
99+
100+
useEffect(() => {
101+
if (!historyReady || !gitReady || imported) {
102+
return;
103+
}
104+
105+
const url = searchParams.get('url');
106+
107+
if (!url) {
108+
window.location.href = '/';
109+
return;
110+
}
111+
112+
importRepo(url);
113+
setImported(true);
114+
}, [searchParams, historyReady, gitReady, imported]);
115+
116+
return <ClientOnly fallback={<BaseChat />}>{() => <Chat />}</ClientOnly>;
117+
}

app/routes/git.tsx

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import type { LoaderFunctionArgs } from '@remix-run/cloudflare';
2+
import { json, type MetaFunction } from '@remix-run/cloudflare';
3+
import { ClientOnly } from 'remix-utils/client-only';
4+
import { BaseChat } from '~/components/chat/BaseChat';
5+
import { GitUrlImport } from '~/components/git/GitUrlImport.client';
6+
import { Header } from '~/components/header/Header';
7+
8+
export const meta: MetaFunction = () => {
9+
return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }];
10+
};
11+
12+
export async function loader(args: LoaderFunctionArgs) {
13+
return json({ url: args.params.url });
14+
}
15+
16+
export default function Index() {
17+
return (
18+
<div className="flex flex-col h-full w-full">
19+
<Header />
20+
<ClientOnly fallback={<BaseChat />}>{() => <GitUrlImport />}</ClientOnly>
21+
</div>
22+
);
23+
}

0 commit comments

Comments
 (0)