Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 44 additions & 48 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,102 +30,98 @@
</a>
</p>

<strong>⚡ CodinIT.dev — Open‑Source AI App Builder ⚡</strong><br/>
<h1 align="center">CodinIT.dev — Open‑Source AI App Builder</h1>

<p align="center">
Build, manage, and deploy intelligent applications faster — directly from your browser or desktop.
</p>
---

✨ What is CodinIT.dev?
---

CodinIT.dev is an open‑source, AI‑powered full‑stack development platform designed to help you build modern Node.js applications with speed and precision. It combines code generation, project management, and deployment tools into a single workflow — powered by your choice of AI providers.
## Overview

Whether you’re prototyping, scaling a SaaS product, or experimenting with local LLMs, CodinIT.dev adapts to your stack and your workflow.
CodinIT.dev is an open‑source, AI full‑stack development platform designed to help developers build modern Node.js applications with speed and precision. It combines code generation, project management, and deployment tools into a single workflow, powered by your choice of AI providers.

Whether you are prototyping, scaling a SaaS product, or experimenting with local LLMs, CodinIT.dev adapts to your stack and workflow.

---

🚀 Quick Start
## Quick Start

Get up and running in minutes.

1️⃣ Clone the Repository
### 1. Clone the Repository

```
git clone https://github.com/codinit-dev/codinit-dev.git
```bash
git clone [https://github.com/codinit-dev/codinit-dev.git](https://github.com/codinit-dev/codinit-dev.git)
cd codinit-dev

```

2️⃣ Install Dependencies
### 2. Install Dependencies

```
```bash
# npm
npm install

# or pnpm
pnpm install
```


3️⃣ Configure Environment
```

Create a .env file and add your preferred AI provider keys:
### 3. Configure Environment

(You can mix and match multiple providers.)
Create a `.env` file and add your preferred AI provider keys. You can mix and match multiple providers depending on your requirements.

4️⃣ Run the Dev Server
### 4. Run the Development Server

```
```bash
pnpm run dev
```

The app will be available at: 👉 http://localhost:5173
```

The application will be available at: http://localhost:5173

---

🧩 Key Features

🧠 AI‑powered full‑stack development for Node.js apps

🌐 19+ AI provider integrations (cloud & local)

🖥️ Web + Desktop (Electron) support

🐳 Docker‑ready — deploy to Vercel, Netlify, or GitHub Pages

🔍 Built‑in search, diff viewer & file‑locking

🧰 Supabase integration, data visualization & voice prompting

🔐 Provider‑agnostic architecture — no vendor lock‑in

## Core Capabilities

- **Automated Full-Stack Engineering:** Streamline the creation and management of complex Node.js architectures using intelligent generation.
- **Universal Model Integration:** Seamlessly connect with over 19 cloud and local AI providers.
- **Hybrid Environment Support:** native compatibility for both Web browsers and Desktop (Electron) environments.
- **Production-Ready Containerization:** Fully Dockerized workflow with preset configurations for Vercel, Netlify, and GitHub Pages.
- **Integrated Development Suite:** Includes robust utilities such as semantic search, diff visualization, and concurrency file-locking.
- **Expanded Ecosystem Connectivity:** Native integration with Supabase, real-time data visualization tools, and voice-command interfaces.
- **Vendor-Neutral Infrastructure:** A flexible architecture designed to prevent vendor lock-in, allowing dynamic switching between backend providers.

---

🔑 Supported AI Providers
## Supported AI Providers

☁️ Cloud Providers
CodinIT.dev allows you to use one provider or switch dynamically per task.

OpenAI · Anthropic · Google · Groq · xAI · DeepSeek · Cohere · Mistral · Together · Perplexity · HuggingFace · OpenRouter · and more
### Cloud Providers

🏠 Local Providers
OpenAI, Anthropic, Google, Groq, xAI, DeepSeek, Cohere, Mistral, Together, Perplexity, HuggingFace, OpenRouter, and more.

Ollama · LM Studio · OpenAI‑compatible local endpoints
### Local Providers

Use one provider or switch dynamically per task.
Ollama, LM Studio, and OpenAI‑compatible local endpoints.

---

🖥️ Desktop & Docker Usage
## Deployment & Desktop Usage

Run with Docker
```
### Run with Docker

```bash
npm run dockerbuild
docker compose --profile development up

```
Run as a Desktop App

Download the latest prebuilt release: 👉 https://github.com/codinit-dev/codinit-dev/releases/latest
### Run as a Desktop App

Download the latest prebuilt release for macOS, Windows, and Linux.

Available for macOS, Windows, and Linux.
[Download Latest Release](https://github.com/codinit-dev/codinit-dev/releases/latest)
2 changes: 1 addition & 1 deletion app/components/mcp/MCPMarketplace.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export const MCP_TEMPLATES: MCPTemplate[] = [
id: '21st-dev',
name: '21st.dev',
description: 'Use 21st.dev Magic MCP to build your next.js app components.',
icon: 'https://21st.dev/favicon.ico',
icon: '/thirdparty/logos/21st.svg',
iconColor: '#ffffff',
iconBgColor: '#0255fbff',
category: 'development',
Expand Down
40 changes: 39 additions & 1 deletion app/lib/runtime/action-runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,12 @@ export type TestResultCallback = (result: {
}) => void;

export class ActionRunner {
static readonly MAX_CONCURRENT_FILE_WRITES = 5;

#webcontainer: Promise<WebContainer>;
#currentExecutionPromise: Promise<void> = Promise.resolve();
#currentFileWrites = 0;
#fileWriteQueue: Array<() => Promise<void>> = [];
#shellTerminal: () => ExampleShell;
runnerId = atom<string>(`${Date.now()}`);
actions: ActionsMap = map({});
Expand Down Expand Up @@ -176,7 +180,7 @@ export class ActionRunner {
break;
}
case 'file': {
await this.#runFileAction(action);
await this.#queueFileWrite(() => this.#runFileAction(action));
break;
}
case 'supabase': {
Expand Down Expand Up @@ -263,6 +267,40 @@ export class ActionRunner {
}
}

async #queueFileWrite(writeOperation: () => Promise<void>): Promise<void> {
if (this.#currentFileWrites < ActionRunner.MAX_CONCURRENT_FILE_WRITES) {
this.#currentFileWrites++;

try {
await writeOperation();
} finally {
this.#currentFileWrites--;
this.#processFileWriteQueue();
}
} else {
await new Promise<void>((resolve) => {
this.#fileWriteQueue.push(async () => {
await writeOperation();
resolve();
});
});
}
}

#processFileWriteQueue() {
while (this.#fileWriteQueue.length > 0 && this.#currentFileWrites < ActionRunner.MAX_CONCURRENT_FILE_WRITES) {
const next = this.#fileWriteQueue.shift();

if (next) {
this.#currentFileWrites++;
next().finally(() => {
this.#currentFileWrites--;
this.#processFileWriteQueue();
});
}
}
}

