rolax 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,22 @@
1
+ # rolax
2
+
3
+ # App builder
4
+
5
+ Usage:
6
+ rolax <app-name> <path>
7
+
8
+ ##
9
+ Start chat session with user and interview them about their app ideas.
10
+ Ask them to write anything they want about their app, these could be incomplete sentences.
11
+ User can exit AI session by typing exit/quit/bye.
12
+ Once these information is captured they will be sent to an LLM and the expected result
13
+ will be AGENTS.md and TODOS.md info in JSON format. That information will be used
14
+ to populate the respective md files.
15
+
16
+ Will create following project structure and md files
17
+
18
+ <app-name>
19
+ - src
20
+ -- index.ts
21
+ - AGENTS.md
22
+ - TODOS.md
package/bun.lock ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "lockfileVersion": 1,
3
+ "workspaces": {
4
+ "": {
5
+ "name": "rolax",
6
+ "dependencies": {
7
+ "dotenv": "^17.2.3",
8
+ },
9
+ "devDependencies": {
10
+ "@types/bun": "latest",
11
+ },
12
+ "peerDependencies": {
13
+ "typescript": "^5",
14
+ },
15
+ },
16
+ },
17
+ "packages": {
18
+ "@types/bun": ["@types/bun@1.3.6", "", { "dependencies": { "bun-types": "1.3.6" } }, "sha512-uWCv6FO/8LcpREhenN1d1b6fcspAB+cefwD7uti8C8VffIv0Um08TKMn98FynpTiU38+y2dUO55T11NgDt8VAA=="],
19
+
20
+ "@types/node": ["@types/node@25.0.10", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg=="],
21
+
22
+ "bun-types": ["bun-types@1.3.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-OlFwHcnNV99r//9v5IIOgQ9Uk37gZqrNMCcqEaExdkVq3Avwqok1bJFmvGMCkCE0FqzdY8VMOZpfpR3lwI+CsQ=="],
23
+
24
+ "dotenv": ["dotenv@17.2.3", "", {}, "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w=="],
25
+
26
+ "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
27
+
28
+ "undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
29
+ }
30
+ }
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ import{createRequire as t}from"node:module";var D=Object.create;var{getPrototypeOf:M,defineProperty:q,getOwnPropertyNames:_}=Object;var d=Object.prototype.hasOwnProperty;var Z=(c,j,R)=>{R=c!=null?D(M(c)):{};let f=j||!c||!c.__esModule?q(R,"default",{value:c,enumerable:!0}):R;for(let O of _(c))if(!d.call(f,O))q(f,O,{get:()=>c[O],enumerable:!0});return f};var F=(c,j)=>()=>(j||c((j={exports:{}}).exports,j),j.exports);var U=t(import.meta.url);var H=F((Zc,h)=>{h.exports={name:"dotenv",version:"17.2.3",description:"Loads environment variables from .env file",main:"lib/main.js",types:"lib/main.d.ts",exports:{".":{types:"./lib/main.d.ts",require:"./lib/main.js",default:"./lib/main.js"},"./config":"./config.js","./config.js":"./config.js","./lib/env-options":"./lib/env-options.js","./lib/env-options.js":"./lib/env-options.js","./lib/cli-options":"./lib/cli-options.js","./lib/cli-options.js":"./lib/cli-options.js","./package.json":"./package.json"},scripts:{"dts-check":"tsc --project tests/types/tsconfig.json",lint:"standard",pretest:"npm run lint && npm run dts-check",test:"tap run tests/**/*.js --allow-empty-coverage --disable-coverage --timeout=60000","test:coverage":"tap run tests/**/*.js --show-full-coverage --timeout=60000 --coverage-report=text --coverage-report=lcov",prerelease:"npm test",release:"standard-version"},repository:{type:"git",url:"git://github.com/motdotla/dotenv.git"},homepage:"https://github.com/motdotla/dotenv#readme",funding:"https://dotenvx.com",keywords:["dotenv","env",".env","environment","variables","config","settings"],readmeFilename:"README.md",license:"BSD-2-Clause",devDependencies:{"@types/node":"^18.11.3",decache:"^4.6.2",sinon:"^14.0.1",standard:"^17.0.0","standard-version":"^9.5.0",tap:"^19.2.0",typescript:"^4.8.4"},engines:{node:">=12"},browser:{fs:!1}}});var G=F((Fc,A)=>{var y=U("fs"),Y=U("path"),s=U("os"),l=U("crypto"),r=H(),z=r.version,N=["\uD83D\uDD10 encrypt with Dotenvx: https://dotenvx.com","\uD83D\uDD10 prevent committing .env to code: https://dotenvx.com/precommit","\uD83D\uDD10 prevent building .env in docker: https://dotenvx.com/prebuild","\uD83D\uDCE1 add observability to secrets: https://dotenvx.com/ops","\uD83D\uDC65 sync secrets across teammates & machines: https://dotenvx.com/ops","\uD83D\uDDC2️ backup and recover secrets: https://dotenvx.com/ops","✅ audit secrets and track compliance: https://dotenvx.com/ops","\uD83D\uDD04 add secrets lifecycle management: https://dotenvx.com/ops","\uD83D\uDD11 add access controls to secrets: https://dotenvx.com/ops","\uD83D\uDEE0️ run anywhere with `dotenvx run -- yourcommand`","⚙️ specify custom .env file path with { path: '/custom/path/.env' }","⚙️ enable debug logging with { debug: true }","⚙️ override existing env vars with { override: true }","⚙️ suppress all logs with { quiet: true }","⚙️ write to custom object with { processEnv: myObject }","⚙️ load multiple .env files with { path: ['.env.local', '.env'] }"];function e(){return N[Math.floor(Math.random()*N.length)]}function x(c){if(typeof c==="string")return!["false","0","no","off",""].includes(c.toLowerCase());return Boolean(c)}function u(){return process.stdout.isTTY}function v(c){return u()?`\x1B[2m${c}\x1B[0m`:c}var p=/(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg;function o(c){let j={},R=c.toString();R=R.replace(/\r\n?/mg,`
3
+ `);let f;while((f=p.exec(R))!=null){let O=f[1],b=f[2]||"";b=b.trim();let g=b[0];if(b=b.replace(/^(['"`])([\s\S]*)\1$/mg,"$2"),g==='"')b=b.replace(/\\n/g,`
4
+ `),b=b.replace(/\\r/g,"\r");j[O]=b}return j}function cc(c){c=c||{};let j=K(c);c.path=j;let R=T.configDotenv(c);if(!R.parsed){let g=Error(`MISSING_DATA: Cannot parse ${j} for an unknown reason`);throw g.code="MISSING_DATA",g}let f=B(c).split(","),O=f.length,b;for(let g=0;g<O;g++)try{let w=f[g].trim(),S=jc(R,w);b=T.decrypt(S.ciphertext,S.key);break}catch(w){if(g+1>=O)throw w}return T.parse(b)}function gc(c){console.error(`[dotenv@${z}][WARN] ${c}`)}function $(c){console.log(`[dotenv@${z}][DEBUG] ${c}`)}function a(c){console.log(`[dotenv@${z}] ${c}`)}function B(c){if(c&&c.DOTENV_KEY&&c.DOTENV_KEY.length>0)return c.DOTENV_KEY;if(process.env.DOTENV_KEY&&process.env.DOTENV_KEY.length>0)return process.env.DOTENV_KEY;return""}function jc(c,j){let R;try{R=new URL(j)}catch(w){if(w.code==="ERR_INVALID_URL"){let S=Error("INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenvx.com/vault/.env.vault?environment=development");throw S.code="INVALID_DOTENV_KEY",S}throw w}let f=R.password;if(!f){let w=Error("INVALID_DOTENV_KEY: Missing key part");throw w.code="INVALID_DOTENV_KEY",w}let O=R.searchParams.get("environment");if(!O){let w=Error("INVALID_DOTENV_KEY: Missing environment part");throw w.code="INVALID_DOTENV_KEY",w}let b=`DOTENV_VAULT_${O.toUpperCase()}`,g=c.parsed[b];if(!g){let w=Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${b} in your .env.vault file.`);throw w.code="NOT_FOUND_DOTENV_ENVIRONMENT",w}return{ciphertext:g,key:f}}function K(c){let j=null;if(c&&c.path&&c.path.length>0)if(Array.isArray(c.path)){for(let R of c.path)if(y.existsSync(R))j=R.endsWith(".vault")?R:`${R}.vault`}else j=c.path.endsWith(".vault")?c.path:`${c.path}.vault`;else j=Y.resolve(process.cwd(),".env.vault");if(y.existsSync(j))return j;return null}function Q(c){return c[0]==="~"?Y.join(s.homedir(),c.slice(1)):c}function Rc(c){let j=x(process.env.DOTENV_CONFIG_DEBUG||c&&c.debug),R=x(process.env.DOTENV_CONFIG_QUIET||c&&c.quiet);if(j||!R)a("Loading env from encrypted .env.vault");let f=T._parseVault(c),O=process.env;if(c&&c.processEnv!=null)O=c.processEnv;return T.populate(O,f,c),{parsed:f}}function fc(c){let j=Y.resolve(process.cwd(),".env"),R="utf8",f=process.env;if(c&&c.processEnv!=null)f=c.processEnv;let O=x(f.DOTENV_CONFIG_DEBUG||c&&c.debug),b=x(f.DOTENV_CONFIG_QUIET||c&&c.quiet);if(c&&c.encoding)R=c.encoding;else if(O)$("No encoding is specified. UTF-8 is used by default");let g=[j];if(c&&c.path)if(!Array.isArray(c.path))g=[Q(c.path)];else{g=[];for(let m of c.path)g.push(Q(m))}let w,S={};for(let m of g)try{let C=T.parse(y.readFileSync(m,{encoding:R}));T.populate(S,C,c)}catch(C){if(O)$(`Failed to load ${m} ${C.message}`);w=C}let I=T.populate(f,S,c);if(O=x(f.DOTENV_CONFIG_DEBUG||O),b=x(f.DOTENV_CONFIG_QUIET||b),O||!b){let m=Object.keys(I).length,C=[];for(let X of g)try{let J=Y.relative(process.cwd(),X);C.push(J)}catch(J){if(O)$(`Failed to load ${X} ${J.message}`);w=J}a(`injecting env (${m}) from ${C.join(",")} ${v(`-- tip: ${e()}`)}`)}if(w)return{parsed:S,error:w};else return{parsed:S}}function wc(c){if(B(c).length===0)return T.configDotenv(c);let j=K(c);if(!j)return gc(`You set DOTENV_KEY but you are missing a .env.vault file at ${j}. Did you forget to build it?`),T.configDotenv(c);return T._configVault(c)}function Oc(c,j){let R=Buffer.from(j.slice(-64),"hex"),f=Buffer.from(c,"base64"),O=f.subarray(0,12),b=f.subarray(-16);f=f.subarray(12,-16);try{let g=l.createDecipheriv("aes-256-gcm",R,O);return g.setAuthTag(b),`${g.update(f)}${g.final()}`}catch(g){let w=g instanceof RangeError,S=g.message==="Invalid key length",I=g.message==="Unsupported state or unable to authenticate data";if(w||S){let m=Error("INVALID_DOTENV_KEY: It must be 64 characters long (or more)");throw m.code="INVALID_DOTENV_KEY",m}else if(I){let m=Error("DECRYPTION_FAILED: Please check your DOTENV_KEY");throw m.code="DECRYPTION_FAILED",m}else throw g}}function bc(c,j,R={}){let f=Boolean(R&&R.debug),O=Boolean(R&&R.override),b={};if(typeof j!=="object"){let g=Error("OBJECT_REQUIRED: Please check the processEnv argument being passed to populate");throw g.code="OBJECT_REQUIRED",g}for(let g of Object.keys(j))if(Object.prototype.hasOwnProperty.call(c,g)){if(O===!0)c[g]=j[g],b[g]=j[g];if(f)if(O===!0)$(`"${g}" is already defined and WAS overwritten`);else $(`"${g}" is already defined and was NOT overwritten`)}else c[g]=j[g],b[g]=j[g];return b}var T={configDotenv:fc,_configVault:Rc,_parseVault:cc,config:wc,decrypt:Oc,parse:o,populate:bc};Fc.configDotenv=T.configDotenv;Fc._configVault=T._configVault;Fc._parseVault=T._parseVault;Fc.config=T.config;Fc.decrypt=T.decrypt;Fc.parse=T.parse;Fc.populate=T.populate;A.exports=T});var P=Z(G(),1);import{platform as Jc}from"os";import{join as Uc}from"path";var n=Z(G(),1);n.default.config();var E={openai:{name:"openai",apiKey:process.env.OPENAI_API_KEY||"",model:process.env.OPENAI_MODEL||"gpt-3.5-turbo",url:"https://api.openai.com/v1/chat/completions"},openrouter:{name:"openrouter",apiKey:process.env.OPENROUTER_API_KEY||"",model:process.env.OPENROUTER_MODEL||"google/gemini-2.5-flash-lite",url:"https://openrouter.ai/api/v1/chat/completions"}},i=["openrouter","openai"];var L={system:`You are an expert app development assistant.
5
+ Your task is to interview users and get the basic idea about the app
6
+ Intension is to gather AGENTS.md and TODOS.md required to build the app.
7
+ Ask relevant questions to understand the app idea clearly.
8
+ Once you have enough information, generate AGENTS.md and TODOS.md files.
9
+ Provide the response in following JSON format:
10
+ {
11
+ "agents": string,
12
+ "todos": string
13
+ }
14
+
15
+ `};import Ic from"node:readline/promises";async function V(c,j){let R={"Content-Type":"application/json",Authorization:`Bearer ${c.apiKey}`};if(c.name==="openrouter")R["HTTP-Referer"]=process.env.OPENROUTER_REFERER||"http://localhost",R["X-Title"]=process.env.OPENROUTER_APP_NAME||"rolax";let f=await fetch(c.url,{method:"POST",headers:R,body:JSON.stringify({model:c.model,messages:j,temperature:0.5})});if(!f.ok){let b=await f.text();throw Error(`LLM request failed (${f.status}): ${b}`)}return(await f.json())?.choices?.[0]?.message?.content?.trim()||""}async function k(c,j){console.log("* Explain your app idea as clearly as possible."),console.log("* When done type exit/quit/bye to end the chat!"),console.log(`Message: ${j}`);let R=[{role:"system",content:L.system},{role:"user",content:j}],f=Ic.createInterface({input:process.stdin,output:process.stdout});try{while(!0){let w=(await f.question("> ")).trim();if(!w)continue;if(/^(exit|quit|bye)$/i.test(w)){console.log("Exiting chat...");break}R.push({role:"user",content:w});let S=await V(c,R);if(S)console.log(S),R.push({role:"assistant",content:S})}}finally{f.close()}console.log("Generating final JSON summary...");let O={role:"user",content:"Return JSON only with keys 'agents' and 'todos'. Use concise markdown-ready text for each value."},g=(await V(c,[...R,O])).trim();console.log("Final response before parsing:",g);try{let w=g.indexOf("{"),S=g.lastIndexOf("}")+1;if(w>=0&&S>w)g=g.substring(w,S);return JSON.parse(g)}catch{return g}}var Yc=Jc();var yc=Uc(process.cwd(),".env"),W=["OPENAI_API_KEY","OPENROUTER_API_KEY"],zc=W.filter((c)=>!process.env[c]);P.default.config({path:yc});console.log("Rolax: Checking required environment variables...");if(zc.length===W.length)console.error(`Error: At least one API key should be set: ${W.join(", ")}`),process.exit(1);console.log(`Detected platform: ${Yc}`);console.log("Environment variables loaded.");console.log("At least one API key is set.");async function Gc(){console.log("Starting Rolax...");let c=i[0],j=Object.values(E).find((R)=>R.name===c&&R.apiKey);if(!j)console.error("Error: No preferred LLM is configured with an API key."),process.exit(1);console.log("Preferred LLM:",j),await Wc(j)}async function Wc(c){let j=await k(c,"Hello, I would like to discuss my app idea.");if(j)console.log("Final JSON:",j),console.log("Agents.md-> ",j.agents)}Gc().catch((c)=>{console.error("Rolax failed:",c),process.exit(1)});
package/package.json ADDED
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "rolax",
3
+ "module": "src/index.ts",
4
+ "type": "module",
5
+ "license": "MIT",
6
+ "version": "0.0.1",
7
+ "bin": {
8
+ "rolax": "dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "bun build src/index.ts --outdir dist --minify --target node",
12
+ "dev": "bun --watch src/index.ts",
13
+ "test": "bun test",
14
+ "pub": "bun run build && npm publish"
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "latest"
18
+ },
19
+ "peerDependencies": {
20
+ "typescript": "^5"
21
+ },
22
+ "dependencies": {
23
+ "dotenv": "^17.2.3"
24
+ }
25
+ }
package/src/config.ts ADDED
@@ -0,0 +1,24 @@
1
+ import dotenv from "dotenv"
2
+
3
+ dotenv.config()
4
+
5
+ export const CONFIG_LLM = {
6
+ openai: {
7
+ name: "openai",
8
+ apiKey: process.env.OPENAI_API_KEY || "",
9
+ model: process.env.OPENAI_MODEL || "gpt-3.5-turbo",
10
+ url: "https://api.openai.com/v1/chat/completions",
11
+ },
12
+ openrouter: {
13
+ name: "openrouter",
14
+ apiKey: process.env.OPENROUTER_API_KEY || "",
15
+ model: process.env.OPENROUTER_MODEL || "google/gemini-2.5-flash-lite",
16
+ // model: process.env.OPENROUTER_MODEL || "google/gemini-3-flash-preview",
17
+ url: "https://openrouter.ai/api/v1/chat/completions",
18
+ },
19
+ }
20
+
21
+ export const CONFIG_LLM_ORDER = [
22
+ "openrouter",
23
+ "openai"
24
+ ]
package/src/index.ts ADDED
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { platform } from "os"
4
+ import { existsSync } from "fs"
5
+ import { exec, execSync } from "child_process"
6
+ import { join } from "path"
7
+ import dotenv from "dotenv"
8
+ import {CONFIG_LLM, CONFIG_LLM_ORDER} from "./config"
9
+ import { chatWithLLM } from "./llm"
10
+
11
+ const platformName = platform()
12
+ const isWindows =platformName === "win32"
13
+ const isMac = platformName === "darwin"
14
+ const isLinux = platformName === "linux"
15
+ const envFilePath = join(process.cwd(), ".env")
16
+ const requiredEnvVars = ["OPENAI_API_KEY", "OPENROUTER_API_KEY"]
17
+ const missingEnvVars = requiredEnvVars.filter((varName) => !process.env[varName])
18
+
19
+ dotenv.config({ path: envFilePath })
20
+ console.log("Rolax: Checking required environment variables...")
21
+
22
+ if (missingEnvVars.length === requiredEnvVars.length) {
23
+ console.error(`Error: At least one API key should be set: ${requiredEnvVars.join(", ")}`)
24
+ process.exit(1)
25
+ }
26
+
27
+ console.log(`Detected platform: ${platformName}`)
28
+ console.log("Environment variables loaded.")
29
+ console.log("At least one API key is set.")
30
+
31
+
32
+ async function start() {
33
+ console.log("Starting Rolax...")
34
+ let preferredName = CONFIG_LLM_ORDER[0]
35
+ let preferredLLM = Object.values(CONFIG_LLM).find((llm) => llm.name === preferredName && llm.apiKey)
36
+
37
+ if (!preferredLLM) {
38
+ console.error("Error: No preferred LLM is configured with an API key.")
39
+ process.exit(1)
40
+ }
41
+
42
+ console.log("Preferred LLM:", preferredLLM)
43
+
44
+ // 1. Chat with user using the preferred LLM
45
+ await startChat(preferredLLM)
46
+ }
47
+
48
+ async function startChat(llm: { name: string; apiKey: string; model: string, url: string }) {
49
+ let summary = await chatWithLLM(llm, "Hello, I would like to discuss my app idea.")
50
+ if (summary) {
51
+ console.log("Final JSON:", summary)
52
+ console.log("Agents.md-> ", summary.agents)
53
+ }
54
+ }
55
+
56
+ start().catch((error) => {
57
+ console.error("Rolax failed:", error)
58
+ process.exit(1)
59
+ })
package/src/llm.ts ADDED
@@ -0,0 +1,111 @@
1
+
2
+ import { APP_BUILDER_PROMPTS } from "./prompts"
3
+ import readline from "node:readline/promises"
4
+
5
+ interface IAppIdeaResponse {
6
+ agents: string
7
+ todos: string
8
+ }
9
+
10
+ type ChatMessage = { role: "system" | "user" | "assistant"; content: string }
11
+
12
+ async function callChatCompletion(llm: { name: string; apiKey: string; model: string, url: string }, messages: ChatMessage[]): Promise<string> {
13
+ let headers: Record<string, string> = {
14
+ "Content-Type": "application/json",
15
+ Authorization: `Bearer ${llm.apiKey}`,
16
+ }
17
+
18
+ if (llm.name === "openrouter") {
19
+ headers["HTTP-Referer"] = process.env.OPENROUTER_REFERER || "http://localhost"
20
+ headers["X-Title"] = process.env.OPENROUTER_APP_NAME || "rolax"
21
+ }
22
+
23
+ let response = await fetch(llm.url, {
24
+ method: "POST",
25
+ headers,
26
+ body: JSON.stringify({
27
+ model: llm.model,
28
+ messages,
29
+ temperature: 0.5,
30
+ }),
31
+ })
32
+
33
+ if (!response.ok) {
34
+ const errorText = await response.text()
35
+ throw new Error(`LLM request failed (${response.status}): ${errorText}`)
36
+ }
37
+
38
+ type ChatCompletionResponse = {
39
+ choices?: Array<{ message?: { content?: string } }>;
40
+ }
41
+ const data = await response.json() as ChatCompletionResponse
42
+ return data?.choices?.[0]?.message?.content?.trim() || ""
43
+ }
44
+
45
+ export async function chatWithLLM(llm: { name: string; apiKey: string; model: string, url: string }, message: string): Promise<any> {
46
+ console.log("* Explain your app idea as clearly as possible.")
47
+ console.log("* When done type exit/quit/bye to end the chat!")
48
+ console.log(`Message: ${message}`)
49
+
50
+ const messages: ChatMessage[] = [
51
+ { role: "system", content: APP_BUILDER_PROMPTS.system },
52
+ { role: "user", content: message },
53
+ ]
54
+
55
+ const rl = readline.createInterface({
56
+ input: process.stdin,
57
+ output: process.stdout,
58
+ })
59
+
60
+ try {
61
+ while (true) {
62
+ const input = (await rl.question("> ")).trim()
63
+ if (!input) {
64
+ continue
65
+ }
66
+
67
+ if (/^(exit|quit|bye)$/i.test(input)) {
68
+ console.log("Exiting chat...")
69
+ break
70
+ }
71
+
72
+ messages.push({ role: "user", content: input })
73
+ let reply = await callChatCompletion(llm, messages)
74
+ if (reply) {
75
+ console.log(reply)
76
+ messages.push({ role: "assistant", content: reply })
77
+ }
78
+ }
79
+ }
80
+ finally {
81
+ rl.close()
82
+ }
83
+
84
+ console.log("Generating final JSON summary...")
85
+ let summaryPrompt: ChatMessage = {
86
+ role: "user",
87
+ content:
88
+ "Return JSON only with keys 'agents' and 'todos'. " +
89
+ "Use concise markdown-ready text for each value.",
90
+ }
91
+
92
+ let finalResponse = await callChatCompletion(llm, [...messages, summaryPrompt])
93
+ let trimmed = finalResponse.trim()
94
+
95
+ console.log("Final response before parsing:", trimmed)
96
+
97
+ try {
98
+ let jsonStart = trimmed.indexOf("{")
99
+ let jsonEnd = trimmed.lastIndexOf("}") + 1
100
+
101
+ if (jsonStart >= 0 && jsonEnd > jsonStart) {
102
+ trimmed = trimmed.substring(jsonStart, jsonEnd)
103
+ }
104
+
105
+ let parsed = JSON.parse(trimmed) as IAppIdeaResponse
106
+ return parsed
107
+ }
108
+ catch {
109
+ return trimmed
110
+ }
111
+ }
package/src/prompts.ts ADDED
@@ -0,0 +1,16 @@
1
+ // App builder prompts
2
+
3
+ export const APP_BUILDER_PROMPTS = {
4
+ system: `You are an expert app development assistant.
5
+ Your task is to interview users and get the basic idea about the app
6
+ Intension is to gather AGENTS.md and TODOS.md required to build the app.
7
+ Ask relevant questions to understand the app idea clearly.
8
+ Once you have enough information, generate AGENTS.md and TODOS.md files.
9
+ Provide the response in following JSON format:
10
+ {
11
+ "agents": string,
12
+ "todos": string
13
+ }
14
+
15
+ `
16
+ }
@@ -0,0 +1 @@
1
+ # Will be populated!
@@ -0,0 +1 @@
1
+ # Will be populated!
@@ -0,0 +1 @@
1
+ console.log("Hello, World!")
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+
11
+ // Bundler mode
12
+ "moduleResolution": "bundler",
13
+ "allowImportingTsExtensions": true,
14
+ "verbatimModuleSyntax": true,
15
+ "noEmit": true,
16
+
17
+ // Best practices
18
+ "strict": true,
19
+ "skipLibCheck": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "noImplicitOverride": true,
23
+
24
+ // Some stricter flags (disabled by default)
25
+ "noUnusedLocals": false,
26
+ "noUnusedParameters": false,
27
+ "noPropertyAccessFromIndexSignature": false
28
+ }
29
+ }