Over the past few months, it became pretty normal to hear about AI agentic workflows. I was getting at least two notifications a day about them from almost every social media platform. So, I decided to dive in and try things out myself.
After reading some blogs and articles, I realized that everyone was building AI-based services. So,I decided to build my own AI-powered code editor, inspired by tools like Cursor
What I Built
A Node.js-based CLI tool powered by Google's Gemini, capable of turning natural language prompts into real code, directory structures, and package setups.
Example:
Create a server for a to-do application
→ This tool will:
- Create files and folders
- Initialize
package.json
and install external packages- Write actual runnable code
Tech Stack
- Node.js
- Google Generative AI (Gemini API) – For generating code from prompts (free to use)
- readline-sync – For CLI input
- fs – For file manipulation
- path – For resolving file paths
- child_process – For running shell commands
Getting Started
First, initialize your Node.js project:
npm init -y
Then, install the required packages:
npm install readline-sync @google/generative-ai
Import all required modules:
const readlineSync = require("readline-sync");
const fs = require("fs");
const path = require("path");
const { GoogleGenerativeAI } = require("@google/generative-ai");
const { exec } = require("child_process");
Setting Up Gemini
const API_KEY = "YOUR_GEMINI_API_KEY";
const genAI = new GoogleGenerativeAI(API_KEY);
const model = genAI.getGenerativeModel({ model: "gemini-2.0-flash" });
Core Functionalities
1. Create Directory
function createDirectory(directoryName) {
if (!directoryName) return;
if (!fs.existsSync(directoryName)) {
fs.mkdirSync(directoryName, { recursive: true });
console.log(`✅ Created directory: ${directoryName}`);
} else {
console.log(`⚠️ Directory already exists: ${directoryName}`);
}
}
What it does: Checks if a directory exists, and if not, creates it recursively.
2. Create File
function createFile(fileName) {
if (!fileName) return;
const dir = path.dirname(fileName);
if (dir !== "." && !fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
if (!fs.existsSync(fileName)) {
fs.writeFileSync(fileName, "");
console.log(`✅ Created file: ${fileName}`);
} else {
console.log(`⚠️ File already exists: ${fileName}`);
}
}
What it does: Ensures the file path exists and then creates an empty file if it doesn't already exist.
3. Write AI-Generated Code
async function writeCode(fileName, description, language) {
if (!fileName || !description) return;
const prompt = `
Generate clean, production-ready code based on this description in:
"${description} in ${language}"
Respond with code only, no explanation. Include internal comments if necessary.
`;
const result = await model.generateContent(prompt);
const codeText = result.response.text().trim();
const codeMatch = codeText.match(/```
{% endraw %}
(?:\w+)?\n([\s\S]*?)\n
{% raw %}
```/);
const finalCode = codeMatch ? codeMatch[1] : codeText;
fs.writeFileSync(fileName, finalCode);
console.log(`✅ Code written to: ${fileName}`);
console.log(`Preview:`);
const lines = finalCode.split("\n").slice(0, 10);
console.log(lines.join("\n"));
if (finalCode.split("\n").length > 10) {
console.log("... (truncated)");
}
}
What it does: Sends the prompt to Gemini, receives the code, extracts it, and writes it to the specified file. Also prints a preview.
4. Run Package Manager Commands
async function cliExec(path, command) {
exec(`cd ${path} && ${command}`, (err, stdout, stderr) => {
if (err) {
console.error(err);
return;
}
console.log(stdout);
});
}
What it does: Runs shell commands (like npm install
) in the specified directory using child_process.exec
.
Prompt Engineering
async function getInstructionsFromGemini(input) {
const prompt = `
You are a smart assistant for a coding CLI tool. Your base language is JS. Interpret the following user request:
"${input}"
Return a JSON array of actions like:
[
{ "action": "createDirectory", "directoryName": "folder_name" },
{ "action": "createFile", "fileName": "path/to/file.js" },
{ "action": "writeCode", "fileName": "path/to/file.js", "description": "description", "language": "JavaScript" }
]
Only respond with the JSON array. No explanations, no code blocks.
`;
const result = await model.generateContent(prompt);
const raw = result.response.text().trim();
try {
const jsonMatch = raw.match(/```
{% endraw %}
(?:json)?\s*([\s\S]*?)\s*
{% raw %}
```|([\s\S]*)/);
const jsonString = jsonMatch ? jsonMatch[1] || jsonMatch[2] : raw;
return JSON.parse(jsonString);
} catch (err) {
console.error(`Failed to parse Gemini response: ${raw}`);
return null;
}
}
What it does: Converts the natural language user prompt into a structured array of actions by prompting Gemini with clear JSON output instructions.
Executing Actions
async function executeAction(action) {
switch (action.action) {
case "createDirectory":
createDirectory(action.directoryName);
break;
case "createFile":
createFile(action.fileName);
break;
case "writeCode":
await writeCode(action.fileName, action.description, action.language);
break;
case "cliExec":
await cliExec(action.path, action.command);
break;
default:
console.log(`Unknown action: ${action.action}`);
}
}
What it does: This is the heart of the agentic loop. Based on the action type returned by Gemini, it calls the appropriate handler (e.g., createDirectory
, writeCode
).
Main CLI Chat Loop
async function runEditor() {
while (true) {
const userInput = readlineSync.question(`AI Editor> `);
if (userInput.toLowerCase() === "exit") {
console.log(`Goodbye!`);
break;
}
try {
const actions = await getInstructionsFromGemini(userInput);
if (!actions || !Array.isArray(actions)) {
console.log(`Could not understand the instruction.`);
continue;
}
for (const action of actions) {
await executeAction(action);
}
} catch (error) {
console.error(`Error: ${error.message}`);
}
}
}
What it does: Repeatedly reads user input and processes it using Gemini. On each prompt, it gets structured instructions, then performs the actions in sequence. Type exit
to quit.
Final Styled Version
To make the CLI more user-friendly, I added some color using ANSI codes:
const colors = {
reset: "\x1b[0m",
red: "\x1b[31m",
green: "\x1b[32m",
yellow: "\x1b[33m",
cyan: "\x1b[36m",
magenta: "\x1b[35m"
};
And here’s how the app welcomes you:
console.log(`${colors.cyan}======================================`);
console.log(` Smart AI Code Editor`);
console.log(`======================================${colors.reset}`);
console.log(`${colors.yellow}Type anything like:${colors.reset}`);
console.log(`"create a folder named utils and add index.js"`);
console.log(`"generate a python script that reads a csv and plots a graph"`);
console.log(`Type 'exit' to quit.`);
console.log(`${colors.cyan}======================================${colors.reset}\n`);
Final Thoughts
This little project helped me learn:
- How to work with agentic workflows
- Using LLMs in CLI-based tools
- Handling file systems, JSON parsing, and process execution in Node.js
- Prompt engineering for best results from Gemini
If you’ve made it this far, thank you! Feel free to fork this and create your own AI tooling.
And, These are some images of the Agent in work