diff --git a/index.ts b/index.ts
index 3d5ff9190db85f8c6cff82d37ce3b2d7967ba5e8..72ee5cd8ba00eef6c6e61c3b326743f47df0d5c3 100644
--- a/index.ts
+++ b/index.ts
@@ -1,20 +1,18 @@
 #!/usr/bin/env node
 /* eslint-disable import/no-extraneous-dependencies */
-import ciInfo from "ci-info";
 import Commander from "commander";
 import Conf from "conf";
 import fs from "fs";
 import path from "path";
-import { blue, bold, cyan, green, red, yellow } from "picocolors";
+import { bold, cyan, green, red, yellow } from "picocolors";
 import prompts from "prompts";
 import checkForUpdate from "update-check";
-import { InstallAppArgs, createApp } from "./create-app";
-import { COMMUNITY_OWNER, COMMUNITY_REPO } from "./helpers/constant";
+import { createApp } from "./create-app";
 import { getPkgManager } from "./helpers/get-pkg-manager";
 import { isFolderEmpty } from "./helpers/is-folder-empty";
-import { getRepoRootFolders } from "./helpers/repo";
 import { validateNpmName } from "./helpers/validate-pkg";
 import packageJson from "./package.json";
+import { QuestionArgs, askQuestions, onPromptState } from "./questions";
 
 let projectPath: string = "";
 
@@ -23,16 +21,6 @@ const handleSigTerm = () => process.exit(0);
 process.on("SIGINT", handleSigTerm);
 process.on("SIGTERM", handleSigTerm);
 
-const onPromptState = (state: any) => {
-  if (state.aborted) {
-    // If we don't re-enable the terminal cursor before exiting
-    // the program, the cursor will remain hidden
-    process.stdout.write("\x1B[?25h");
-    process.stdout.write("\n");
-    process.exit(1);
-  }
-};
-
 const program = new Commander.Command(packageJson.name)
   .version(packageJson.version)
   .arguments("<project-directory>")
@@ -157,259 +145,8 @@ async function run(): Promise<void> {
     process.exit(1);
   }
 
