From 37aa6e33b1ef6082374b6324a62f9cbc1a863b7a Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:28:20 +0530 Subject: [PATCH 01/24] feat: add tracing middleware for agent execution logging --- apps/code/lib/{middleware.ts => middlewares/tracingAgents.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename apps/code/lib/{middleware.ts => middlewares/tracingAgents.ts} (99%) diff --git a/apps/code/lib/middleware.ts b/apps/code/lib/middlewares/tracingAgents.ts similarity index 99% rename from apps/code/lib/middleware.ts rename to apps/code/lib/middlewares/tracingAgents.ts index d69efe725..cad0e79df 100644 --- a/apps/code/lib/middleware.ts +++ b/apps/code/lib/middlewares/tracingAgents.ts @@ -13,9 +13,9 @@ export const tracingMiddleware = async (ctx: any, next: () => Promise) => // Get the path of the agent being called from the internal execution context. const path = ctx.executionContext?.currentPath || 'unknown_path'; - + console.log(`[Trace] ==> Entering: ${path}`); - + // Add the current path to our trace log on the state object. if (ctx.state.executionTrace) { ctx.state.executionTrace.push(path); From af8a0af775c3af9030c8a9874f0419f9f5bf6cca Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:28:37 +0530 Subject: [PATCH 02/24] feat: add TypeScript definitions for PackageInfo and related types using Zod validation --- apps/code/{ => lib}/types/PackageInfo.d.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/code/{ => lib}/types/PackageInfo.d.ts (100%) diff --git a/apps/code/types/PackageInfo.d.ts b/apps/code/lib/types/PackageInfo.d.ts similarity index 100% rename from apps/code/types/PackageInfo.d.ts rename to apps/code/lib/types/PackageInfo.d.ts From a957c81930d6802e3fec385bfc8e06ca44492e82 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:28:56 +0530 Subject: [PATCH 03/24] feat: add utility function to get project root directory --- apps/code/{ => lib}/helpers/utils.ts | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename apps/code/{ => lib}/helpers/utils.ts (100%) diff --git a/apps/code/helpers/utils.ts b/apps/code/lib/helpers/utils.ts similarity index 100% rename from apps/code/helpers/utils.ts rename to apps/code/lib/helpers/utils.ts From 3ac917c9d4594260e6ab05f2aa7ecda2e5b2bb7e Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:29:06 +0530 Subject: [PATCH 04/24] chore: update TypeScript documentation link in next-env.d.ts --- apps/code/next-env.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/code/next-env.d.ts b/apps/code/next-env.d.ts index 4f11a03dc..40c3d6809 100644 --- a/apps/code/next-env.d.ts +++ b/apps/code/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/basic-features/typescript for more information. +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. From 18e4fd5040d5856117bc1aa469c7a2bfaa954320 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:29:36 +0530 Subject: [PATCH 05/24] refactor: remove unused orchestrator and middleware files --- .../lib/agents/middlewares/getPackageDocs.ts | 47 ------------ .../lib/agents/middlewares/getPackageInfo.ts | 36 --------- apps/code/lib/agents/orchestrator.ts | 64 ---------------- apps/code/lib/agents/slsFox/utils.ts | 76 ------------------- 4 files changed, 223 deletions(-) delete mode 100644 apps/code/lib/agents/middlewares/getPackageDocs.ts delete mode 100644 apps/code/lib/agents/middlewares/getPackageInfo.ts delete mode 100644 apps/code/lib/agents/orchestrator.ts delete mode 100644 apps/code/lib/agents/slsFox/utils.ts diff --git a/apps/code/lib/agents/middlewares/getPackageDocs.ts b/apps/code/lib/agents/middlewares/getPackageDocs.ts deleted file mode 100644 index 53e73be14..000000000 --- a/apps/code/lib/agents/middlewares/getPackageDocs.ts +++ /dev/null @@ -1,47 +0,0 @@ -import { AiMiddleware, AiRouter } from '@microfox/ai-router'; -import { z } from 'zod'; -import * as fs from 'fs'; -import * as path from 'path'; -import { getProjectRoot } from '@/helpers/utils'; - -export const getPackageDocs: AiMiddleware = async (ctx, next) => { - let { packageName } = ctx.request; - const taskContext = ctx.state; - packageName = packageName.replace('@microfox/', ''); - if (taskContext[packageName] && taskContext[packageName].docs) { - ctx.response.write({ type: 'text', text: `Package docs already initialized for: ${packageName}` }); - } - try { - // 1. Initialize Paths - taskContext.projectRoot = getProjectRoot(); - taskContext[packageName] = { - ...taskContext[packageName], - packageDir: path.join(taskContext.projectRoot, 'packages', packageName), - slsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'sls'), - docsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'docs'), - }; - - // 2. Read package-info.json - const docsDir = taskContext[packageName].docsDir; - if (!fs.existsSync(docsDir)) { - throw new Error(`Docs directory not found: ${docsDir}`); - } - taskContext[packageName].docs = { - path: docsDir, - constructors: fs.readdirSync(path.join(docsDir, 'constructors')).reduce((acc, file) => { - acc[file.replace('.md', '') as string] = fs.readFileSync(path.join(docsDir, 'constructors', file), 'utf8'); - return acc; - }, {} as Record), - functions: fs.readdirSync(path.join(docsDir, 'functions')).reduce((acc, file) => { - acc[file.replace('.md', '') as string] = fs.readFileSync(path.join(docsDir, 'functions', file), 'utf8'); - return acc; - }, {} as Record), - rules: fs.readFileSync(path.join(docsDir, 'rules.md'), 'utf8'), - main: fs.readFileSync(path.join(docsDir, 'main.md'), 'utf8') - } - ctx.state = taskContext; - return next(); - } catch (error: any) { - ctx.response.write({ type: 'text', text: `Error initializing context: ${error.message}` }); - } -} \ No newline at end of file diff --git a/apps/code/lib/agents/middlewares/getPackageInfo.ts b/apps/code/lib/agents/middlewares/getPackageInfo.ts deleted file mode 100644 index 171d7653e..000000000 --- a/apps/code/lib/agents/middlewares/getPackageInfo.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { AiMiddleware } from '@microfox/ai-router'; -import * as fs from 'fs'; -import * as path from 'path'; -import { getProjectRoot } from '@/helpers/utils'; - -export const getPackageInfo: AiMiddleware = async (ctx, next) => { - let { packageName } = ctx.request - const taskContext = ctx.state; - packageName = packageName.replace('@microfox/', ''); - if (taskContext[packageName] && taskContext[packageName].packageInfo) { - ctx.response.write({ type: 'text', text: `Package info already initialized for: ${packageName}` }); - } - - try { - // 1. Initialize Paths - taskContext.projectRoot = getProjectRoot(); - taskContext[packageName] = { - ...taskContext[packageName], - packageDir: path.join(taskContext.projectRoot, 'packages', packageName), - slsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'sls'), - docsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'docs'), - }; - - // 2. Read package-info.json - const packageInfoPath = path.join(taskContext[packageName].packageDir, 'package-info.json'); - if (!fs.existsSync(packageInfoPath)) { - throw new Error(`Package info not found: ${packageInfoPath}`); - } - const packageInfoContent = fs.readFileSync(packageInfoPath, 'utf8'); - taskContext[packageName]['packageInfo'] = JSON.parse(packageInfoContent); - ctx.state = taskContext; - return next(); - } catch (error: any) { - ctx.response.write({ type: 'text', text: `Error initializing context: ${error.message}` }); - } -} \ No newline at end of file diff --git a/apps/code/lib/agents/orchestrator.ts b/apps/code/lib/agents/orchestrator.ts deleted file mode 100644 index ffff4e087..000000000 --- a/apps/code/lib/agents/orchestrator.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { AiRouter } from '@microfox/ai-router'; -import { generateObject } from 'ai'; -import { google } from '@ai-sdk/google'; -import { z } from 'zod'; - -export const orchestratorAgent = new AiRouter(); - -const orchestratorSchema = z.object({ - // The user's high-level goal. - prompt: z.string().describe('The user a high-level goal to accomplish.'), -}); - -// This is the schema for the execution plan the orchestrator will generate. -const planSchema = z.object({ - plan: z - .array( - z.object({ - agent: z.enum(['summarize', 'docs', 'code']), - details: z.string().describe('The specific goal for this step.'), - }) - ) - .describe('The sequence of agents to call to accomplish the user goal.'), -}); - -orchestratorAgent - .actAsTool('/', { - description: 'Analyzes a user prompt and creates a step-by-step plan of agent calls.', - inputSchema: orchestratorSchema as any, - }) - - .agent('/', async ({ request, response, state, next }: any) => { - const { prompt } = await request.json(); - - // 1. Create the Execution Plan - response.write({ type: 'text', text: 'Generating execution plan...\n' }); - const { object: planResult } = await generateObject({ - model: google('gemini-2.5-pro-preview-06-05'), - prompt: `Based on the following user prompt, create a step-by-step execution plan. The available agents are: 'summarize', 'docs', 'code'. - -User Prompt: ${prompt}`, - schema: planSchema, - }); - - response.write({ type: 'text', text: `Plan: ${JSON.stringify(planResult.plan, null, 2)}\n\n` }); - - // 2. Execute the Plan - // Initialize the shared context that will be passed through the chain. - state.initialPrompt = prompt; - state.executionTrace = []; - - for (const step of planResult.plan) { - response.write({ type: 'text', text: `Executing agent: ${step.agent}...\n` }); - - const result = await next.callAgent(`/agent/${step.agent}`); - - if (!result.ok) { - const errorMsg = `Error executing agent ${step.agent}: ${result.error.message}`; - response.write({ type: 'text', text: errorMsg }); - return; // Stop execution on failure. - } - } - - response.write({ type: 'text', text: `\nExecution complete. Final result:\n${state.finalOutput}` }); - }); \ No newline at end of file diff --git a/apps/code/lib/agents/slsFox/utils.ts b/apps/code/lib/agents/slsFox/utils.ts deleted file mode 100644 index c2b837b78..000000000 --- a/apps/code/lib/agents/slsFox/utils.ts +++ /dev/null @@ -1,76 +0,0 @@ - -import * as fs from 'fs'; -import * as path from 'path'; - - -export function copyDirectory(src: string, dest: string): void { - if (!fs.existsSync(dest)) { - fs.mkdirSync(dest, { recursive: true }); - } - - const entries = fs.readdirSync(src, { withFileTypes: true }); - - for (const entry of entries) { - const srcPath = path.join(src, entry.name); - - if (entry.isDirectory()) { - const destPath = path.join(dest, entry.name); - copyDirectory(srcPath, destPath); - } else { - let destFileName = entry.name; - if (entry.name.endsWith('.txt')) { - const baseName = entry.name.replace('.txt', ''); - const extensionMap: { [key: string]: string } = { - package: 'package.json', - tsconfig: 'tsconfig.json', - eslint: 'eslint.config.js', - serverless: 'serverless.yml', - openapi: 'openapi.json', - sdkInit: 'sdkInit.ts', - index: 'index.ts', - }; - destFileName = extensionMap[baseName] || `${baseName}.txt`; - } - - const destPath = path.join(dest, destFileName); - fs.copyFileSync(srcPath, destPath); - } - } - } - - export function updateTemplateFiles( - slsDir: string, - packageName: string, - description: string, - ): void { - // Update package.json - const packageJsonPath = path.join(slsDir, 'package.json'); - const parentPackageJsonPath = path.join(slsDir, '..', 'package.json'); - const parentPackageJson = JSON.parse( - fs.readFileSync(parentPackageJsonPath, 'utf8'), - ); - if (fs.existsSync(packageJsonPath)) { - const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); - packageJson.name = `public-${packageName}-api`; - packageJson.description = description; - packageJson.dependencies['@microfox/tool-core'] = `^1.0.1`; - packageJson.dependencies[parentPackageJson.name] = - `^${parentPackageJson.version}`; - fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); - console.log(`✅ Updated package.json for ${packageJson.name}`); - } - - // Update serverless.yml - const serverlessPath = path.join(slsDir, 'serverless.yml'); - if (fs.existsSync(serverlessPath)) { - let serverlessContent = fs.readFileSync(serverlessPath, 'utf8'); - serverlessContent = serverlessContent.replace( - /^service:\s+.+$/m, - `service: public-${packageName}-api`, - ); - fs.writeFileSync(serverlessPath, serverlessContent); - console.log( - `✅ Updated serverless.yml for public-${packageName}-api`, - ); - } - } \ No newline at end of file From 3fa48e3d9116055d30831b65fd6baf34ea135524 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:29:47 +0530 Subject: [PATCH 06/24] feat: add middleware for retrieving package documentation and information --- apps/code/lib/middlewares/getPackageDocs.ts | 46 +++++++++++++++++++++ apps/code/lib/middlewares/getPackageInfo.ts | 36 ++++++++++++++++ 2 files changed, 82 insertions(+) create mode 100644 apps/code/lib/middlewares/getPackageDocs.ts create mode 100644 apps/code/lib/middlewares/getPackageInfo.ts diff --git a/apps/code/lib/middlewares/getPackageDocs.ts b/apps/code/lib/middlewares/getPackageDocs.ts new file mode 100644 index 000000000..8d4872202 --- /dev/null +++ b/apps/code/lib/middlewares/getPackageDocs.ts @@ -0,0 +1,46 @@ +import { AiMiddleware } from '@microfox/ai-router'; +import * as fs from 'fs'; +import * as path from 'path'; +import { getProjectRoot } from '@/lib/helpers/utils'; + +export const getPackageDocs: AiMiddleware = async (ctx, next) => { + let { packageName } = ctx.request; + const taskContext = ctx.state; + packageName = packageName.replace('@microfox/', ''); + if (taskContext[packageName] && taskContext[packageName].docs) { + ctx.response.write({ type: 'text', text: `Package docs already initialized for: ${packageName}` }); + } + try { + // 1. Initialize Paths + taskContext.projectRoot = getProjectRoot(); + taskContext[packageName] = { + ...taskContext[packageName], + packageDir: path.join(taskContext.projectRoot, 'packages', packageName), + slsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'sls'), + docsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'docs'), + }; + + // 2. Read package-info.json + const docsDir = taskContext[packageName].docsDir; + if (!fs.existsSync(docsDir)) { + throw new Error(`Docs directory not found: ${docsDir}`); + } + taskContext[packageName].docs = { + path: docsDir, + constructors: fs.readdirSync(path.join(docsDir, 'constructors')).reduce((acc, file) => { + acc[file.replace('.md', '') as string] = fs.readFileSync(path.join(docsDir, 'constructors', file), 'utf8'); + return acc; + }, {} as Record), + functions: fs.readdirSync(path.join(docsDir, 'functions')).reduce((acc, file) => { + acc[file.replace('.md', '') as string] = fs.readFileSync(path.join(docsDir, 'functions', file), 'utf8'); + return acc; + }, {} as Record), + rules: fs.readFileSync(path.join(docsDir, 'rules.md'), 'utf8'), + main: fs.readFileSync(path.join(docsDir, 'main.md'), 'utf8') + } + ctx.state = taskContext; + return next(); + } catch (error: any) { + ctx.response.write({ type: 'text', text: `Error initializing context: ${error.message}` }); + } +} \ No newline at end of file diff --git a/apps/code/lib/middlewares/getPackageInfo.ts b/apps/code/lib/middlewares/getPackageInfo.ts new file mode 100644 index 000000000..0a32826f6 --- /dev/null +++ b/apps/code/lib/middlewares/getPackageInfo.ts @@ -0,0 +1,36 @@ +import { AiMiddleware } from '@microfox/ai-router'; +import * as fs from 'fs'; +import * as path from 'path'; +import { getProjectRoot } from '@/lib/helpers/utils'; + +export const getPackageInfo: AiMiddleware = async (ctx, next) => { + let { packageName } = ctx.request + const taskContext = ctx.state; + packageName = packageName.replace('@microfox/', ''); + if (taskContext[packageName] && taskContext[packageName].packageInfo) { + ctx.response.write({ type: 'text', text: `Package info already initialized for: ${packageName}` }); + } + + try { + // 1. Initialize Paths + taskContext.projectRoot = getProjectRoot(); + taskContext[packageName] = { + ...taskContext[packageName], + packageDir: path.join(taskContext.projectRoot, 'packages', packageName), + slsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'sls'), + docsDir: path.join(taskContext.projectRoot, 'packages', packageName, 'docs'), + }; + + // 2. Read package-info.json + const packageInfoPath = path.join(taskContext[packageName].packageDir, 'package-info.json'); + if (!fs.existsSync(packageInfoPath)) { + throw new Error(`Package info not found: ${packageInfoPath}`); + } + const packageInfoContent = fs.readFileSync(packageInfoPath, 'utf8'); + taskContext[packageName]['packageInfo'] = JSON.parse(packageInfoContent); + ctx.state = taskContext; + return next(); + } catch (error: any) { + ctx.response.write({ type: 'text', text: `Error initializing context: ${error.message}` }); + } +} \ No newline at end of file From 2ca73013d7f6dbe9f4a5706c9c5fd814bc350d47 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:29:57 +0530 Subject: [PATCH 07/24] refactor: reorganize imports and clean up code structure in SLS Fox agents --- apps/code/lib/agents/slsFox/genOpenApi.ts | 4 +- apps/code/lib/agents/slsFox/genOpenApiMd.ts | 2 +- apps/code/lib/agents/slsFox/genSdkMap.ts | 2 +- apps/code/lib/agents/slsFox/index.ts | 14 ++-- apps/code/lib/agents/slsFox/template.ts | 75 +++++++++++++++++++++ 5 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 apps/code/lib/agents/slsFox/template.ts diff --git a/apps/code/lib/agents/slsFox/genOpenApi.ts b/apps/code/lib/agents/slsFox/genOpenApi.ts index 58f9ebce3..8a87c464c 100644 --- a/apps/code/lib/agents/slsFox/genOpenApi.ts +++ b/apps/code/lib/agents/slsFox/genOpenApi.ts @@ -91,6 +91,6 @@ genOpenApiAgent } catch (error: any) { ctx.response.write({ type: 'text', text: `Error generating OpenAPI spec: ${error.message}` }); } - }); + }); - genOpenApiAgent.agent('/genPathSpec', genPathSpecAgent) +genOpenApiAgent.agent('/genPathSpec', genPathSpecAgent) diff --git a/apps/code/lib/agents/slsFox/genOpenApiMd.ts b/apps/code/lib/agents/slsFox/genOpenApiMd.ts index a2429ee7a..99375f44e 100644 --- a/apps/code/lib/agents/slsFox/genOpenApiMd.ts +++ b/apps/code/lib/agents/slsFox/genOpenApiMd.ts @@ -65,7 +65,7 @@ async function generateOpenAPIMarkdown( **API Functionality:** ${functionalityMarkdown} `; - + const { object: result } = await generateObject({ model: google('gemini-1.5-pro-latest'), system: systemPrompt, diff --git a/apps/code/lib/agents/slsFox/genSdkMap.ts b/apps/code/lib/agents/slsFox/genSdkMap.ts index e09be10c0..24bfdd34f 100644 --- a/apps/code/lib/agents/slsFox/genSdkMap.ts +++ b/apps/code/lib/agents/slsFox/genSdkMap.ts @@ -4,7 +4,7 @@ import { generateObject } from 'ai'; import path from 'path'; import * as fs from 'fs'; import { anthropic } from '@ai-sdk/anthropic'; -import { PackageInfo } from '@/types/PackageInfo'; +import { PackageInfo } from '@/lib/types/PackageInfo'; export const genSdkMapAgent = new AiRouter(); diff --git a/apps/code/lib/agents/slsFox/index.ts b/apps/code/lib/agents/slsFox/index.ts index 32ea94f47..548d155c3 100644 --- a/apps/code/lib/agents/slsFox/index.ts +++ b/apps/code/lib/agents/slsFox/index.ts @@ -2,9 +2,9 @@ import { AiRouter } from '@microfox/ai-router'; import { z } from 'zod'; import * as fs from 'fs'; import * as path from 'path'; -import { getPackageInfo } from '../middlewares/getPackageInfo'; -import { getPackageDocs } from '../middlewares/getPackageDocs'; -import { copyDirectory, updateTemplateFiles } from './utils'; +import { getPackageInfo } from '../../middlewares/getPackageInfo'; +import { getPackageDocs } from '../../middlewares/getPackageDocs'; +import { copyDirectory, updateTemplateFiles } from './template'; import { genOpenApiAgent } from './genOpenApi'; import { genOpenApiMdAgent } from './genOpenApiMd'; import { genSdkMapAgent } from './genSdkMap'; @@ -75,8 +75,8 @@ slsfoxAgent } catch (error: any) { ctx.response.write({ type: 'text', text: `Error generating serverless structure: ${error.message}` }); } - }); + }); - slsfoxAgent.agent('/genOpenApi', genOpenApiAgent) - slsfoxAgent.agent('/genOpenApiMd', genOpenApiMdAgent) - slsfoxAgent.agent('/genSdkMap', genSdkMapAgent) \ No newline at end of file +slsfoxAgent.agent('/genOpenApi', genOpenApiAgent) +slsfoxAgent.agent('/genOpenApiMd', genOpenApiMdAgent) +slsfoxAgent.agent('/genSdkMap', genSdkMapAgent) \ No newline at end of file diff --git a/apps/code/lib/agents/slsFox/template.ts b/apps/code/lib/agents/slsFox/template.ts new file mode 100644 index 000000000..b6ac6f327 --- /dev/null +++ b/apps/code/lib/agents/slsFox/template.ts @@ -0,0 +1,75 @@ +import * as fs from 'fs'; +import * as path from 'path'; + + +export function copyDirectory(src: string, dest: string): void { + if (!fs.existsSync(dest)) { + fs.mkdirSync(dest, { recursive: true }); + } + + const entries = fs.readdirSync(src, { withFileTypes: true }); + + for (const entry of entries) { + const srcPath = path.join(src, entry.name); + + if (entry.isDirectory()) { + const destPath = path.join(dest, entry.name); + copyDirectory(srcPath, destPath); + } else { + let destFileName = entry.name; + if (entry.name.endsWith('.txt')) { + const baseName = entry.name.replace('.txt', ''); + const extensionMap: { [key: string]: string } = { + package: 'package.json', + tsconfig: 'tsconfig.json', + eslint: 'eslint.config.js', + serverless: 'serverless.yml', + openapi: 'openapi.json', + sdkInit: 'sdkInit.ts', + index: 'index.ts', + }; + destFileName = extensionMap[baseName] || `${baseName}.txt`; + } + + const destPath = path.join(dest, destFileName); + fs.copyFileSync(srcPath, destPath); + } + } +} + +export function updateTemplateFiles( + slsDir: string, + packageName: string, + description: string, +): void { + // Update package.json + const packageJsonPath = path.join(slsDir, 'package.json'); + const parentPackageJsonPath = path.join(slsDir, '..', 'package.json'); + const parentPackageJson = JSON.parse( + fs.readFileSync(parentPackageJsonPath, 'utf8'), + ); + if (fs.existsSync(packageJsonPath)) { + const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); + packageJson.name = `public-${packageName}-api`; + packageJson.description = description; + packageJson.dependencies['@microfox/tool-core'] = `^1.0.1`; + packageJson.dependencies[parentPackageJson.name] = + `^${parentPackageJson.version}`; + fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); + console.log(`✅ Updated package.json for ${packageJson.name}`); + } + + // Update serverless.yml + const serverlessPath = path.join(slsDir, 'serverless.yml'); + if (fs.existsSync(serverlessPath)) { + let serverlessContent = fs.readFileSync(serverlessPath, 'utf8'); + serverlessContent = serverlessContent.replace( + /^service:\s+.+$/m, + `service: public-${packageName}-api`, + ); + fs.writeFileSync(serverlessPath, serverlessContent); + console.log( + `✅ Updated serverless.yml for public-${packageName}-api`, + ); + } +} \ No newline at end of file From 99dc3aa199873298055babc31d35d4eac7c91629 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:30:32 +0530 Subject: [PATCH 08/24] feat: implement orchestrator agent for generating and executing step-by-step plans --- apps/code/lib/orchestrator.ts | 64 +++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 apps/code/lib/orchestrator.ts diff --git a/apps/code/lib/orchestrator.ts b/apps/code/lib/orchestrator.ts new file mode 100644 index 000000000..fa1188c9d --- /dev/null +++ b/apps/code/lib/orchestrator.ts @@ -0,0 +1,64 @@ +import { AiRouter } from '@microfox/ai-router'; +import { generateObject } from 'ai'; +import { google } from '@ai-sdk/google'; +import { z } from 'zod'; + +export const orchestratorAgent = new AiRouter(); + +const orchestratorSchema = z.object({ + // The user's high-level goal. + prompt: z.string().describe('The user a high-level goal to accomplish.'), +}); + +// This is the schema for the execution plan the orchestrator will generate. +const planSchema = z.object({ + plan: z + .array( + z.object({ + agent: z.enum(['summarize', 'docs', 'code']), + details: z.string().describe('The specific goal for this step.'), + }) + ) + .describe('The sequence of agents to call to accomplish the user goal.'), +}); + +orchestratorAgent + .actAsTool('/', { + description: 'Analyzes a user prompt and creates a step-by-step plan of agent calls.', + inputSchema: orchestratorSchema as any, + }) + + .agent('/', async ({ request, response, state, next }: any) => { + const { prompt } = await request.json(); + + // 1. Create the Execution Plan + response.write({ type: 'text', text: 'Generating execution plan...\n' }); + const { object: planResult } = await generateObject({ + model: google('gemini-2.5-pro-preview-06-05'), + prompt: `Based on the following user prompt, create a step-by-step execution plan. The available agents are: 'summarize', 'docs', 'code'. + +User Prompt: ${prompt}`, + schema: planSchema, + }); + + response.write({ type: 'text', text: `Plan: ${JSON.stringify(planResult.plan, null, 2)}\n\n` }); + + // 2. Execute the Plan + // Initialize the shared context that will be passed through the chain. + state.initialPrompt = prompt; + state.executionTrace = []; + + for (const step of planResult.plan) { + response.write({ type: 'text', text: `Executing agent: ${step.agent}...\n` }); + + const result = await next.callAgent(`/agent/${step.agent}`); + + if (!result.ok) { + const errorMsg = `Error executing agent ${step.agent}: ${result.error.message}`; + response.write({ type: 'text', text: errorMsg }); + return; // Stop execution on failure. + } + } + + response.write({ type: 'text', text: `\nExecution complete. Final result:\n${state.finalOutput}` }); + }); \ No newline at end of file From 5dd4bccfdb9f0317df473be0772917fb59bc095f Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Fri, 11 Jul 2025 17:31:55 +0530 Subject: [PATCH 09/24] chore: update TypeScript documentation link in next-env.d.ts --- apps/code/next-env.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/code/next-env.d.ts b/apps/code/next-env.d.ts index 40c3d6809..4f11a03dc 100644 --- a/apps/code/next-env.d.ts +++ b/apps/code/next-env.d.ts @@ -2,4 +2,4 @@ /// // NOTE: This file should not be edited -// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. +// see https://nextjs.org/docs/basic-features/typescript for more information. From 889dca529e13a744ef097b4d28f72b4833612902 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 01:27:03 +0530 Subject: [PATCH 10/24] feat: replace orchestrator agent with receptionist agent for improved request handling --- apps/code/app/api/agent/[[...slug]]/route.ts | 22 +++---- apps/code/app/api/orchestrate/route.ts | 27 --------- apps/code/lib/agents/receptionist.ts | 45 ++++++++++++++ apps/code/lib/orchestrator.ts | 64 -------------------- 4 files changed, 56 insertions(+), 102 deletions(-) delete mode 100644 apps/code/app/api/orchestrate/route.ts create mode 100644 apps/code/lib/agents/receptionist.ts delete mode 100644 apps/code/lib/orchestrator.ts diff --git a/apps/code/app/api/agent/[[...slug]]/route.ts b/apps/code/app/api/agent/[[...slug]]/route.ts index f23dca2b7..e24a97523 100644 --- a/apps/code/app/api/agent/[[...slug]]/route.ts +++ b/apps/code/app/api/agent/[[...slug]]/route.ts @@ -1,32 +1,32 @@ import { AiRouter } from '@microfox/ai-router'; -import { tracingMiddleware } from '@/lib/middleware'; -import { slsfoxAgent } from '@/lib/agents/slsFox'; +import { tracingMiddleware } from '@/lib/middlewares/tracingAgents'; +import { receptionistAgent } from '@/lib/agents/receptionist'; -// This router will handle direct calls to individual agents. const agentRouter = new AiRouter(); -// Apply the tracing middleware to all agents mounted on this router. agentRouter.use('*', tracingMiddleware); -// Mount the individual agent routers here. -agentRouter.agent('/slsfox', slsfoxAgent); +agentRouter.agent('/', receptionistAgent); // We will export a POST handler that processes all incoming requests. // The `[[...slug]]` in the filename makes this a catch-all route under /agent. export async function POST(request: Request, { params }: { params: { slug: string[] } }) { // Determine the path from the URL slug. const path = `/${(params.slug || []).join('/')}`; + + const body = await request.json(); + const { messages, ...restOfBody } = body; + console.log("body", body) + console.log("messages", messages) + console.log("path", path) - const body = await request.json() - - console.log("body", body, path) // Let the AiRouter handle the request and generate a response. // We add an empty 'messages' array to the request to satisfy the router's type. const response = agentRouter.handle(path, { request: { - ...body, - messages: [] + ...restOfBody, + messages: messages || [] } }); diff --git a/apps/code/app/api/orchestrate/route.ts b/apps/code/app/api/orchestrate/route.ts deleted file mode 100644 index 9baa387a5..000000000 --- a/apps/code/app/api/orchestrate/route.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { orchestratorAgent } from '@/lib/agents/orchestrator'; -import { tracingMiddleware } from '@/lib/middleware'; - -// Apply the tracing middleware to all routes in the orchestrator agent. -orchestratorAgent.use('*', tracingMiddleware); - -// We will export a POST handler that processes all incoming requests. -export async function POST(request: Request) { - // Let the orchestrator agent handle the request and generate a response. - // We pass '/' as the path because the orchestrator's base path is already set. - // We add an empty 'messages' array to the request to satisfy the router's type. - const body = await request.json() - - console.log("body", body) - - // Let the AiRouter handle the request and generate a response. - // We add an empty 'messages' array to the request to satisfy the router's type. - const response = orchestratorAgent.handle('/', { - request: { - ...body, - messages: [] - } - }); - - // Return the response generated by the agent. - return response; -} \ No newline at end of file diff --git a/apps/code/lib/agents/receptionist.ts b/apps/code/lib/agents/receptionist.ts new file mode 100644 index 000000000..067394429 --- /dev/null +++ b/apps/code/lib/agents/receptionist.ts @@ -0,0 +1,45 @@ +import { AiRouter } from '@microfox/ai-router'; +import { generateText } from 'ai'; +import { google } from '@ai-sdk/google'; +import { z } from 'zod'; +import { codeAgent } from '@/lib/agents/code'; +import { docsAgent } from '@/lib/agents/docs'; +import { summarizeAgent } from '@/lib/agents/summarize'; +import { slsfoxAgent } from '@/lib/agents/slsFox'; + +export const receptionistAgent = new AiRouter(); + +const receptionistSchema = z.object({ + prompt: z.string().describe('The user a high-level goal to accomplish.'), +}); + +receptionistAgent + .actAsTool('/', { + description: 'Analyzes a user prompt and calls the appropriate agent.', + inputSchema: receptionistSchema as any, + }) + .agent('/', async (ctx) => { + const { prompt } = ctx.request + + ctx.response.write({ type: 'text', text: 'Finding the right agent for the job...\n' }); + + const { toolCalls } = await generateText({ + model: google('gemini-2.5-pro-preview-06-05'), + prompt: `Based on the following user prompt, decide which agent to call. + +User Prompt: ${prompt}`, + tools: { + slsfox: ctx.next.agentAsTool("/slsfox"), + summarize: ctx.next.agentAsTool("/summarize"), + docs: ctx.next.agentAsTool("/docs"), + code: ctx.next.agentAsTool("/code"), + } + }); + + ctx.response.write({ type: 'text', text: `\nExecution complete. Tool Calls:\n${toolCalls}` }); + }); + +receptionistAgent.agent('/slsfox', slsfoxAgent); +receptionistAgent.agent('/code', codeAgent); +receptionistAgent.agent('/docs', docsAgent); +receptionistAgent.agent('/summarize', summarizeAgent); diff --git a/apps/code/lib/orchestrator.ts b/apps/code/lib/orchestrator.ts deleted file mode 100644 index fa1188c9d..000000000 --- a/apps/code/lib/orchestrator.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { AiRouter } from '@microfox/ai-router'; -import { generateObject } from 'ai'; -import { google } from '@ai-sdk/google'; -import { z } from 'zod'; - -export const orchestratorAgent = new AiRouter(); - -const orchestratorSchema = z.object({ - // The user's high-level goal. - prompt: z.string().describe('The user a high-level goal to accomplish.'), -}); - -// This is the schema for the execution plan the orchestrator will generate. -const planSchema = z.object({ - plan: z - .array( - z.object({ - agent: z.enum(['summarize', 'docs', 'code']), - details: z.string().describe('The specific goal for this step.'), - }) - ) - .describe('The sequence of agents to call to accomplish the user goal.'), -}); - -orchestratorAgent - .actAsTool('/', { - description: 'Analyzes a user prompt and creates a step-by-step plan of agent calls.', - inputSchema: orchestratorSchema as any, - }) - - .agent('/', async ({ request, response, state, next }: any) => { - const { prompt } = await request.json(); - - // 1. Create the Execution Plan - response.write({ type: 'text', text: 'Generating execution plan...\n' }); - const { object: planResult } = await generateObject({ - model: google('gemini-2.5-pro-preview-06-05'), - prompt: `Based on the following user prompt, create a step-by-step execution plan. The available agents are: 'summarize', 'docs', 'code'. - -User Prompt: ${prompt}`, - schema: planSchema, - }); - - response.write({ type: 'text', text: `Plan: ${JSON.stringify(planResult.plan, null, 2)}\n\n` }); - - // 2. Execute the Plan - // Initialize the shared context that will be passed through the chain. - state.initialPrompt = prompt; - state.executionTrace = []; - - for (const step of planResult.plan) { - response.write({ type: 'text', text: `Executing agent: ${step.agent}...\n` }); - - const result = await next.callAgent(`/agent/${step.agent}`); - - if (!result.ok) { - const errorMsg = `Error executing agent ${step.agent}: ${result.error.message}`; - response.write({ type: 'text', text: errorMsg }); - return; // Stop execution on failure. - } - } - - response.write({ type: 'text', text: `\nExecution complete. Final result:\n${state.finalOutput}` }); - }); \ No newline at end of file From 81900b23ad92494af2f452a84a71d56e2fd5ce8d Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 01:27:20 +0530 Subject: [PATCH 11/24] refactor: update agents to use unified routing and improve error handling --- apps/code/lib/agents/code.ts | 47 +++++++++++++++++++++---------- apps/code/lib/agents/docs.ts | 27 +++++++++--------- apps/code/lib/agents/summarize.ts | 20 ++++++------- 3 files changed, 55 insertions(+), 39 deletions(-) diff --git a/apps/code/lib/agents/code.ts b/apps/code/lib/agents/code.ts index 5a6d5e5f2..946cae091 100644 --- a/apps/code/lib/agents/code.ts +++ b/apps/code/lib/agents/code.ts @@ -1,44 +1,61 @@ import { AiRouter } from '@microfox/ai-router'; import { generateCodeV2 } from '@microfox/ai-code'; import { google } from '@ai-sdk/google'; +import { anthropic } from '@ai-sdk/anthropic'; import { z } from 'zod'; +import * as path from 'path'; export const codeAgent = new AiRouter(); const codeSchema = z.object({ - // Input comes from the context. + prompt: z.string().describe('The detailed prompt or description for the code to be generated.'), }); codeAgent - .actAsTool('/code', { - description: 'Generates code based on documentation from the context.', + .actAsTool('/', { + description: 'Generates code from a prompt or description.', inputSchema: codeSchema as any, }) - .agent('/code', async ({ state, response }: any) => { - const taskContext = state; - const prompt = taskContext.documentation || taskContext.initialPrompt; + .agent('/', async (ctx) => { + const prompt = ctx.request.params?.prompt as string if (!prompt) { - const errorMsg = 'Error: No prompt for code generation found in the context.'; - response.write({ type: 'text', text: errorMsg }); - taskContext.finalOutput = errorMsg; + const errorMsg = 'Error: No prompt for code generation provided.'; + ctx.response.write({ type: 'text', text: errorMsg }); return; } try { + let isFirstChunk = true; await generateCodeV2({ - model: google('gemini-2.5-pro-preview-06-05'), + model: anthropic('claude-4-opus-20250514'), + submodel: google('gemini-2.5-pro-preview-06-05'), systemPrompt: 'You are an expert programmer. Generate the code for the file as requested.', userPrompt: prompt, + dir: process.cwd(), + verbose: true, + onChunkSubmit: async ({ chunk, filePlan }) => { + if (isFirstChunk) { + const fullFileName = `${filePlan.fileName}.${filePlan.fileExtension}`; + const finalPath = path.join(filePlan.path, fullFileName); + ctx.response.write({ + type: 'data-metadata', + data: { + filePath: finalPath, + fileName: fullFileName, + }, + }); + isFirstChunk = false; + } + ctx.response.write({ type: 'text', text: chunk }); + }, onFileSubmit: async (filePath: string, code: string) => { - taskContext.code = code; - taskContext.finalOutput = code; - response.write({ type: 'text', text: code }); + ctx.response.write({ type: 'text', text: '\n[GENERATION COMPLETE]' }); + console.log(`File generation complete for ${filePath}. Total length: ${code.length}`); }, }); } catch (error: any) { const errorMessage = `Error generating code: ${error.message}`; - taskContext.finalOutput = errorMessage; - response.write({ type: 'text', text: errorMessage }); + ctx.response.write({ type: 'text', text: errorMessage }); } }); \ No newline at end of file diff --git a/apps/code/lib/agents/docs.ts b/apps/code/lib/agents/docs.ts index 58c741e3f..ef9b7ba15 100644 --- a/apps/code/lib/agents/docs.ts +++ b/apps/code/lib/agents/docs.ts @@ -6,30 +6,31 @@ import { z } from 'zod'; export const docsAgent = new AiRouter(); const docsSchema = z.object({ - // Input comes from the context. + topic: z.string().describe('The topic to generate documentation for.'), + context: z.any().describe('The context to generate documentation for.').optional() }); docsAgent - .actAsTool('/docs', { - description: 'Generates documentation based on a summary or topic from the context.', + .actAsTool('/', { + description: 'Generates documentation for a topic. Provide the topic and the context if available.', inputSchema: docsSchema as any, }) - .agent('/docs', async ({ state, response }: any) => { - const taskContext = state; - // This agent can build on the work of the previous one. - const topic = taskContext.summary || taskContext.initialPrompt; + .agent('/', async (ctx) => { + const topic = ctx.request.params?.topic as string + const context = ctx.request.params?.context as any if (!topic) { - response.write({ type: 'text', text: 'Error: No topic for documentation found in the context.' }); + ctx.response.write({ type: 'text', text: 'Error: No topic for documentation provided.' }); return; } const { text: documentation } = await generateText({ model: google('gemini-2.5-pro-preview-06-05'), - prompt: `Please generate technical documentation based on the following: ${topic}`, + prompt: `Please generate technical documentation based on the following: ${topic} + + context: ${JSON.stringify(context)} + `, }); - // Write the result back to the shared context and the response. - taskContext.documentation = documentation; - response.write({ type: 'text', text: documentation }); - }); \ No newline at end of file + ctx.response.write({ type: 'text', text: documentation }); + }); diff --git a/apps/code/lib/agents/summarize.ts b/apps/code/lib/agents/summarize.ts index d068d457e..e5fdf920b 100644 --- a/apps/code/lib/agents/summarize.ts +++ b/apps/code/lib/agents/summarize.ts @@ -6,20 +6,19 @@ import { z } from 'zod'; export const summarizeAgent = new AiRouter(); const summarizeSchema = z.object({ - // The input for this agent now comes from the context, so the schema is empty. + textToSummarize: z.string().describe('The text that needs to be summarized.'), }); summarizeAgent - .actAsTool('/summarize', { - description: 'Summarizes the initial prompt or other content found in the task context.', + .actAsTool('/', { + description: 'Summarizes a piece of text.', inputSchema: summarizeSchema as any, }) - .agent('/summarize', async ({ state, response }: any) => { - const taskContext = state; - const textToSummarize = taskContext.initialPrompt; + .agent('/', async (ctx) => { + const textToSummarize = ctx.request.params?.textToSummarize as string if (!textToSummarize) { - response.write({ type: 'text', text: 'Error: No text to summarize found in the context.' }); + ctx.response.write({ type: 'text', text: 'Error: No text to summarize provided.' }); return; } @@ -28,7 +27,6 @@ summarizeAgent prompt: `Please provide a concise summary of the following text: ${textToSummarize}`, }); - // Write the final summary back to the shared context and the response. - taskContext.summary = summary; - response.write({ type: 'text', text: summary }); - }); \ No newline at end of file + ctx.response.write({ type: 'text', text: summary }); + }); + \ No newline at end of file From 08ffbeab7894dda8d3173b1b4144e8c319ed7c2d Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 01:27:36 +0530 Subject: [PATCH 12/24] refactor: update agents to extract packageName and functionName from request parameters --- apps/code/lib/agents/slsFox/genOpenApi.ts | 7 +++++-- apps/code/lib/agents/slsFox/genOpenApiMd.ts | 2 +- apps/code/lib/agents/slsFox/genPathSpec.ts | 3 ++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/code/lib/agents/slsFox/genOpenApi.ts b/apps/code/lib/agents/slsFox/genOpenApi.ts index 8a87c464c..f44ffb850 100644 --- a/apps/code/lib/agents/slsFox/genOpenApi.ts +++ b/apps/code/lib/agents/slsFox/genOpenApi.ts @@ -64,7 +64,7 @@ genOpenApiAgent inputSchema: schema as any, }) .agent('/', async (ctx) => { - const { packageName } = ctx.request; + const packageName = ctx.request.params?.packageName as string try { const openapiDir = path.join(ctx.state[packageName].slsDir, 'openapi'); @@ -82,7 +82,10 @@ genOpenApiAgent } console.log('genOpenApiAgent', packageName, funcName); ctx.request.functionName = funcName; - await ctx.next.callAgent('/genPathSpec'); + await ctx.next.callAgent('/genPathSpec', { + packageName, + functionName: funcName, + }); } } diff --git a/apps/code/lib/agents/slsFox/genOpenApiMd.ts b/apps/code/lib/agents/slsFox/genOpenApiMd.ts index 99375f44e..ea3b929f1 100644 --- a/apps/code/lib/agents/slsFox/genOpenApiMd.ts +++ b/apps/code/lib/agents/slsFox/genOpenApiMd.ts @@ -94,7 +94,7 @@ genOpenApiMdAgent inputSchema: schema as any, }) .agent('/', async (ctx) => { - const { packageName } = ctx.request; + const packageName = ctx.request.params?.packageName as string try { const markdownContent = await generateOpenAPIMarkdown(ctx.state[packageName].packageInfo, ctx.state[packageName].slsDir); diff --git a/apps/code/lib/agents/slsFox/genPathSpec.ts b/apps/code/lib/agents/slsFox/genPathSpec.ts index ab308a109..1178c1289 100644 --- a/apps/code/lib/agents/slsFox/genPathSpec.ts +++ b/apps/code/lib/agents/slsFox/genPathSpec.ts @@ -187,7 +187,8 @@ genPathSpecAgent inputSchema: schema as any, }) .agent('/', async (ctx) => { - const { packageName, functionName } = ctx.request; + const packageName = ctx.request.params?.packageName as string + const functionName = ctx.request.params?.functionName as string console.log('genPathSpecAgent', packageName, functionName); try { const pathSpec = await generateOpenAPIPath( From 21d3abacfa752a2bbbd1d5035ba0e92e31cd19db Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 01:27:55 +0530 Subject: [PATCH 13/24] refactor: streamline request handling by extracting packageName --- apps/code/lib/agents/slsFox/genSdkMap.ts | 2 +- apps/code/lib/agents/slsFox/index.ts | 16 ++++++++++++---- apps/code/lib/agents/slsFox/mergePathSpec.ts | 2 +- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/apps/code/lib/agents/slsFox/genSdkMap.ts b/apps/code/lib/agents/slsFox/genSdkMap.ts index 24bfdd34f..b2ebcfd7d 100644 --- a/apps/code/lib/agents/slsFox/genSdkMap.ts +++ b/apps/code/lib/agents/slsFox/genSdkMap.ts @@ -132,7 +132,7 @@ genSdkMapAgent inputSchema: schema as any, }) .agent('/', async (ctx) => { - const { packageName } = ctx.request + const packageName = ctx.request.params?.packageName as string try { const sdkInitContent = await generateSDKInitContent(ctx.state[packageName].packageInfo); diff --git a/apps/code/lib/agents/slsFox/index.ts b/apps/code/lib/agents/slsFox/index.ts index 548d155c3..f65703901 100644 --- a/apps/code/lib/agents/slsFox/index.ts +++ b/apps/code/lib/agents/slsFox/index.ts @@ -21,11 +21,14 @@ slsfoxAgent .use('/', getPackageInfo) .use('/', getPackageDocs) .actAsTool('/', { - description: 'Generates the complete serverless structure for a package.', + description: 'Generates the complete sls structure for a package.', inputSchema: schema as any, }) .agent('/', async (ctx) => { - const { packageName, specificFunctions } = ctx.request + console.log("sdgdfhgjktjdhdnjdxngxdg", ctx.request) + console.log("sdgdhfchfcjgj", ctx) + const packageName = ctx.request.params?.packageName as string + const specificFunctions = ctx.request.params?.specificFunctions as string[] | undefined console.log("packageName", packageName) console.log("specificFunctions", specificFunctions) @@ -38,7 +41,10 @@ slsfoxAgent if (specificFunctions && specificFunctions.length > 0) { for (const func of specificFunctions) { ctx.request.functionName = func; - await ctx.next.callAgent('/genOpenApi/genPathSpec'); + await ctx.next.callAgent('/genOpenApi/genPathSpec', { + packageName, + functionName: func, + }); } ctx.response.write({ type: 'text', text: `Successfully generated openapi.json for ${packageName}'s functions ${specificFunctions.join(', ')}.` }); return; @@ -58,7 +64,9 @@ slsfoxAgent updateTemplateFiles(ctx.state[packageName].slsDir, packageName, ctx.state[packageName].packageInfo.description); // Generate sdkInit.ts - await ctx.next.callAgent('/genSdkMap', { packageName }); + await ctx.next.callAgent('/genSdkMap', { + packageName, + }); // Generate openapi.json await ctx.next.callAgent('/genOpenApi', { diff --git a/apps/code/lib/agents/slsFox/mergePathSpec.ts b/apps/code/lib/agents/slsFox/mergePathSpec.ts index d23d123fe..cdf986d3d 100644 --- a/apps/code/lib/agents/slsFox/mergePathSpec.ts +++ b/apps/code/lib/agents/slsFox/mergePathSpec.ts @@ -15,7 +15,7 @@ genPathSpecAgent inputSchema: schema as any, }) .agent('/', async (ctx) => { - const { packageName } = ctx.request; + const packageName = ctx.request.params?.packageName as string const slsOpenapiPath = path.join(ctx.state[packageName].slsDir, 'openapi.json'); let slsOpenapi = JSON.parse(fs.readFileSync(slsOpenapiPath, 'utf8')); From 545d158188bbdf02dbc74da18f96ad0ae14cc08c Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 01:28:53 +0530 Subject: [PATCH 14/24] chore: add aws-lambda and @types/aws-lambda dependencies to package.txt --- apps/code/lib/agents/slsFox/template/package.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/apps/code/lib/agents/slsFox/template/package.txt b/apps/code/lib/agents/slsFox/template/package.txt index 0530c3039..4de92f717 100644 --- a/apps/code/lib/agents/slsFox/template/package.txt +++ b/apps/code/lib/agents/slsFox/template/package.txt @@ -18,9 +18,11 @@ "dependencies": { "@microfox/crypto-sdk": "^1.0.3", "@microfox/tool-core": "^1.0.4", + "aws-lambda": "^1.0.7", "dotenv": "^16.4.5" }, "devDependencies": { + "@types/aws-lambda": "^8.10.150", "@types/node": "^20.11.19", "@typescript-eslint/eslint-plugin": "^8.28.0", "@typescript-eslint/parser": "^8.28.0", From 68a875dcebd0ce4c299bc3add2002ae51bc8edbd Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 02:32:55 +0530 Subject: [PATCH 15/24] refactor: enhance package middleware to handle missing packageName in request parameters --- apps/code/lib/middlewares/getPackageDocs.ts | 5 ++++- apps/code/lib/middlewares/getPackageInfo.ts | 6 +++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/apps/code/lib/middlewares/getPackageDocs.ts b/apps/code/lib/middlewares/getPackageDocs.ts index 8d4872202..1dad30161 100644 --- a/apps/code/lib/middlewares/getPackageDocs.ts +++ b/apps/code/lib/middlewares/getPackageDocs.ts @@ -4,7 +4,10 @@ import * as path from 'path'; import { getProjectRoot } from '@/lib/helpers/utils'; export const getPackageDocs: AiMiddleware = async (ctx, next) => { - let { packageName } = ctx.request; + let { packageName } = ctx.request.params; + if (!packageName) { + return next(); + } const taskContext = ctx.state; packageName = packageName.replace('@microfox/', ''); if (taskContext[packageName] && taskContext[packageName].docs) { diff --git a/apps/code/lib/middlewares/getPackageInfo.ts b/apps/code/lib/middlewares/getPackageInfo.ts index 0a32826f6..4892b9a6a 100644 --- a/apps/code/lib/middlewares/getPackageInfo.ts +++ b/apps/code/lib/middlewares/getPackageInfo.ts @@ -4,7 +4,11 @@ import * as path from 'path'; import { getProjectRoot } from '@/lib/helpers/utils'; export const getPackageInfo: AiMiddleware = async (ctx, next) => { - let { packageName } = ctx.request + let { packageName } = ctx.request.params + if (!packageName) { + return next(); + } + const taskContext = ctx.state; packageName = packageName.replace('@microfox/', ''); if (taskContext[packageName] && taskContext[packageName].packageInfo) { From 2fbdf9c6bb6c5c2d5f7128038ae5add9886a9a23 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 02:34:03 +0530 Subject: [PATCH 16/24] feat: implement receptionist agent to analyze user prompts and route to agents --- .../lib/agents/{receptionist.ts => index.ts} | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) rename apps/code/lib/agents/{receptionist.ts => index.ts} (71%) diff --git a/apps/code/lib/agents/receptionist.ts b/apps/code/lib/agents/index.ts similarity index 71% rename from apps/code/lib/agents/receptionist.ts rename to apps/code/lib/agents/index.ts index 067394429..7efcd1d2a 100644 --- a/apps/code/lib/agents/receptionist.ts +++ b/apps/code/lib/agents/index.ts @@ -7,16 +7,16 @@ import { docsAgent } from '@/lib/agents/docs'; import { summarizeAgent } from '@/lib/agents/summarize'; import { slsfoxAgent } from '@/lib/agents/slsFox'; -export const receptionistAgent = new AiRouter(); +export const receptionistAgent = new AiRouter(); -const receptionistSchema = z.object({ +const receptionistInput = z.object({ prompt: z.string().describe('The user a high-level goal to accomplish.'), }); receptionistAgent .actAsTool('/', { description: 'Analyzes a user prompt and calls the appropriate agent.', - inputSchema: receptionistSchema as any, + inputSchema: receptionistInput as any, }) .agent('/', async (ctx) => { const { prompt } = ctx.request @@ -25,9 +25,14 @@ receptionistAgent const { toolCalls } = await generateText({ model: google('gemini-2.5-pro-preview-06-05'), - prompt: `Based on the following user prompt, decide which agent to call. - -User Prompt: ${prompt}`, + prompt: `You are a highly intelligent routing system. Your purpose is to deeply analyze a user's request to understand its fundamental intent. From this analysis, you will determine the single most appropriate specialized function to execute. + +Analyze the following user prompt: +--- +${prompt} +--- + +Now, determine the core task the user wants to accomplish and invoke the corresponding function.`, tools: { slsfox: ctx.next.agentAsTool("/slsfox"), summarize: ctx.next.agentAsTool("/summarize"), From 7731e9ba7724a644a83051df1ff4d818e11e9a93 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 02:34:16 +0530 Subject: [PATCH 17/24] refactor: update import path for receptionist agent to improve code organization --- apps/code/app/api/agent/[[...slug]]/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/code/app/api/agent/[[...slug]]/route.ts b/apps/code/app/api/agent/[[...slug]]/route.ts index e24a97523..73365a605 100644 --- a/apps/code/app/api/agent/[[...slug]]/route.ts +++ b/apps/code/app/api/agent/[[...slug]]/route.ts @@ -1,6 +1,6 @@ import { AiRouter } from '@microfox/ai-router'; import { tracingMiddleware } from '@/lib/middlewares/tracingAgents'; -import { receptionistAgent } from '@/lib/agents/receptionist'; +import { receptionistAgent } from '@/lib/agents'; const agentRouter = new AiRouter(); From 9417c86d901a33cb82df6025bbb6b4f998df5854 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 02:35:04 +0530 Subject: [PATCH 18/24] feat: enhance slsFox agent to support natural language queries --- apps/code/lib/agents/slsFox/index.ts | 88 ++++++++++------------------ 1 file changed, 31 insertions(+), 57 deletions(-) diff --git a/apps/code/lib/agents/slsFox/index.ts b/apps/code/lib/agents/slsFox/index.ts index f65703901..e95672488 100644 --- a/apps/code/lib/agents/slsFox/index.ts +++ b/apps/code/lib/agents/slsFox/index.ts @@ -1,90 +1,64 @@ import { AiRouter } from '@microfox/ai-router'; import { z } from 'zod'; -import * as fs from 'fs'; -import * as path from 'path'; +import { generateText } from 'ai'; +import { google } from '@ai-sdk/google'; import { getPackageInfo } from '../../middlewares/getPackageInfo'; import { getPackageDocs } from '../../middlewares/getPackageDocs'; -import { copyDirectory, updateTemplateFiles } from './template'; +import { genFullSlsAgent } from './genFullSls'; import { genOpenApiAgent } from './genOpenApi'; import { genOpenApiMdAgent } from './genOpenApiMd'; import { genSdkMapAgent } from './genSdkMap'; +import { genPathSpecAgent } from './genPathSpec'; -export const slsfoxAgent = new AiRouter(); +export const slsfoxAgent = new AiRouter(); const schema = z.object({ - packageName: z.string().describe('The name of the package (e.g., "google-sheets").'), - specificFunctions: z.array(z.string()).optional().describe('Optional list of specific functions to update.'), + packageName: z.string().optional().describe('The name of the package (e.g., "google-sheets").'), + query: z.string().optional().describe('A natural language prompt for what to build or update.'), }); - slsfoxAgent .use('/', getPackageInfo) .use('/', getPackageDocs) .actAsTool('/', { - description: 'Generates the complete sls structure for a package.', + description: 'Generates or updates the serverless structure for a package based on a prompt.', inputSchema: schema as any, }) .agent('/', async (ctx) => { - console.log("sdgdfhgjktjdhdnjdxngxdg", ctx.request) - console.log("sdgdhfchfcjgj", ctx) - const packageName = ctx.request.params?.packageName as string - const specificFunctions = ctx.request.params?.specificFunctions as string[] | undefined - - console.log("packageName", packageName) - console.log("specificFunctions", specificFunctions) + const { query, packageName: initialPackageName } = ctx.request.params as z.infer; - if (!packageName) { - ctx.response.write({ type: 'text', text: `packageName is required` }); + if (!query && !initialPackageName) { + ctx.response.write({ type: 'text', text: `Either a query or a package name is required.` }); return; } - if (specificFunctions && specificFunctions.length > 0) { - for (const func of specificFunctions) { - ctx.request.functionName = func; - await ctx.next.callAgent('/genOpenApi/genPathSpec', { - packageName, - functionName: func, - }); - } - ctx.response.write({ type: 'text', text: `Successfully generated openapi.json for ${packageName}'s functions ${specificFunctions.join(', ')}.` }); + if (!query){ + await ctx.next.callAgent('/genFullSls', { packageName: initialPackageName }); return; } - try { - // Copy template directory - const templateDir = path.join(process.cwd(), 'lib', 'agents', 'slsFox', 'template'); - if (fs.existsSync(ctx.state[packageName].slsDir)) { - console.log(`⚠️ SLS directory already exists, removing it first...`); - fs.rmSync(ctx.state[packageName].slsDir, { recursive: true, force: true }); - } - copyDirectory(templateDir, ctx.state[packageName].slsDir); - console.log(`✅ Template copied successfully`); - - // Update template files - updateTemplateFiles(ctx.state[packageName].slsDir, packageName, ctx.state[packageName].packageInfo.description); + ctx.response.write({ type: 'text', text: 'Deciding which tool to use...\n' }); - // Generate sdkInit.ts - await ctx.next.callAgent('/genSdkMap', { - packageName, - }); + const { toolCalls } = await generateText({ + model: google('gemini-2.5-pro-preview-06-05'), + prompt: `You are an expert system for managing serverless package structures. Based on the user's request, decide which tool to call. Extract the packageName and any other relevant parameters from the request. - // Generate openapi.json - await ctx.next.callAgent('/genOpenApi', { - packageName, - }); - - // Generate openapi.md - await ctx.next.callAgent('/genOpenApiMd', { - packageName, - }); - - ctx.response.write({ type: 'text', text: `Successfully generated serverless structure for ${packageName}.` }); +User Request: ${query || `Generate full structure for ${initialPackageName}`}`, + tools: { + generateFullSls: ctx.next.agentAsTool('/genFullSls'), + generateOpenApi: ctx.next.agentAsTool('/genOpenApi'), + generateOpenApiMd: ctx.next.agentAsTool('/genOpenApiMd'), + generateSdkMap: ctx.next.agentAsTool('/genSdkMap'), + generatePathSpec: ctx.next.agentAsTool('/genPathSpec'), + } + }); - } catch (error: any) { - ctx.response.write({ type: 'text', text: `Error generating serverless structure: ${error.message}` }); - } + ctx.response.write({ type: 'text', text: `\nExecution complete. Tool Calls:\n${JSON.stringify(toolCalls)}` }); }); + +slsfoxAgent.agent('/genFullSls', genFullSlsAgent) slsfoxAgent.agent('/genOpenApi', genOpenApiAgent) slsfoxAgent.agent('/genOpenApiMd', genOpenApiMdAgent) -slsfoxAgent.agent('/genSdkMap', genSdkMapAgent) \ No newline at end of file +slsfoxAgent.agent('/genSdkMap', genSdkMapAgent) +slsfoxAgent.agent('/genPathSpec', genPathSpecAgent) From 2a9156f5657f8385c2d12b9a71f40682c013ea06 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 02:35:12 +0530 Subject: [PATCH 19/24] feat: add genFullSls agent to generate complete serverless structure for packages --- apps/code/lib/agents/slsFox/genFullSls.ts | 42 +++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 apps/code/lib/agents/slsFox/genFullSls.ts diff --git a/apps/code/lib/agents/slsFox/genFullSls.ts b/apps/code/lib/agents/slsFox/genFullSls.ts new file mode 100644 index 000000000..78c2bc4c7 --- /dev/null +++ b/apps/code/lib/agents/slsFox/genFullSls.ts @@ -0,0 +1,42 @@ +import { AiRouter } from '@microfox/ai-router'; +import { z } from 'zod'; +import { generateObject } from 'ai'; +import path from 'path'; +import * as fs from 'fs'; +import { anthropic } from '@ai-sdk/anthropic'; +import { PackageInfo } from '@/lib/types/PackageInfo'; +import { copyDirectory, updateTemplateFiles } from './template'; + +export const genFullSlsAgent = new AiRouter(); + +const fullSlsSchema = z.object({ + packageName: z.string().describe('The name of the package for which to generate the full structure.'), +}); + +genFullSlsAgent + .actAsTool('/', { + description: 'Generates the complete sls structure for a package.', + inputSchema: fullSlsSchema as any + }) + .agent('/', async (ctx) => { + const packageName = ctx.request.params?.packageName as string + ctx.response.write({ type: 'text', text: `Generating full SLS structure for ${packageName}...\n` }); + + try { + const templateDir = path.join(process.cwd(), 'lib', 'agents', 'slsFox', 'template'); + if (fs.existsSync(ctx.state[packageName].slsDir)) { + fs.rmSync(ctx.state[packageName].slsDir, { recursive: true, force: true }); + } + copyDirectory(templateDir, ctx.state[packageName].slsDir); + updateTemplateFiles(ctx.state[packageName].slsDir, packageName, ctx.state[packageName].packageInfo.description); + + await ctx.next.callAgent('@/slsfox/genSdkMap', { packageName }); + await ctx.next.callAgent('@/slsfox/genOpenApi', { packageName }); + await ctx.next.callAgent('@/slsfox/genOpenApiMd', { packageName }); + + ctx.response.write({ type: 'text', text: `Successfully generated serverless structure for ${packageName}.\n` }); + + } catch (error: any) { + ctx.response.write({ type: 'text', text: `Error generating serverless structure: ${error.message}` }); + } + }); From 78b8592b9526458d71ac5fcbbb4578a360169270 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 02:35:22 +0530 Subject: [PATCH 20/24] refactor: update agents to use generic types for improved type safety --- apps/code/lib/agents/slsFox/genOpenApi.ts | 7 ++----- apps/code/lib/agents/slsFox/genOpenApiMd.ts | 2 +- apps/code/lib/agents/slsFox/genPathSpec.ts | 2 +- apps/code/lib/agents/slsFox/genSdkMap.ts | 2 +- 4 files changed, 5 insertions(+), 8 deletions(-) diff --git a/apps/code/lib/agents/slsFox/genOpenApi.ts b/apps/code/lib/agents/slsFox/genOpenApi.ts index f44ffb850..03dbbdc14 100644 --- a/apps/code/lib/agents/slsFox/genOpenApi.ts +++ b/apps/code/lib/agents/slsFox/genOpenApi.ts @@ -2,9 +2,8 @@ import { AiRouter } from '@microfox/ai-router'; import { z } from 'zod'; import * as fs from 'fs'; import * as path from 'path'; -import { genPathSpecAgent } from './genPathSpec'; -export const genOpenApiAgent = new AiRouter(); +export const genOpenApiAgent = new AiRouter(); const schema = z.object({ packageName: z.string().describe('The name of the package (e.g., "google-sheets").'), @@ -82,7 +81,7 @@ genOpenApiAgent } console.log('genOpenApiAgent', packageName, funcName); ctx.request.functionName = funcName; - await ctx.next.callAgent('/genPathSpec', { + await ctx.next.callAgent('@/slsfox/genPathSpec', { packageName, functionName: funcName, }); @@ -95,5 +94,3 @@ genOpenApiAgent ctx.response.write({ type: 'text', text: `Error generating OpenAPI spec: ${error.message}` }); } }); - -genOpenApiAgent.agent('/genPathSpec', genPathSpecAgent) diff --git a/apps/code/lib/agents/slsFox/genOpenApiMd.ts b/apps/code/lib/agents/slsFox/genOpenApiMd.ts index ea3b929f1..a4afb26cf 100644 --- a/apps/code/lib/agents/slsFox/genOpenApiMd.ts +++ b/apps/code/lib/agents/slsFox/genOpenApiMd.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { google } from '@ai-sdk/google'; -export const genOpenApiMdAgent = new AiRouter(); +export const genOpenApiMdAgent = new AiRouter(); const schema = z.object({ packageName: z.string().describe('The name of the package (e.g., "google-sheets").'), diff --git a/apps/code/lib/agents/slsFox/genPathSpec.ts b/apps/code/lib/agents/slsFox/genPathSpec.ts index 1178c1289..c4117d3fc 100644 --- a/apps/code/lib/agents/slsFox/genPathSpec.ts +++ b/apps/code/lib/agents/slsFox/genPathSpec.ts @@ -6,7 +6,7 @@ import { anthropic } from '@ai-sdk/anthropic'; import path from 'path'; import * as fs from 'fs'; -export const genPathSpecAgent = new AiRouter(); +export const genPathSpecAgent = new AiRouter(); const schema = z.object({ functionName: z diff --git a/apps/code/lib/agents/slsFox/genSdkMap.ts b/apps/code/lib/agents/slsFox/genSdkMap.ts index b2ebcfd7d..040d62106 100644 --- a/apps/code/lib/agents/slsFox/genSdkMap.ts +++ b/apps/code/lib/agents/slsFox/genSdkMap.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import { anthropic } from '@ai-sdk/anthropic'; import { PackageInfo } from '@/lib/types/PackageInfo'; -export const genSdkMapAgent = new AiRouter(); +export const genSdkMapAgent = new AiRouter(); const schema = z.object({ packageName: z.string().describe('The name of the package (e.g., "google-sheets").'), From 019e7bf85698fb4f3e2e0c58fcbf958faaeb3b4c Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 04:57:09 +0530 Subject: [PATCH 21/24] refactor: update getProjectRoot function to support async execution --- apps/code/lib/helpers/utils.ts | 22 +++++++++++++++------ apps/code/lib/middlewares/getPackageDocs.ts | 2 +- apps/code/lib/middlewares/getPackageInfo.ts | 2 +- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/apps/code/lib/helpers/utils.ts b/apps/code/lib/helpers/utils.ts index 80795bb92..9a0609d71 100644 --- a/apps/code/lib/helpers/utils.ts +++ b/apps/code/lib/helpers/utils.ts @@ -1,10 +1,20 @@ import * as path from 'path'; import * as fs from 'fs'; -export function getProjectRoot(): string { - let currentDir = __dirname; - while (!fs.existsSync(path.join(currentDir, 'package.json'))) { - currentDir = path.dirname(currentDir); +export async function getProjectRoot(startPath: string = process.cwd()): Promise { + let currentPath = startPath; + let count = 0 + while (true) { + const microfoxRootPath = path.join(currentPath, 'microfox-root'); + if (fs.existsSync(microfoxRootPath)) { + return currentPath; + } + + const parentPath = path.dirname(currentPath); + if (parentPath === currentPath || count > 10) { + // Reached the root of the file system + return null; + } + currentPath = parentPath; } - return path.join(currentDir, '..', '..', '..') -} +} \ No newline at end of file diff --git a/apps/code/lib/middlewares/getPackageDocs.ts b/apps/code/lib/middlewares/getPackageDocs.ts index 1dad30161..cbbfd4227 100644 --- a/apps/code/lib/middlewares/getPackageDocs.ts +++ b/apps/code/lib/middlewares/getPackageDocs.ts @@ -15,7 +15,7 @@ export const getPackageDocs: AiMiddleware = async (ctx, next) => { } try { // 1. Initialize Paths - taskContext.projectRoot = getProjectRoot(); + taskContext.projectRoot = await getProjectRoot(); taskContext[packageName] = { ...taskContext[packageName], packageDir: path.join(taskContext.projectRoot, 'packages', packageName), diff --git a/apps/code/lib/middlewares/getPackageInfo.ts b/apps/code/lib/middlewares/getPackageInfo.ts index 4892b9a6a..d9d672bbb 100644 --- a/apps/code/lib/middlewares/getPackageInfo.ts +++ b/apps/code/lib/middlewares/getPackageInfo.ts @@ -17,7 +17,7 @@ export const getPackageInfo: AiMiddleware = async (ctx, next) => { try { // 1. Initialize Paths - taskContext.projectRoot = getProjectRoot(); + taskContext.projectRoot = await getProjectRoot(); taskContext[packageName] = { ...taskContext[packageName], packageDir: path.join(taskContext.projectRoot, 'packages', packageName), From 1a76c86f7d40838fb9e04498853770d03f4a1475 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 04:58:55 +0530 Subject: [PATCH 22/24] chore: update commander and zod dependencies to latest versions --- packages/cli/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/cli/package.json b/packages/cli/package.json index 189188095..5463508de 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -30,11 +30,11 @@ "@types/micromatch": "^4.0.9", "axios": "^1.10.0", "chalk": "^5.4.1", - "commander": "^13.1.0", + "commander": "^14.0.0", "inquirer": "^12.7.0", "micromatch": "^4.0.8", "readline-sync": "^1.4.10", - "zod": "^3.25.67" + "zod": "^4.0.5" }, "devDependencies": { "@microfox/tsconfig": "*", From 409a2f5b82dc08f65339f6b255b3487f975e69f4 Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 04:59:06 +0530 Subject: [PATCH 23/24] feat: add findProjectRoot utility to locate the project root directory --- microfox-root | 0 packages/cli/src/utils/findProjectRoot.ts | 20 ++++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 microfox-root create mode 100644 packages/cli/src/utils/findProjectRoot.ts diff --git a/microfox-root b/microfox-root new file mode 100644 index 000000000..e69de29bb diff --git a/packages/cli/src/utils/findProjectRoot.ts b/packages/cli/src/utils/findProjectRoot.ts new file mode 100644 index 000000000..e17fc3a1b --- /dev/null +++ b/packages/cli/src/utils/findProjectRoot.ts @@ -0,0 +1,20 @@ +import path from 'path'; +import fs from 'fs'; + +export async function findProjectRoot(startPath: string = process.cwd()): Promise { + let currentPath = startPath; + let count = 0 + while (true) { + const microfoxRootPath = path.join(currentPath, 'microfox-root'); + if (fs.existsSync(microfoxRootPath)) { + return currentPath; + } + + const parentPath = path.dirname(currentPath); + if (parentPath === currentPath || count > 10) { + // Reached the root of the file system + return null; + } + currentPath = parentPath; + } +} \ No newline at end of file From 53a43355ccaec906f8449fc83b1c90c80b9eb9aa Mon Sep 17 00:00:00 2001 From: VISHWAJ33T Date: Sat, 12 Jul 2025 04:59:22 +0530 Subject: [PATCH 24/24] feat: add code command to run Next.js server and interact with the code agent --- packages/cli/src/cli.ts | 13 +++ packages/cli/src/commands/code.ts | 143 ++++++++++++++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 packages/cli/src/commands/code.ts diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 8f5d9b900..c3d042657 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -3,6 +3,7 @@ import chalk from 'chalk'; import { kickstartCommand } from './commands/kickstart'; import { pushCommand } from './commands/push'; import { statusCommand, logsCommand, metricsCommand } from './commands/status'; +import { codeCommand } from './commands/code'; import { version } from '../package.json'; const program = new Command(); @@ -73,6 +74,18 @@ program } }); +program + .command('code') + .description('Run the code agent for your project') + .action(async () => { + try { + await codeCommand(); + } catch (error) { + console.error(chalk.red('❌ Error:'), error instanceof Error ? error.message : String(error)); + process.exit(1); + } + }); + // Show help if no command is provided if (process.argv.length <= 2) { program.help(); diff --git a/packages/cli/src/commands/code.ts b/packages/cli/src/commands/code.ts new file mode 100644 index 000000000..0e5dcbcf1 --- /dev/null +++ b/packages/cli/src/commands/code.ts @@ -0,0 +1,143 @@ +import chalk from 'chalk'; +import axios from 'axios'; +import { spawn, ChildProcess } from 'child_process'; +import { findProjectRoot } from '../utils/findProjectRoot'; +import path from 'path'; +import readline from 'readline'; + +const NEXTJS_PORT = 3000; +const API_URL = `http://localhost:${NEXTJS_PORT}/api/agent`; + +const createLogger = (rl: readline.Interface) => { + return (source: string, message: string, color: chalk.Chalk) => { + readline.cursorTo(process.stdout, 0); + readline.clearLine(process.stdout, 0); + + const prefix = color(`[${source}]`); + + const lines = message.trim().split('\n'); + for (const line of lines) { + console.log(`${prefix} ${line}`); + } + + rl.prompt(true); + }; +}; + + +export async function codeCommand(): Promise { + let childProcess: ChildProcess | null = null; + + const killProcess = () => { + if (childProcess && childProcess.pid) { + console.log(chalk.yellow('\nGracefully shutting down...')); + if (process.platform === 'win32') { + spawn('taskkill', ['/pid', childProcess.pid.toString(), '/f', '/t']); + } else { + childProcess.kill('SIGINT'); + } + childProcess = null; + } + }; + + process.on('SIGINT', () => { + killProcess(); + process.exit(0); + }); + process.on('exit', killProcess); + + + try { + const projectRoot = await findProjectRoot(); + if (!projectRoot) { + console.error( + chalk.red('Error: Could not find project root. Make sure you are inside a Microfox project.') + ); + process.exit(1); + } + + const codeAppPath = path.join(projectRoot, 'apps', 'code'); + + console.log(chalk.cyan(`Starting Next.js server in ${codeAppPath}...`)); + + childProcess = spawn('npm', ['run', 'dev'], { + cwd: codeAppPath, + shell: true, + env: { ...process.env, FORCE_COLOR: 'true' } + }); + + const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + prompt: chalk.cyan('> ') + }); + + const log = createLogger(rl); + + let serverReady = false; + + const onServerData = (data: Buffer) => { + const output = data.toString(); + if (!serverReady) { + process.stdout.write(output); + if (output.toLowerCase().includes('ready in') || output.toLowerCase().includes('compiled successfully')) { + serverReady = true; + console.log(chalk.green('\nServer is ready. You can now type your queries.')); + rl.prompt(); + } + } else { + log('nextjs', output, chalk.gray); + } + }; + + childProcess.stdout?.on('data', onServerData); + childProcess.stderr?.on('data', onServerData); + + childProcess.on('exit', (code) => { + log('system', `Next.js process exited with code ${code}`, chalk.red); + process.exit(code ?? 1); + }); + + rl.on('line', async (line) => { + const query = line.trim(); + if (!serverReady) { + log('system', 'Server is not ready yet, please wait.', chalk.yellow); + rl.prompt(); + return; + } + if (query.toLowerCase() === 'exit') { + rl.close(); + } + if (query) { + try { + const response = await axios.post(API_URL, { prompt: query }); + const responseData = typeof response.data === 'object' + ? JSON.stringify(response.data, null, 2) + : response.data; + + log('agent', responseData, chalk.green); + + } catch (error) { + if (axios.isAxiosError(error)) { + log('agent', `Error: ${error.message}`, chalk.red); + } else if (error instanceof Error) { + log('agent', `An unknown error occurred: ${error.message}`, chalk.red); + } + } + } + rl.prompt(); + }); + + rl.on('close', () => { + killProcess(); + process.exit(0); + }); + + } catch (error) { + killProcess(); + if (error instanceof Error) { + console.error(chalk.red(`Error: ${error.message}`)); + } + process.exit(1); + } +} \ No newline at end of file