async #runShellAction(action: ActionState) {
if (action.type !== 'shell') {
unreachable('Expected shell action');
Expand Down
7 changes: 7 additions & 0 deletions app/lib/runtime/message-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ const THINKING_TAG_CLOSE = '</codinitThinking>';
const THINKING_ARTIFACT_TAG_OPEN = '<thinkingArtifact';
const THINKING_ARTIFACT_TAG_CLOSE = '</thinkingArtifact>';

const MAX_FILE_CHUNK_SIZE = 1024 * 1024;

const logger = createScopedLogger('MessageParser');

export interface ArtifactCallbackData extends CodinitArtifactData {
Expand Down Expand Up @@ -199,6 +201,11 @@ export class StreamingMessageParser {
if ('type' in currentAction && currentAction.type === 'file') {
let content = input.slice(i);

if (content.length > MAX_FILE_CHUNK_SIZE) {
content = content.slice(0, MAX_FILE_CHUNK_SIZE);
logger.warn(`File content exceeds 1MB limit, truncating for streaming`);
}

if (!currentAction.filePath.endsWith('.md')) {
content = cleanoutMarkdownSyntax(content);
content = cleanEscapedTags(content);
Expand Down
46 changes: 31 additions & 15 deletions app/lib/stores/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ export class FilesStore {

files: MapStore<FileMap> = import.meta.hot?.data.files ?? map({});

#updateBatchTimeout: number | null = null;

#pendingUpdates: FileMap = {};

get filesCount() {
return this.#size;
}
Expand Down Expand Up @@ -561,7 +565,7 @@ export class FilesStore {
// Set up file watcher
webcontainer.internal.watchPaths(
{ include: [`${WORK_DIR}/**`], exclude: ['**/node_modules', '.git'], includeContent: true },
bufferWatchEvents(300, this.#processEventBuffer.bind(this)),
bufferWatchEvents(1000, this.#processEventBuffer.bind(this)),
);

// Get the current chat ID
Expand All @@ -573,18 +577,11 @@ export class FilesStore {
// Load locked files immediately for the current chat
this.#loadLockedFiles(currentChatId);

/**
* Also set up a timer to load locked files again after a delay.
* This ensures that locks are applied even if files are loaded asynchronously.
*/
setTimeout(() => {
this.#loadLockedFiles(currentChatId);
}, 2000);
}

/**
* Removes any deleted files/folders from the store
*/
#cleanupDeletedFiles() {
if (this.#deletedPaths.size === 0) {
return;
Expand Down Expand Up @@ -640,6 +637,23 @@ export class FilesStore {
}
}

#scheduleBatchUpdate() {
if (this.#updateBatchTimeout !== null) {
return;
}

this.#updateBatchTimeout = self.setTimeout(() => {
const updates = this.#pendingUpdates;
this.#pendingUpdates = {};
this.#updateBatchTimeout = null;

if (Object.keys(updates).length > 0) {
const currentFiles = this.files.get();
this.files.set({ ...currentFiles, ...updates });
}
}, 500);
}

#processEventBuffer(events: Array<[events: PathWatcherEvent[]]>) {
const watchEvents = events.flat(2);

Expand Down Expand Up @@ -668,15 +682,15 @@ export class FilesStore {
switch (type) {
case 'add_dir': {
// we intentionally add a trailing slash so we can distinguish files from folders in the file tree
this.files.setKey(sanitizedPath, { type: 'folder' });
this.#pendingUpdates[sanitizedPath] = { type: 'folder' };
break;
}
case 'remove_dir': {
this.files.setKey(sanitizedPath, undefined);
this.#pendingUpdates[sanitizedPath] = undefined;

for (const [direntPath] of Object.entries(this.files)) {
for (const [direntPath] of Object.entries(this.files.get())) {
if (direntPath.startsWith(sanitizedPath)) {
this.files.setKey(direntPath, undefined);
this.#pendingUpdates[direntPath] = undefined;
}
}

Expand Down Expand Up @@ -715,17 +729,17 @@ export class FilesStore {
// Preserve lock state if the file already exists
const isLocked = existingFile?.type === 'file' ? existingFile.isLocked : false;

this.files.setKey(sanitizedPath, {
this.#pendingUpdates[sanitizedPath] = {
type: 'file',
content,
isBinary,
isLocked,
});
};
break;
}
case 'remove_file': {
this.#size--;
this.files.setKey(sanitizedPath, undefined);
this.#pendingUpdates[sanitizedPath] = undefined;
break;
}
case 'update_directory': {
Expand All @@ -734,6 +748,8 @@ export class FilesStore {
}
}
}

this.#scheduleBatchUpdate();
}

#decodeFileContent(buffer?: Uint8Array) {
Expand Down
Loading