-  // TODO: use Args also for program
-  type Args = Omit<InstallAppArgs, "appPath" | "packageManager">;
-
-  const preferences = (conf.get("preferences") || {}) as Args;
-
-  const defaults: Args = {
-    template: "streaming",
-    framework: "nextjs",
-    engine: "simple",
-    ui: "html",
-    eslint: true,
-    frontend: false,
-    openAIKey: "",
-    model: "gpt-3.5-turbo",
-    communityProjectPath: "",
-  };
-  const getPrefOrDefault = (field: keyof Args) =>
-    preferences[field] ?? defaults[field];
-
-  const handlers = {
-    onCancel: () => {
-      console.error("Exiting.");
-      process.exit(1);
-    },
-  };
-
-  if (!program.template) {
-    if (ciInfo.isCI) {
-      program.template = getPrefOrDefault("template");
-    } else {
-      const { template } = await prompts(
-        {
-          type: "select",
-          name: "template",
-          message: "Which template would you like to use?",
-          choices: [
-            { title: "Chat without streaming", value: "simple" },
-            { title: "Chat with streaming", value: "streaming" },
-            { title: "Community templates", value: "community" },
-          ],
-          initial: 1,
-        },
-        handlers,
-      );
-      program.template = template;
-      preferences.template = template;
-    }
-  }
-
-  if (program.template === "community") {
-    const rootFolderNames = await getRepoRootFolders(
-      COMMUNITY_OWNER,
-      COMMUNITY_REPO,
-    );
-    const { communityProjectPath } = await prompts(
-      {
-        type: "select",
-        name: "communityProjectPath",
-        message: "Select community templates?",
-        choices: rootFolderNames.map((name) => ({
-          title: name,
-          value: name,
-        })),
-        initial: 0,
-      },
-      {
-        onCancel: () => {
-          console.error("Exiting.");
-          process.exit(1);
-        },
-      },
-    );
-
-    program.communityProjectPath = communityProjectPath;
-    preferences.communityProjectPath = communityProjectPath;
-  } else {
-    if (!program.framework) {
-      if (ciInfo.isCI) {
-        program.framework = getPrefOrDefault("framework");
-      } else {
-        const allChoices = [
-          { title: "NextJS", value: "nextjs" },
-          { title: "Express", value: "express" },
-          { title: "FastAPI (Python)", value: "fastapi" },
-        ];
-        const choiceIndexes =
-          program.template === "simple" ? [1, 2] : [0, 1, 2];
-        const choices = allChoices.filter((_, i) => choiceIndexes.includes(i));
-
-        const { framework } = await prompts(
-          {
-            type: "select",
-            name: "framework",
-            message: "Which framework would you like to use?",
-            choices,
-            initial: 0,
-          },
-          handlers,
-        );
-        program.framework = framework;
-        preferences.framework = framework;
-      }
-    }
-
-    if (program.framework === "nextjs") {
-      program.template = "streaming";
-    }
-
-    if (program.framework === "express" || program.framework === "fastapi") {
-      // if a backend-only framework is selected, ask whether we should create a frontend
-      if (!program.frontend) {
-        if (ciInfo.isCI) {
-          program.frontend = getPrefOrDefault("frontend");
-        } else {
-          const styledNextJS = blue("NextJS");
-          const styledBackend = green(
-            program.framework === "express"
-              ? "Express "
-              : program.framework === "fastapi"
-                ? "FastAPI (Python) "
-                : "",
-          );
-          const { frontend } = await prompts({
-            onState: onPromptState,
-            type: "toggle",
-            name: "frontend",
-            message: `Would you like to generate a ${styledNextJS} frontend for your ${styledBackend}backend?`,
-            initial: getPrefOrDefault("frontend"),
-            active: "Yes",
-            inactive: "No",
-          });
-          program.frontend = Boolean(frontend);
-          preferences.frontend = Boolean(frontend);
-        }
-      }
-    }
-
-    if (program.framework === "nextjs" || program.frontend) {
-      if (!program.ui) {
-        if (ciInfo.isCI) {
-          program.ui = getPrefOrDefault("ui");
-        } else {
-          const { ui } = await prompts(
-            {
-              type: "select",
-              name: "ui",
-              message: "Which UI would you like to use?",
-              choices: [
-                { title: "Just HTML", value: "html" },
-                { title: "Shadcn", value: "shadcn" },
-              ],
-              initial: 0,
-            },
-            handlers,
-          );
-          program.ui = ui;
-          preferences.ui = ui;
-        }
-      }
-    }
-
-    if (program.framework === "nextjs") {
-      if (!program.model) {
-        if (ciInfo.isCI) {
-          program.model = getPrefOrDefault("model");
-        } else {
-          const { model } = await prompts(
-            {
-              type: "select",
-              name: "model",
-              message: "Which model would you like to use?",
-              choices: [
-                { title: "gpt-3.5-turbo", value: "gpt-3.5-turbo" },
-                { title: "gpt-4", value: "gpt-4" },
-                { title: "gpt-4-1106-preview", value: "gpt-4-1106-preview" },
-                {
-                  title: "gpt-4-vision-preview",
-                  value: "gpt-4-vision-preview",
-                },
-              ],
-              initial: 0,
-            },
-            handlers,
-          );
-          program.model = model;
-          preferences.model = model;
-        }
-      }
-    }
-
-    if (program.framework === "express" || program.framework === "nextjs") {
-      if (!program.engine) {
-        if (ciInfo.isCI) {
-          program.engine = getPrefOrDefault("engine");
-        } else {
-          const { engine } = await prompts(
-            {
-              type: "select",
-              name: "engine",
-              message: "Which chat engine would you like to use?",
-              choices: [
-                { title: "ContextChatEngine", value: "context" },
-                {
-                  title: "SimpleChatEngine (no data, just chat)",
-                  value: "simple",
-                },
-              ],
-              initial: 0,
-            },
-            handlers,
-          );
-          program.engine = engine;
-          preferences.engine = engine;
-        }
-      }
-    }
-
-    if (!program.openAIKey) {
-      const { key } = await prompts(
-        {
-          type: "text",
-          name: "key",
-          message: "Please provide your OpenAI API key (leave blank to skip):",
-        },
-        handlers,
-      );
-      program.openAIKey = key;
-      preferences.openAIKey = key;
-    }
-
-    if (
-      program.framework !== "fastapi" &&
-      !process.argv.includes("--eslint") &&
-      !process.argv.includes("--no-eslint")
-    ) {
-      if (ciInfo.isCI) {
-        program.eslint = getPrefOrDefault("eslint");
-      } else {
-        const styledEslint = blue("ESLint");
-        const { eslint } = await prompts({
-          onState: onPromptState,
-          type: "toggle",
-          name: "eslint",
-          message: `Would you like to use ${styledEslint}?`,
-          initial: getPrefOrDefault("eslint"),
-          active: "Yes",
-          inactive: "No",
-        });
-        program.eslint = Boolean(eslint);
-        preferences.eslint = Boolean(eslint);
-      }
-    }
-  }
+  const preferences = (conf.get("preferences") || {}) as QuestionArgs;
+  await askQuestions(program as unknown as QuestionArgs, preferences);
 
   await createApp({
     template: program.template,
diff --git a/questions.ts b/questions.ts
new file mode 100644
index 0000000000000000000000000000000000000000..7f0786acb0197988b8423025c09fe796c98f6085
--- /dev/null
+++ b/questions.ts
@@ -0,0 +1,277 @@
+import ciInfo from "ci-info";
+import { blue, green } from "picocolors";
+import prompts from "prompts";
+import { InstallAppArgs } from "./create-app";
+import { COMMUNITY_OWNER, COMMUNITY_REPO } from "./helpers/constant";
+import { getRepoRootFolders } from "./helpers/repo";
+
+export type QuestionArgs = Omit<InstallAppArgs, "appPath" | "packageManager">;
+
+const defaults: QuestionArgs = {
+  template: "streaming",
+  framework: "nextjs",
+  engine: "simple",
+  ui: "html",
+  eslint: true,
+  frontend: false,
+  openAIKey: "",
+  model: "gpt-3.5-turbo",
+  communityProjectPath: "",
+};
+
+const handlers = {
+  onCancel: () => {
+    console.error("Exiting.");
+    process.exit(1);
+  },
+};
+
+export const onPromptState = (state: any) => {
+  if (state.aborted) {
+    // If we don't re-enable the terminal cursor before exiting
+    // the program, the cursor will remain hidden
+    process.stdout.write("\x1B[?25h");
+    process.stdout.write("\n");
+    process.exit(1);
+  }
+};
+
+export const askQuestions = async (
+  program: QuestionArgs,
+  preferences: QuestionArgs,
+) => {
+  const getPrefOrDefault = <K extends keyof QuestionArgs>(
+    field: K,
+  ): QuestionArgs[K] => preferences[field] ?? defaults[field];
+
+  if (!program.template) {
+    if (ciInfo.isCI) {
+      program.template = getPrefOrDefault("template");
+    } else {
+      const styledRepo = blue(
+        `https://github.com/${COMMUNITY_OWNER}/${COMMUNITY_REPO}`,
+      );
+      const { template } = await prompts(
+        {
+          type: "select",
+          name: "template",
+          message: "Which template would you like to use?",
+          choices: [
+            { title: "Chat without streaming", value: "simple" },
+            { title: "Chat with streaming", value: "streaming" },
+            {
+              title: `Community template from ${styledRepo}`,
+              value: "community",
+            },
+          ],
+          initial: 1,
+        },
+        handlers,
+      );
+      program.template = template;
+      preferences.template = template;
+    }
+  }
+
+  if (program.template === "community") {
+    const rootFolderNames = await getRepoRootFolders(
+      COMMUNITY_OWNER,
+      COMMUNITY_REPO,
+    );
+    const { communityProjectPath } = await prompts(
+      {
+        type: "select",
+        name: "communityProjectPath",
+        message: "Select community template",
+        choices: rootFolderNames.map((name) => ({
+          title: name,
+          value: name,
+        })),
+        initial: 0,
+      },
+      {
+        onCancel: () => {
+          console.error("Exiting.");
+          process.exit(1);
+        },
+      },
+    );
+
+    program.communityProjectPath = communityProjectPath;
+    preferences.communityProjectPath = communityProjectPath;
+    return; // early return - no further questions needed for community projects
+  }
+
+  if (!program.framework) {
+    if (ciInfo.isCI) {
+      program.framework = getPrefOrDefault("framework");
+    } else {
+      const choices = [
+        { title: "Express", value: "express" },
+        { title: "FastAPI (Python)", value: "fastapi" },
+      ];
+      if (program.template === "streaming") {
+        // allow NextJS only for streaming template
+        choices.unshift({ title: "NextJS", value: "nextjs" });
+      }
+
+      const { framework } = await prompts(
+        {
+          type: "select",
+          name: "framework",
+          message: "Which framework would you like to use?",
+          choices,
+          initial: 0,
+        },
+        handlers,
+      );
+      program.framework = framework;
+      preferences.framework = framework;
+    }
+  }
+
+  if (program.framework === "express" || program.framework === "fastapi") {
+    // if a backend-only framework is selected, ask whether we should create a frontend
+    if (!program.frontend) {
+      if (ciInfo.isCI) {
+        program.frontend = getPrefOrDefault("frontend");
+      } else {
+        const styledNextJS = blue("NextJS");
+        const styledBackend = green(
+          program.framework === "express"
+            ? "Express "
+            : program.framework === "fastapi"
+              ? "FastAPI (Python) "
+              : "",
+        );
+        const { frontend } = await prompts({
+          onState: onPromptState,
+          type: "toggle",
+          name: "frontend",
+          message: `Would you like to generate a ${styledNextJS} frontend for your ${styledBackend}backend?`,
+          initial: getPrefOrDefault("frontend"),
+          active: "Yes",
+          inactive: "No",
+        });
+        program.frontend = Boolean(frontend);
+        preferences.frontend = Boolean(frontend);
+      }
+    }
+  }
+
+  if (program.framework === "nextjs" || program.frontend) {
+    if (!program.ui) {
+      if (ciInfo.isCI) {
+        program.ui = getPrefOrDefault("ui");
+      } else {
+        const { ui } = await prompts(
+          {
+            type: "select",
+            name: "ui",
+            message: "Which UI would you like to use?",
+            choices: [
+              { title: "Just HTML", value: "html" },
+              { title: "Shadcn", value: "shadcn" },
+            ],
+            initial: 0,
+          },
+          handlers,
+        );
+        program.ui = ui;
+        preferences.ui = ui;
+      }
+    }
+  }
+
+  if (program.framework === "nextjs") {
+    if (!program.model) {
+      if (ciInfo.isCI) {
+        program.model = getPrefOrDefault("model");
+      } else {
+        const { model } = await prompts(
+          {
+            type: "select",
+            name: "model",
+            message: "Which model would you like to use?",
+            choices: [
+              { title: "gpt-3.5-turbo", value: "gpt-3.5-turbo" },
+              { title: "gpt-4", value: "gpt-4" },
+              { title: "gpt-4-1106-preview", value: "gpt-4-1106-preview" },
+              {
+                title: "gpt-4-vision-preview",
+                value: "gpt-4-vision-preview",
+              },
+            ],
+            initial: 0,
+          },
+          handlers,
+        );
+        program.model = model;
+        preferences.model = model;
+      }
+    }
+  }
+
+  if (program.framework === "express" || program.framework === "nextjs") {
+    if (!program.engine) {
+      if (ciInfo.isCI) {
+        program.engine = getPrefOrDefault("engine");
+      } else {
+        const { engine } = await prompts(
+          {
+            type: "select",
+            name: "engine",
+            message: "Which chat engine would you like to use?",
+            choices: [
+              { title: "ContextChatEngine", value: "context" },
+              {
+                title: "SimpleChatEngine (no data, just chat)",
+                value: "simple",
+              },
+            ],
+            initial: 0,
+          },
+          handlers,
+        );
+        program.engine = engine;
+        preferences.engine = engine;
+      }
+    }
+  }
+
+  if (!program.openAIKey) {
+    const { key } = await prompts(
+      {
+        type: "text",
+        name: "key",
+        message: "Please provide your OpenAI API key (leave blank to skip):",
+      },
+      handlers,
+    );
+    program.openAIKey = key;
+    preferences.openAIKey = key;
+  }
+
+  if (
+    program.framework !== "fastapi" &&
+    !process.argv.includes("--eslint") &&
+    !process.argv.includes("--no-eslint")
+  ) {
+    if (ciInfo.isCI) {
+      program.eslint = getPrefOrDefault("eslint");
+    } else {
+      const styledEslint = blue("ESLint");
+      const { eslint } = await prompts({
+        onState: onPromptState,
+        type: "toggle",
+        name: "eslint",
+        message: `Would you like to use ${styledEslint}?`,
+        initial: getPrefOrDefault("eslint"),
+        active: "Yes",
+        inactive: "No",
+      });
+      program.eslint = Boolean(eslint);
+      preferences.eslint = Boolean(eslint);
+    }
+  }
+};