diff --git a/packages/create-llama/README.md b/packages/create-llama/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..5cb61026618ef3b4732d1b00ee9d651fd517f460
--- /dev/null
+++ b/packages/create-llama/README.md
@@ -0,0 +1,54 @@
+# Create LlamaIndex App
+
+The easiest way to get started with LlamaIndex is by using `create-llama`. This CLI tool enables you to quickly start building a new LlamaIndex application, with everything set up for you. 
+To get started, use the following command:
+
+### Interactive
+
+You can create a new project interactively by running:
+
+```bash
+npx create-llama@latest
+# or
+npm create llama
+# or
+yarn create llama
+# or
+pnpm create llama
+```
+
+You will be asked for the name of your project, and then which framework you want to use
+create a TypeScript project:
+
+```bash
+✔ Which framework would you like to use? › NextJS
+```
+
+You can choose between NextJS and Express.
+
+### Non-interactive
+
+You can also pass command line arguments to set up a new project
+non-interactively. See `create-llama --help`:
+
+```bash
+create-llama <project-directory> [options]
+
+Options:
+  -V, --version                      output the version number
+ 
+
+  --use-npm
+
+    Explicitly tell the CLI to bootstrap the app using npm
+
+  --use-pnpm
+
+    Explicitly tell the CLI to bootstrap the app using pnpm
+
+  --use-yarn
+
+    Explicitly tell the CLI to bootstrap the app using Yarn
+
+```
+
diff --git a/packages/create-llama/create-app.ts b/packages/create-llama/create-app.ts
new file mode 100644
index 0000000000000000000000000000000000000000..70d7b40dc437cae91c7ee152719abd6e0075cae4
--- /dev/null
+++ b/packages/create-llama/create-app.ts
@@ -0,0 +1,109 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import path from "path";
+import { green } from "picocolors";
+import { tryGitInit } from "./helpers/git";
+import { isFolderEmpty } from "./helpers/is-folder-empty";
+import { getOnline } from "./helpers/is-online";
+import { isWriteable } from "./helpers/is-writeable";
+import { makeDir } from "./helpers/make-dir";
+
+import fs from "fs";
+import terminalLink from "terminal-link";
+import type { InstallTemplateArgs } from "./templates";
+import { installTemplate } from "./templates";
+
+export async function createApp({
+  template,
+  framework,
+  engine,
+  ui,
+  appPath,
+  packageManager,
+  eslint,
+  frontend,
+  openAIKey,
+}: Omit<
+  InstallTemplateArgs,
+  "appName" | "root" | "isOnline" | "customApiPath"
+> & {
+  appPath: string;
+  frontend: boolean;
+}): Promise<void> {
+  const root = path.resolve(appPath);
+
+  if (!(await isWriteable(path.dirname(root)))) {
+    console.error(
+      "The application path is not writable, please check folder permissions and try again.",
+    );
+    console.error(
+      "It is likely you do not have write permissions for this folder.",
+    );
+    process.exit(1);
+  }
+
+  const appName = path.basename(root);
+
+  await makeDir(root);
+  if (!isFolderEmpty(root, appName)) {
+    process.exit(1);
+  }
+
+  const useYarn = packageManager === "yarn";
+  const isOnline = !useYarn || (await getOnline());
+
+  console.log(`Creating a new LlamaIndex app in ${green(root)}.`);
+  console.log();
+
+  const args = {
+    appName,
+    root,
+    template,
+    framework,
+    engine,
+    ui,
+    packageManager,
+    isOnline,
+    eslint,
+    openAIKey,
+  };
+
+  if (frontend) {
+    // install backend
+    const backendRoot = path.join(root, "backend");
+    await makeDir(backendRoot);
+    await installTemplate({ ...args, root: backendRoot, backend: true });
+    // install frontend
+    const frontendRoot = path.join(root, "frontend");
+    await makeDir(frontendRoot);
+    await installTemplate({
+      ...args,
+      root: frontendRoot,
+      framework: "nextjs",
+      customApiPath: "http://localhost:8000/api/chat",
+      backend: false,
+    });
+    // copy readme for fullstack
+    await fs.promises.copyFile(
+      path.join(__dirname, "templates", "README-fullstack.md"),
+      path.join(root, "README.md"),
+    );
+  } else {
+    await installTemplate({ ...args, backend: true });
+  }
+
+  process.chdir(root);
+  if (tryGitInit(root)) {
+    console.log("Initialized a git repository.");
+    console.log();
+  }
+
+  console.log(`${green("Success!")} Created ${appName} at ${appPath}`);
+
+  console.log(
+    `Now have a look at the ${terminalLink(
+      "README.md",
+      `file://${appName}/README.md`,
+    )} and learn how to get started.`,
+  );
+  console.log();
+}
diff --git a/packages/create-llama/helpers/copy.ts b/packages/create-llama/helpers/copy.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a5b722ba34fe41e867e686f10b1bdf725c58c0a0
--- /dev/null
+++ b/packages/create-llama/helpers/copy.ts
@@ -0,0 +1,50 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { async as glob } from "fast-glob";
+import fs from "fs";
+import path from "path";
+
+interface CopyOption {
+  cwd?: string;
+  rename?: (basename: string) => string;
+  parents?: boolean;
+}
+
+const identity = (x: string) => x;
+
+export const copy = async (
+  src: string | string[],
+  dest: string,
+  { cwd, rename = identity, parents = true }: CopyOption = {},
+) => {
+  const source = typeof src === "string" ? [src] : src;
+
+  if (source.length === 0 || !dest) {
+    throw new TypeError("`src` and `dest` are required");
+  }
+
+  const sourceFiles = await glob(source, {
+    cwd,
+    dot: true,
+    absolute: false,
+    stats: false,
+  });
+
+  const destRelativeToCwd = cwd ? path.resolve(cwd, dest) : dest;
+
+  return Promise.all(
+    sourceFiles.map(async (p) => {
+      const dirname = path.dirname(p);
+      const basename = rename(path.basename(p));
+
+      const from = cwd ? path.resolve(cwd, p) : p;
+      const to = parents
+        ? path.join(destRelativeToCwd, dirname, basename)
+        : path.join(destRelativeToCwd, basename);
+
+      // Ensure the destination directory exists
+      await fs.promises.mkdir(path.dirname(to), { recursive: true });
+
+      return fs.promises.copyFile(from, to);
+    }),
+  );
+};
diff --git a/packages/create-llama/helpers/get-pkg-manager.ts b/packages/create-llama/helpers/get-pkg-manager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0187c88a49e67bf7aa7fe95520b3af91777987f8
--- /dev/null
+++ b/packages/create-llama/helpers/get-pkg-manager.ts
@@ -0,0 +1,15 @@
+export type PackageManager = "npm" | "pnpm" | "yarn";
+
+export function getPkgManager(): PackageManager {
+  const userAgent = process.env.npm_config_user_agent || "";
+
+  if (userAgent.startsWith("yarn")) {
+    return "yarn";
+  }
+
+  if (userAgent.startsWith("pnpm")) {
+    return "pnpm";
+  }
+
+  return "npm";
+}
diff --git a/packages/create-llama/helpers/git.ts b/packages/create-llama/helpers/git.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fc27e609953251dff5ffb898bb7ad92039a1ca53
--- /dev/null
+++ b/packages/create-llama/helpers/git.ts
@@ -0,0 +1,58 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import { execSync } from "child_process";
+import fs from "fs";
+import path from "path";
+
+function isInGitRepository(): boolean {
+  try {
+    execSync("git rev-parse --is-inside-work-tree", { stdio: "ignore" });
+    return true;
+  } catch (_) {}
+  return false;
+}
+
+function isInMercurialRepository(): boolean {
+  try {
+    execSync("hg --cwd . root", { stdio: "ignore" });
+    return true;
+  } catch (_) {}
+  return false;
+}
+
+function isDefaultBranchSet(): boolean {
+  try {
+    execSync("git config init.defaultBranch", { stdio: "ignore" });
+    return true;
+  } catch (_) {}
+  return false;
+}
+
+export function tryGitInit(root: string): boolean {
+  let didInit = false;
+  try {
+    execSync("git --version", { stdio: "ignore" });
+    if (isInGitRepository() || isInMercurialRepository()) {
+      return false;
+    }
+
+    execSync("git init", { stdio: "ignore" });
+    didInit = true;
+
+    if (!isDefaultBranchSet()) {
+      execSync("git checkout -b main", { stdio: "ignore" });
+    }
+
+    execSync("git add -A", { stdio: "ignore" });
+    execSync('git commit -m "Initial commit from Create Next App"', {
+      stdio: "ignore",
+    });
+    return true;
+  } catch (e) {
+    if (didInit) {
+      try {
+        fs.rmSync(path.join(root, ".git"), { recursive: true, force: true });
+      } catch (_) {}
+    }
+    return false;
+  }
+}
diff --git a/packages/create-llama/helpers/install.ts b/packages/create-llama/helpers/install.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9f0f20356265081bbb062454b14d1c9059544900
--- /dev/null
+++ b/packages/create-llama/helpers/install.ts
@@ -0,0 +1,50 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import spawn from "cross-spawn";
+import { yellow } from "picocolors";
+import type { PackageManager } from "./get-pkg-manager";
+
+/**
+ * Spawn a package manager installation based on user preference.
+ *
+ * @returns A Promise that resolves once the installation is finished.
+ */
+export async function callPackageManager(
+  /** Indicate which package manager to use. */
+  packageManager: PackageManager,
+  /** Indicate whether there is an active Internet connection.*/
+  isOnline: boolean,
+  args: string[] = ["install"],
+): Promise<void> {
+  if (!isOnline) {
+    console.log(
+      yellow("You appear to be offline.\nFalling back to the local cache."),
+    );
+    args.push("--offline");
+  }
+  /**
+   * Return a Promise that resolves once the installation is finished.
+   */
+  return new Promise((resolve, reject) => {
+    /**
+     * Spawn the installation process.
+     */
+    const child = spawn(packageManager, args, {
+      stdio: "inherit",
+      env: {
+        ...process.env,
+        ADBLOCK: "1",
+        // we set NODE_ENV to development as pnpm skips dev
+        // dependencies when production
+        NODE_ENV: "development",
+        DISABLE_OPENCOLLECTIVE: "1",
+      },
+    });
+    child.on("close", (code) => {
+      if (code !== 0) {
+        reject({ command: `${packageManager} ${args.join(" ")}` });
+        return;
+      }
+      resolve();
+    });
+  });
+}
diff --git a/packages/create-llama/helpers/is-folder-empty.ts b/packages/create-llama/helpers/is-folder-empty.ts
new file mode 100644
index 0000000000000000000000000000000000000000..927a344c00fe80bc8c906349d70ffad5bb47d15e
--- /dev/null
+++ b/packages/create-llama/helpers/is-folder-empty.ts
@@ -0,0 +1,62 @@
+/* eslint-disable import/no-extraneous-dependencies */
+import fs from "fs";
+import path from "path";
+import { blue, green } from "picocolors";
+
+export function isFolderEmpty(root: string, name: string): boolean {
+  const validFiles = [
+    ".DS_Store",
+    ".git",
+    ".gitattributes",
+    ".gitignore",
+    ".gitlab-ci.yml",
+    ".hg",
+    ".hgcheck",
+    ".hgignore",
+    ".idea",
+    ".npmignore",
+    ".travis.yml",
+    "LICENSE",
+    "Thumbs.db",
+    "docs",
+    "mkdocs.yml",
+    "npm-debug.log",
+    "yarn-debug.log",
+    "yarn-error.log",
+    "yarnrc.yml",
+    ".yarn",
+  ];
+
+  const conflicts = fs
+    .readdirSync(root)
+    .filter((file) => !validFiles.includes(file))
+    // Support IntelliJ IDEA-based editors
+    .filter((file) => !/\.iml$/.test(file));
+
+  if (conflicts.length > 0) {
+    console.log(
+      `The directory ${green(name)} contains files that could conflict:`,
+    );
+    console.log();
+    for (const file of conflicts) {
+      try {
+        const stats = fs.lstatSync(path.join(root, file));
+        if (stats.isDirectory()) {
+          console.log(`  ${blue(file)}/`);
+        } else {
+          console.log(`  ${file}`);
+        }
+      } catch {
+        console.log(`  ${file}`);
+      }
+    }
+    console.log();
+    console.log(
+      "Either try using a new directory name, or remove the files listed above.",
+    );
+    console.log();
+    return false;
+  }
+
+  return true;
+}
diff --git a/packages/create-llama/helpers/is-online.ts b/packages/create-llama/helpers/is-online.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eab6980053517b8c350f537eb34ef238826909a8
--- /dev/null
+++ b/packages/create-llama/helpers/is-online.ts
@@ -0,0 +1,40 @@
+import { execSync } from "child_process";
+import dns from "dns";
+import url from "url";
+
+function getProxy(): string | undefined {
+  if (process.env.https_proxy) {
+    return process.env.https_proxy;
+  }
+
+  try {
+    const httpsProxy = execSync("npm config get https-proxy").toString().trim();
+    return httpsProxy !== "null" ? httpsProxy : undefined;
+  } catch (e) {
+    return;
+  }
+}
+
+export function getOnline(): Promise<boolean> {
+  return new Promise((resolve) => {
+    dns.lookup("registry.yarnpkg.com", (registryErr) => {
+      if (!registryErr) {
+        return resolve(true);
+      }
+
+      const proxy = getProxy();
+      if (!proxy) {
+        return resolve(false);
+      }
+
+      const { hostname } = url.parse(proxy);
+      if (!hostname) {
+        return resolve(false);
+      }
+
+      dns.lookup(hostname, (proxyErr) => {
+        resolve(proxyErr == null);
+      });
+    });
+  });
+}
diff --git a/packages/create-llama/helpers/is-url.ts b/packages/create-llama/helpers/is-url.ts
new file mode 100644
index 0000000000000000000000000000000000000000..eb87b975252f721bf596f98b628f4b6394a63516
--- /dev/null
+++ b/packages/create-llama/helpers/is-url.ts
@@ -0,0 +1,8 @@
+export function isUrl(url: string): boolean {
+  try {
+    new URL(url);
+    return true;
+  } catch (error) {
+    return false;
+  }
+}
diff --git a/packages/create-llama/helpers/is-writeable.ts b/packages/create-llama/helpers/is-writeable.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fa29d60558b33c0cd9823ec29842d48648b37901
--- /dev/null
+++ b/packages/create-llama/helpers/is-writeable.ts
@@ -0,0 +1,10 @@
+import fs from "fs";
+
+export async function isWriteable(directory: string): Promise<boolean> {
+  try {
+    await fs.promises.access(directory, (fs.constants || fs).W_OK);
+    return true;
+  } catch (err) {
+    return false;
+  }
+}
diff --git a/packages/create-llama/helpers/make-dir.ts b/packages/create-llama/helpers/make-dir.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2c258fd6b5a0d2362c13cfcbade7cac21702fb5a
--- /dev/null
+++ b/packages/create-llama/helpers/make-dir.ts
@@ -0,0 +1,8 @@
+import fs from "fs";
+
+export function makeDir(
+  root: string,
+  options = { recursive: true },
+): Promise<string | undefined> {
+  return fs.promises.mkdir(root, options);
+}
diff --git a/packages/create-llama/helpers/validate-pkg.ts b/packages/create-llama/helpers/validate-pkg.ts
new file mode 100644
index 0000000000000000000000000000000000000000..68317653c8e455aec9ce6d192f725a74b8199cfd
--- /dev/null
+++ b/packages/create-llama/helpers/validate-pkg.ts
@@ -0,0 +1,20 @@
+// eslint-disable-next-line import/no-extraneous-dependencies
+import validateProjectName from "validate-npm-package-name";
+
+export function validateNpmName(name: string): {
+  valid: boolean;
+  problems?: string[];
+} {
+  const nameValidation = validateProjectName(name);
+  if (nameValidation.validForNewPackages) {
+    return { valid: true };
+  }
+
+  return {
+    valid: false,
+    problems: [
+      ...(nameValidation.errors || []),
+      ...(nameValidation.warnings || []),
+    ],
+  };
+}
diff --git a/packages/create-llama/index.ts b/packages/create-llama/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b1009a4dbd043960edeb018c04b6f265bf57d3c6
--- /dev/null
+++ b/packages/create-llama/index.ts
@@ -0,0 +1,399 @@
+#!/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 prompts from "prompts";
+import checkForUpdate from "update-check";
+import { createApp } from "./create-app";
+import { getPkgManager } from "./helpers/get-pkg-manager";
+import { isFolderEmpty } from "./helpers/is-folder-empty";
+import { validateNpmName } from "./helpers/validate-pkg";
+import packageJson from "./package.json";
+
+let projectPath: string = "";
+
+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>")
+  .usage(`${green("<project-directory>")} [options]`)
+  .action((name) => {
+    projectPath = name;
+  })
+  .option(
+    "--eslint",
+    `
+
+  Initialize with eslint config.
+`,
+  )
+  .option(
+    "--use-npm",
+    `
+
+  Explicitly tell the CLI to bootstrap the application using npm
+`,
+  )
+  .option(
+    "--use-pnpm",
+    `
+
+  Explicitly tell the CLI to bootstrap the application using pnpm
+`,
+  )
+  .option(
+    "--use-yarn",
+    `
+
+  Explicitly tell the CLI to bootstrap the application using Yarn
+`,
+  )
+  .option(
+    "--reset-preferences",
+    `
+
+  Explicitly tell the CLI to reset any stored preferences
+`,
+  )
+  .allowUnknownOption()
+  .parse(process.argv);
+
+const packageManager = !!program.useNpm
+  ? "npm"
+  : !!program.usePnpm
+  ? "pnpm"
+  : !!program.useYarn
+  ? "yarn"
+  : getPkgManager();
+
+async function run(): Promise<void> {
+  const conf = new Conf({ projectName: "create-llama" });
+
+  if (program.resetPreferences) {
+    conf.clear();
+    console.log(`Preferences reset successfully`);
+    return;
+  }
+
+  if (typeof projectPath === "string") {
+    projectPath = projectPath.trim();
+  }
+
+  if (!projectPath) {
+    const res = await prompts({
+      onState: onPromptState,
+      type: "text",
+      name: "path",
+      message: "What is your project named?",
+      initial: "my-app",
+      validate: (name) => {
+        const validation = validateNpmName(path.basename(path.resolve(name)));
+        if (validation.valid) {
+          return true;
+        }
+        return "Invalid project name: " + validation.problems![0];
+      },
+    });
+
+    if (typeof res.path === "string") {
+      projectPath = res.path.trim();
+    }
+  }
+
+  if (!projectPath) {
+    console.log(
+      "\nPlease specify the project directory:\n" +
+        `  ${cyan(program.name())} ${green("<project-directory>")}\n` +
+        "For example:\n" +
+        `  ${cyan(program.name())} ${green("my-next-app")}\n\n` +
+        `Run ${cyan(`${program.name()} --help`)} to see all options.`,
+    );
+    process.exit(1);
+  }
+
+  const resolvedProjectPath = path.resolve(projectPath);
+  const projectName = path.basename(resolvedProjectPath);
+
+  const { valid, problems } = validateNpmName(projectName);
+  if (!valid) {
+    console.error(
+      `Could not create a project called ${red(
+        `"${projectName}"`,
+      )} because of npm naming restrictions:`,
+    );
+
+    problems!.forEach((p) => console.error(`    ${red(bold("*"))} ${p}`));
+    process.exit(1);
+  }
+
+  /**
+   * Verify the project dir is empty or doesn't exist
+   */
+  const root = path.resolve(resolvedProjectPath);
+  const appName = path.basename(root);
+  const folderExists = fs.existsSync(root);
+
+  if (folderExists && !isFolderEmpty(root, appName)) {
+    process.exit(1);
+  }
+
+  const preferences = (conf.get("preferences") || {}) as Record<
+    string,
+    boolean | string
+  >;
+
+  const defaults: typeof preferences = {
+    template: "simple",
+    framework: "nextjs",
+    engine: "simple",
+    ui: "html",
+    eslint: true,
+    frontend: false,
+    openAIKey: "",
+  };
+  const getPrefOrDefault = (field: string) =>
+    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" },
+          ],
+          initial: 1,
+        },
+        handlers,
+      );
+      program.template = template;
+      preferences.template = template;
+    }
+  }
+
+  if (!program.framework) {
+    if (ciInfo.isCI) {
+      program.framework = getPrefOrDefault("framework");
+    } else {
+      const { framework } = await prompts(
+        {
+          type: "select",
+          name: "framework",
+          message: "Which framework would you like to use?",
+          choices: [
+            { title: "NextJS", value: "nextjs" },
+            { title: "Express", value: "express" },
+            { title: "FastAPI (Python)", value: "fastapi" },
+          ],
+          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 === "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: "SimpleChatEngine", value: "simple" },
+              { title: "ContextChatEngine", value: "context" },
+            ],
+            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);
+    }
+  }
+
+  await createApp({
+    template: program.template,
+    framework: program.framework,
+    engine: program.engine,
+    ui: program.ui,
+    appPath: resolvedProjectPath,
+    packageManager,
+    eslint: program.eslint,
+    frontend: program.frontend,
+    openAIKey: program.openAIKey,
+  });
+  conf.set("preferences", preferences);
+}
+
+const update = checkForUpdate(packageJson).catch(() => null);
+
+async function notifyUpdate(): Promise<void> {
+  try {
+    const res = await update;
+    if (res?.latest) {
+      const updateMessage =
+        packageManager === "yarn"
+          ? "yarn global add create-llama"
+          : packageManager === "pnpm"
+          ? "pnpm add -g create-llama"
+          : "npm i -g create-llama";
+
+      console.log(
+        yellow(bold("A new version of `create-llama` is available!")) +
+          "\n" +
+          "You can update by running: " +
+          cyan(updateMessage) +
+          "\n",
+      );
+    }
+    process.exit();
+  } catch {
+    // ignore error
+  }
+}
+
+run()
+  .then(notifyUpdate)
+  .catch(async (reason) => {
+    console.log();
+    console.log("Aborting installation.");
+    if (reason.command) {
+      console.log(`  ${cyan(reason.command)} has failed.`);
+    } else {
+      console.log(
+        red("Unexpected error. Please report it as a bug:") + "\n",
+        reason,
+      );
+    }
+    console.log();
+
+    await notifyUpdate();
+
+    process.exit(1);
+  });
diff --git a/packages/create-llama/package.json b/packages/create-llama/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..56e1aee0be26ade3540be67a72f357e98f94904d
--- /dev/null
+++ b/packages/create-llama/package.json
@@ -0,0 +1,55 @@
+{
+  "name": "create-llama",
+  "version": "0.0.34",
+  "keywords": [
+    "rag",
+    "llamaindex",
+    "next.js"
+  ],
+  "description": "Create LlamaIndex-powered apps with one command",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/run-llama/LlamaIndexTS",
+    "directory": "packages/create-llama"
+  },
+  "license": "MIT",
+  "bin": {
+    "create-llama": "./dist/index.js"
+  },
+  "files": [
+    "dist"
+  ],
+  "scripts": {
+    "dev": "ncc build ./index.ts -w -o dist/",
+    "build": "ncc build ./index.ts -o ./dist/ --minify --no-cache --no-source-map-register",
+    "lint": "eslint . --ignore-pattern dist",
+    "prepublishOnly": "cd ../../ && turbo run build"
+  },
+  "devDependencies": {
+    "@types/async-retry": "1.4.2",
+    "@types/ci-info": "2.0.0",
+    "@types/cross-spawn": "6.0.0",
+    "@types/node": "^20.2.5",
+    "@types/prompts": "2.0.1",
+    "@types/tar": "6.1.5",
+    "@types/validate-npm-package-name": "3.0.0",
+    "@vercel/ncc": "0.34.0",
+    "async-retry": "1.3.1",
+    "async-sema": "3.0.1",
+    "ci-info": "watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540",
+    "commander": "2.20.0",
+    "conf": "10.2.0",
+    "cross-spawn": "7.0.3",
+    "fast-glob": "3.3.1",
+    "got": "10.7.0",
+    "picocolors": "1.0.0",
+    "prompts": "2.1.0",
+    "tar": "6.1.15",
+    "update-check": "1.5.4",
+    "validate-npm-package-name": "3.0.0",
+    "terminal-link": "^3.0.0"
+  },
+  "engines": {
+    "node": ">=16.14.0"
+  }
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/.gitignore b/packages/create-llama/templates/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..ec6c67b630467343abb46cfeea0535ce4b339554
--- /dev/null
+++ b/packages/create-llama/templates/.gitignore
@@ -0,0 +1,3 @@
+__pycache__
+poetry.lock
+storage
diff --git a/packages/create-llama/templates/README-fullstack.md b/packages/create-llama/templates/README-fullstack.md
new file mode 100644
index 0000000000000000000000000000000000000000..5a41b8cfc370f4fe99269331065bdee8b6aa8e8c
--- /dev/null
+++ b/packages/create-llama/templates/README-fullstack.md
@@ -0,0 +1,18 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, startup the backend as described in the [backend README](./backend/README.md).
+
+Second, run the development server of the frontend as described in the [frontend README](./frontend/README.md).
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
+- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
+
+You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/components/data/101.pdf b/packages/create-llama/templates/components/data/101.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..ae5acffd5398b7c59e2df9e6dead2d99128b719c
Binary files /dev/null and b/packages/create-llama/templates/components/data/101.pdf differ
diff --git a/packages/create-llama/templates/components/engines/context/constants.mjs b/packages/create-llama/templates/components/engines/context/constants.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..8cfb403c3790b0c5088a802071896d2fdca98ded
--- /dev/null
+++ b/packages/create-llama/templates/components/engines/context/constants.mjs
@@ -0,0 +1,4 @@
+export const STORAGE_DIR = "./data";
+export const STORAGE_CACHE_DIR = "./cache";
+export const CHUNK_SIZE = 512;
+export const CHUNK_OVERLAP = 20;
diff --git a/packages/create-llama/templates/components/engines/context/generate.mjs b/packages/create-llama/templates/components/engines/context/generate.mjs
new file mode 100644
index 0000000000000000000000000000000000000000..8420dd5f81eab52574ec51770cf0e09ccb139563
--- /dev/null
+++ b/packages/create-llama/templates/components/engines/context/generate.mjs
@@ -0,0 +1,48 @@
+import {
+  serviceContextFromDefaults,
+  SimpleDirectoryReader,
+  storageContextFromDefaults,
+  VectorStoreIndex,
+} from "llamaindex";
+
+import {
+  CHUNK_OVERLAP,
+  CHUNK_SIZE,
+  STORAGE_CACHE_DIR,
+  STORAGE_DIR,
+} from "./constants.mjs";
+
+async function getRuntime(func) {
+  const start = Date.now();
+  await func();
+  const end = Date.now();
+  return end - start;
+}
+
+async function generateDatasource(serviceContext) {
+  console.log(`Generating storage context...`);
+  // Split documents, create embeddings and store them in the storage context
+  const ms = await getRuntime(async () => {
+    const storageContext = await storageContextFromDefaults({
+      persistDir: STORAGE_CACHE_DIR,
+    });
+    const documents = await new SimpleDirectoryReader().loadData({
+      directoryPath: STORAGE_DIR,
+    });
+    await VectorStoreIndex.fromDocuments(documents, {
+      storageContext,
+      serviceContext,
+    });
+  });
+  console.log(`Storage context successfully generated in ${ms / 1000}s.`);
+}
+
+(async () => {
+  const serviceContext = serviceContextFromDefaults({
+    chunkSize: CHUNK_SIZE,
+    chunkOverlap: CHUNK_OVERLAP,
+  });
+
+  await generateDatasource(serviceContext);
+  console.log("Finished generating storage.");
+})();
diff --git a/packages/create-llama/templates/components/engines/context/index.ts b/packages/create-llama/templates/components/engines/context/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cdd93809dcd7d9939aba8798fdac3639151e4ac5
--- /dev/null
+++ b/packages/create-llama/templates/components/engines/context/index.ts
@@ -0,0 +1,44 @@
+import {
+  ContextChatEngine,
+  LLM,
+  serviceContextFromDefaults,
+  SimpleDocumentStore,
+  storageContextFromDefaults,
+  VectorStoreIndex,
+} from "llamaindex";
+import { CHUNK_OVERLAP, CHUNK_SIZE, STORAGE_CACHE_DIR } from "./constants.mjs";
+
+async function getDataSource(llm: LLM) {
+  const serviceContext = serviceContextFromDefaults({
+    llm,
+    chunkSize: CHUNK_SIZE,
+    chunkOverlap: CHUNK_OVERLAP,
+  });
+  let storageContext = await storageContextFromDefaults({
+    persistDir: `${STORAGE_CACHE_DIR}`,
+  });
+
+  const numberOfDocs = Object.keys(
+    (storageContext.docStore as SimpleDocumentStore).toDict(),
+  ).length;
+  if (numberOfDocs === 0) {
+    throw new Error(
+      `StorageContext is empty - call 'npm run generate' to generate the storage first`,
+    );
+  }
+  return await VectorStoreIndex.init({
+    storageContext,
+    serviceContext,
+  });
+}
+
+export async function createChatEngine(llm: LLM) {
+  const index = await getDataSource(llm);
+  const retriever = index.asRetriever();
+  retriever.similarityTopK = 5;
+
+  return new ContextChatEngine({
+    chatModel: llm,
+    retriever,
+  });
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/README-template.md b/packages/create-llama/templates/components/ui/shadcn/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..ebfcf48c999ef8e040b282adfd4312767f731bb5
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/README-template.md
@@ -0,0 +1 @@
+Using the chat component from https://github.com/marcusschiesser/ui (based on https://ui.shadcn.com/)
diff --git a/packages/create-llama/templates/components/ui/shadcn/button.tsx b/packages/create-llama/templates/components/ui/shadcn/button.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..662b0404d83445eb7fca1ead6724e944610fdf25
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/button.tsx
@@ -0,0 +1,56 @@
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react";
+
+import { cn } from "./lib/utils";
+
+const buttonVariants = cva(
+  "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+  {
+    variants: {
+      variant: {
+        default: "bg-primary text-primary-foreground hover:bg-primary/90",
+        destructive:
+          "bg-destructive text-destructive-foreground hover:bg-destructive/90",
+        outline:
+          "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+        secondary:
+          "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+        ghost: "hover:bg-accent hover:text-accent-foreground",
+        link: "text-primary underline-offset-4 hover:underline",
+      },
+      size: {
+        default: "h-10 px-4 py-2",
+        sm: "h-9 rounded-md px-3",
+        lg: "h-11 rounded-md px-8",
+        icon: "h-10 w-10",
+      },
+    },
+    defaultVariants: {
+      variant: "default",
+      size: "default",
+    },
+  },
+);
+
+export interface ButtonProps
+  extends React.ButtonHTMLAttributes<HTMLButtonElement>,
+    VariantProps<typeof buttonVariants> {
+  asChild?: boolean;
+}
+
+const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
+  ({ className, variant, size, asChild = false, ...props }, ref) => {
+    const Comp = asChild ? Slot : "button";
+    return (
+      <Comp
+        className={cn(buttonVariants({ variant, size, className }))}
+        ref={ref}
+        {...props}
+      />
+    );
+  },
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/chat-actions.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/chat-actions.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..151ef61a945c49cacfd44c77cbdf7287b5967861
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/chat-actions.tsx
@@ -0,0 +1,28 @@
+import { PauseCircle, RefreshCw } from "lucide-react";
+
+import { Button } from "../button";
+import { ChatHandler } from "./chat.interface";
+
+export default function ChatActions(
+  props: Pick<ChatHandler, "stop" | "reload"> & {
+    showReload?: boolean;
+    showStop?: boolean;
+  },
+) {
+  return (
+    <div className="space-x-4">
+      {props.showStop && (
+        <Button variant="outline" size="sm" onClick={props.stop}>
+          <PauseCircle className="mr-2 h-4 w-4" />
+          Stop generating
+        </Button>
+      )}
+      {props.showReload && (
+        <Button variant="outline" size="sm" onClick={props.reload}>
+          <RefreshCw className="mr-2 h-4 w-4" />
+          Regenerate
+        </Button>
+      )}
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/chat-avatar.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/chat-avatar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ce04e306a7164e49e7ea6950a55c4f5cedc2ee2a
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/chat-avatar.tsx
@@ -0,0 +1,25 @@
+import { User2 } from "lucide-react";
+import Image from "next/image";
+
+export default function ChatAvatar({ role }: { role: string }) {
+  if (role === "user") {
+    return (
+      <div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border bg-background shadow">
+        <User2 className="h-4 w-4" />
+      </div>
+    );
+  }
+
+  return (
+    <div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border bg-black text-white shadow">
+      <Image
+        className="rounded-md"
+        src="/llama.png"
+        alt="Llama Logo"
+        width={24}
+        height={24}
+        priority
+      />
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/chat-input.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/chat-input.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..1a0cc3e0cc6d92178bafce5e8d573586e024e3f6
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/chat-input.tsx
@@ -0,0 +1,29 @@
+import { Button } from "../button";
+import { Input } from "../input";
+import { ChatHandler } from "./chat.interface";
+
+export default function ChatInput(
+  props: Pick<
+    ChatHandler,
+    "isLoading" | "handleSubmit" | "handleInputChange" | "input"
+  >,
+) {
+  return (
+    <form
+      onSubmit={props.handleSubmit}
+      className="flex w-full items-start justify-between gap-4 rounded-xl bg-white p-4 shadow-xl"
+    >
+      <Input
+        autoFocus
+        name="message"
+        placeholder="Type a message"
+        className="flex-1"
+        value={props.input}
+        onChange={props.handleInputChange}
+      />
+      <Button type="submit" disabled={props.isLoading}>
+        Send message
+      </Button>
+    </form>
+  );
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/chat-message.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/chat-message.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..9ada08a3d7498a204ef3403b2148007f302936bf
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/chat-message.tsx
@@ -0,0 +1,33 @@
+import { Check, Copy } from "lucide-react";
+
+import { Button } from "../button";
+import ChatAvatar from "./chat-avatar";
+import { Message } from "./chat.interface";
+import Markdown from "./markdown";
+import { useCopyToClipboard } from "./use-copy-to-clipboard";
+
+export default function ChatMessage(chatMessage: Message) {
+  const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 });
+  return (
+    <div className="flex items-start gap-4 pr-5 pt-5">
+      <ChatAvatar role={chatMessage.role} />
+      <div className="group flex flex-1 justify-between gap-2">
+        <div className="flex-1">
+          <Markdown content={chatMessage.content} />
+        </div>
+        <Button
+          onClick={() => copyToClipboard(chatMessage.content)}
+          size="icon"
+          variant="ghost"
+          className="h-8 w-8 opacity-0 group-hover:opacity-100"
+        >
+          {isCopied ? (
+            <Check className="h-4 w-4" />
+          ) : (
+            <Copy className="h-4 w-4" />
+          )}
+        </Button>
+      </div>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/chat-messages.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/chat-messages.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..dd0a442b6cd567094f6393bf2ccf221ee1eb178d
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/chat-messages.tsx
@@ -0,0 +1,51 @@
+import { useEffect, useRef } from "react";
+
+import ChatActions from "./chat-actions";
+import ChatMessage from "./chat-message";
+import { ChatHandler } from "./chat.interface";
+
+export default function ChatMessages(
+  props: Pick<ChatHandler, "messages" | "isLoading" | "reload" | "stop">,
+) {
+  const scrollableChatContainerRef = useRef<HTMLDivElement>(null);
+  const messageLength = props.messages.length;
+  const lastMessage = props.messages[messageLength - 1];
+
+  const scrollToBottom = () => {
+    if (scrollableChatContainerRef.current) {
+      scrollableChatContainerRef.current.scrollTop =
+        scrollableChatContainerRef.current.scrollHeight;
+    }
+  };
+
+  const isLastMessageFromAssistant =
+    messageLength > 0 && lastMessage?.role !== "user";
+  const showReload =
+    props.reload && !props.isLoading && isLastMessageFromAssistant;
+  const showStop = props.stop && props.isLoading;
+
+  useEffect(() => {
+    scrollToBottom();
+  }, [messageLength, lastMessage]);
+
+  return (
+    <div className="w-full rounded-xl bg-white p-4 shadow-xl pb-0">
+      <div
+        className="flex h-[50vh] flex-col gap-5 divide-y overflow-y-auto pb-4"
+        ref={scrollableChatContainerRef}
+      >
+        {props.messages.map((m) => (
+          <ChatMessage key={m.id} {...m} />
+        ))}
+      </div>
+      <div className="flex justify-end py-4">
+        <ChatActions
+          reload={props.reload}
+          stop={props.stop}
+          showReload={showReload}
+          showStop={showStop}
+        />
+      </div>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/chat.interface.ts b/packages/create-llama/templates/components/ui/shadcn/chat/chat.interface.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3256f7f031b42114f192f3375632654dc21f78d8
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/chat.interface.ts
@@ -0,0 +1,15 @@
+export interface Message {
+  id: string;
+  content: string;
+  role: string;
+}
+
+export interface ChatHandler {
+  messages: Message[];
+  input: string;
+  isLoading: boolean;
+  handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
+  handleInputChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
+  reload?: () => void;
+  stop?: () => void;
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/codeblock.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/codeblock.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..10598223b811a77c09a53a27857cd433a71dcfef
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/codeblock.tsx
@@ -0,0 +1,139 @@
+"use client"
+
+import React, { FC, memo } from "react"
+import { Check, Copy, Download } from "lucide-react"
+import { Prism, SyntaxHighlighterProps } from "react-syntax-highlighter"
+import { coldarkDark } from "react-syntax-highlighter/dist/cjs/styles/prism"
+
+import { Button } from "../button"
+import { useCopyToClipboard } from "./use-copy-to-clipboard"
+
+// TODO: Remove this when @type/react-syntax-highlighter is updated
+const SyntaxHighlighter = Prism as unknown as FC<SyntaxHighlighterProps>
+
+interface Props {
+  language: string
+  value: string
+}
+
+interface languageMap {
+  [key: string]: string | undefined
+}
+
+export const programmingLanguages: languageMap = {
+  javascript: ".js",
+  python: ".py",
+  java: ".java",
+  c: ".c",
+  cpp: ".cpp",
+  "c++": ".cpp",
+  "c#": ".cs",
+  ruby: ".rb",
+  php: ".php",
+  swift: ".swift",
+  "objective-c": ".m",
+  kotlin: ".kt",
+  typescript: ".ts",
+  go: ".go",
+  perl: ".pl",
+  rust: ".rs",
+  scala: ".scala",
+  haskell: ".hs",
+  lua: ".lua",
+  shell: ".sh",
+  sql: ".sql",
+  html: ".html",
+  css: ".css",
+  // add more file extensions here, make sure the key is same as language prop in CodeBlock.tsx component
+}
+
+export const generateRandomString = (length: number, lowercase = false) => {
+  const chars = "ABCDEFGHJKLMNPQRSTUVWXY3456789" // excluding similar looking characters like Z, 2, I, 1, O, 0
+  let result = ""
+  for (let i = 0; i < length; i++) {
+    result += chars.charAt(Math.floor(Math.random() * chars.length))
+  }
+  return lowercase ? result.toLowerCase() : result
+}
+
+const CodeBlock: FC<Props> = memo(({ language, value }) => {
+  const { isCopied, copyToClipboard } = useCopyToClipboard({ timeout: 2000 })
+
+  const downloadAsFile = () => {
+    if (typeof window === "undefined") {
+      return
+    }
+    const fileExtension = programmingLanguages[language] || ".file"
+    const suggestedFileName = `file-${generateRandomString(
+      3,
+      true
+    )}${fileExtension}`
+    const fileName = window.prompt("Enter file name" || "", suggestedFileName)
+
+    if (!fileName) {
+      // User pressed cancel on prompt.
+      return
+    }
+
+    const blob = new Blob([value], { type: "text/plain" })
+    const url = URL.createObjectURL(blob)
+    const link = document.createElement("a")
+    link.download = fileName
+    link.href = url
+    link.style.display = "none"
+    document.body.appendChild(link)
+    link.click()
+    document.body.removeChild(link)
+    URL.revokeObjectURL(url)
+  }
+
+  const onCopy = () => {
+    if (isCopied) return
+    copyToClipboard(value)
+  }
+
+  return (
+    <div className="codeblock relative w-full bg-zinc-950 font-sans">
+      <div className="flex w-full items-center justify-between bg-zinc-800 px-6 py-2 pr-4 text-zinc-100">
+        <span className="text-xs lowercase">{language}</span>
+        <div className="flex items-center space-x-1">
+          <Button variant="ghost" onClick={downloadAsFile} size="icon">
+            <Download />
+            <span className="sr-only">Download</span>
+          </Button>
+          <Button variant="ghost" size="icon" onClick={onCopy}>
+            {isCopied ? (
+              <Check className="h-4 w-4" />
+            ) : (
+              <Copy className="h-4 w-4" />
+            )}
+            <span className="sr-only">Copy code</span>
+          </Button>
+        </div>
+      </div>
+      <SyntaxHighlighter
+        language={language}
+        style={coldarkDark}
+        PreTag="div"
+        showLineNumbers
+        customStyle={{
+          width: "100%",
+          background: "transparent",
+          padding: "1.5rem 1rem",
+          borderRadius: "0.5rem",
+        }}
+        codeTagProps={{
+          style: {
+            fontSize: "0.9rem",
+            fontFamily: "var(--font-mono)",
+          },
+        }}
+      >
+        {value}
+      </SyntaxHighlighter>
+    </div>
+  )
+})
+CodeBlock.displayName = "CodeBlock"
+
+export { CodeBlock }
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/index.ts b/packages/create-llama/templates/components/ui/shadcn/chat/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..0b8104960cff17adb4bb2068d0cb7dcd1f8a93ae
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/index.ts
@@ -0,0 +1,5 @@
+import ChatInput from "./chat-input";
+import ChatMessages from "./chat-messages";
+
+export { type ChatHandler, type Message } from "./chat.interface";
+export { ChatMessages, ChatInput };
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/markdown.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/markdown.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..31b78242d257db674676d4c69a076b26785d0033
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/markdown.tsx
@@ -0,0 +1,59 @@
+import { FC, memo } from "react"
+import ReactMarkdown, { Options } from "react-markdown"
+import remarkGfm from "remark-gfm"
+import remarkMath from "remark-math"
+
+import { CodeBlock } from "./codeblock"
+
+const MemoizedReactMarkdown: FC<Options> = memo(
+  ReactMarkdown,
+  (prevProps, nextProps) =>
+    prevProps.children === nextProps.children &&
+    prevProps.className === nextProps.className
+)
+
+export default function Markdown({ content }: { content: string }) {
+  return (
+    <MemoizedReactMarkdown
+      className="prose dark:prose-invert prose-p:leading-relaxed prose-pre:p-0 break-words"
+      remarkPlugins={[remarkGfm, remarkMath]}
+      components={{
+        p({ children }) {
+          return <p className="mb-2 last:mb-0">{children}</p>
+        },
+        code({ node, inline, className, children, ...props }) {
+          if (children.length) {
+            if (children[0] == "▍") {
+              return (
+                <span className="mt-1 animate-pulse cursor-default">▍</span>
+              )
+            }
+
+            children[0] = (children[0] as string).replace("`▍`", "▍")
+          }
+
+          const match = /language-(\w+)/.exec(className || "")
+
+          if (inline) {
+            return (
+              <code className={className} {...props}>
+                {children}
+              </code>
+            )
+          }
+
+          return (
+            <CodeBlock
+              key={Math.random()}
+              language={(match && match[1]) || ""}
+              value={String(children).replace(/\n$/, "")}
+              {...props}
+            />
+          )
+        },
+      }}
+    >
+      {content}
+    </MemoizedReactMarkdown>
+  )
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/chat/use-copy-to-clipboard.tsx b/packages/create-llama/templates/components/ui/shadcn/chat/use-copy-to-clipboard.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..62f7156dca246c46b213151af003a3a177977ccf
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/chat/use-copy-to-clipboard.tsx
@@ -0,0 +1,33 @@
+'use client'
+
+import * as React from 'react'
+
+export interface useCopyToClipboardProps {
+  timeout?: number
+}
+
+export function useCopyToClipboard({
+  timeout = 2000
+}: useCopyToClipboardProps) {
+  const [isCopied, setIsCopied] = React.useState<Boolean>(false)
+
+  const copyToClipboard = (value: string) => {
+    if (typeof window === 'undefined' || !navigator.clipboard?.writeText) {
+      return
+    }
+
+    if (!value) {
+      return
+    }
+
+    navigator.clipboard.writeText(value).then(() => {
+      setIsCopied(true)
+
+      setTimeout(() => {
+        setIsCopied(false)
+      }, timeout)
+    })
+  }
+
+  return { isCopied, copyToClipboard }
+}
diff --git a/packages/create-llama/templates/components/ui/shadcn/input.tsx b/packages/create-llama/templates/components/ui/shadcn/input.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..edfa129e623cca64fa706d7750e3635fecd1d628
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/input.tsx
@@ -0,0 +1,25 @@
+import * as React from "react";
+
+import { cn } from "./lib/utils";
+
+export interface InputProps
+  extends React.InputHTMLAttributes<HTMLInputElement> {}
+
+const Input = React.forwardRef<HTMLInputElement, InputProps>(
+  ({ className, type, ...props }, ref) => {
+    return (
+      <input
+        type={type}
+        className={cn(
+          "flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
+          className,
+        )}
+        ref={ref}
+        {...props}
+      />
+    );
+  },
+);
+Input.displayName = "Input";
+
+export { Input };
diff --git a/packages/create-llama/templates/components/ui/shadcn/lib/utils.ts b/packages/create-llama/templates/components/ui/shadcn/lib/utils.ts
new file mode 100644
index 0000000000000000000000000000000000000000..a5ef193506d07d0459fec4f187af08283094d7c8
--- /dev/null
+++ b/packages/create-llama/templates/components/ui/shadcn/lib/utils.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from "clsx";
+import { twMerge } from "tailwind-merge";
+
+export function cn(...inputs: ClassValue[]) {
+  return twMerge(clsx(inputs));
+}
diff --git a/packages/create-llama/templates/index.ts b/packages/create-llama/templates/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..98eece524f8987199cdf69f7ecec0d47e16ca811
--- /dev/null
+++ b/packages/create-llama/templates/index.ts
@@ -0,0 +1,296 @@
+import { copy } from "../helpers/copy";
+import { callPackageManager } from "../helpers/install";
+
+import fs from "fs/promises";
+import os from "os";
+import path from "path";
+import { bold, cyan } from "picocolors";
+import { version } from "../package.json";
+
+import { PackageManager } from "../helpers/get-pkg-manager";
+import {
+  InstallTemplateArgs,
+  TemplateEngine,
+  TemplateFramework,
+} from "./types";
+
+const envFileNameMap: Record<TemplateFramework, string> = {
+  nextjs: ".env.local",
+  express: ".env",
+  fastapi: ".env",
+};
+
+const createEnvLocalFile = async (
+  root: string,
+  framework: TemplateFramework,
+  openAIKey?: string,
+) => {
+  if (openAIKey) {
+    const envFileName = envFileNameMap[framework];
+    if (!envFileName) return;
+    await fs.writeFile(
+      path.join(root, envFileName),
+      `OPENAI_API_KEY=${openAIKey}\n`,
+    );
+    console.log(`Created '${envFileName}' file containing OPENAI_API_KEY`);
+  }
+};
+
+const copyTestData = async (
+  root: string,
+  framework: TemplateFramework,
+  packageManager?: PackageManager,
+  engine?: TemplateEngine,
+) => {
+  if (engine === "context" || framework === "fastapi") {
+    const srcPath = path.join(__dirname, "components", "data");
+    const destPath = path.join(root, "data");
+    console.log(`\nCopying test data to ${cyan(destPath)}\n`);
+    await copy("**", destPath, {
+      parents: true,
+      cwd: srcPath,
+    });
+  }
+
+  if (packageManager && engine === "context") {
+    console.log(
+      `\nRunning ${cyan("npm run generate")} to generate the context data.\n`,
+    );
+    await callPackageManager(packageManager, true, ["run", "generate"]);
+    console.log();
+  }
+};
+
+const rename = (name: string) => {
+  switch (name) {
+    case "gitignore":
+    case "eslintrc.json": {
+      return `.${name}`;
+    }
+    // README.md is ignored by webpack-asset-relocator-loader used by ncc:
+    // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227
+    case "README-template.md": {
+      return "README.md";
+    }
+    default: {
+      return name;
+    }
+  }
+};
+
+/**
+ * Install a LlamaIndex internal template to a given `root` directory.
+ */
+const installTSTemplate = async ({
+  appName,
+  root,
+  packageManager,
+  isOnline,
+  template,
+  framework,
+  engine,
+  ui,
+  eslint,
+  customApiPath,
+}: InstallTemplateArgs) => {
+  console.log(bold(`Using ${packageManager}.`));
+
+  /**
+   * Copy the template files to the target directory.
+   */
+  console.log("\nInitializing project with template:", template, "\n");
+  const templatePath = path.join(__dirname, "types", template, framework);
+  const copySource = ["**"];
+  if (!eslint) copySource.push("!eslintrc.json");
+
+  await copy(copySource, root, {
+    parents: true,
+    cwd: templatePath,
+    rename,
+  });
+
+  /**
+   * Copy the selected chat engine files to the target directory and reference it.
+   */
+  let relativeEngineDestPath;
+  const compPath = path.join(__dirname, "components");
+  if (engine && (framework === "express" || framework === "nextjs")) {
+    console.log("\nUsing chat engine:", engine, "\n");
+    const enginePath = path.join(compPath, "engines", engine);
+    relativeEngineDestPath =
+      framework === "nextjs"
+        ? path.join("app", "api", "chat")
+        : path.join("src", "controllers");
+    await copy("**", path.join(root, relativeEngineDestPath, "engine"), {
+      parents: true,
+      cwd: enginePath,
+    });
+  }
+
+  /**
+   * Copy the selected UI files to the target directory and reference it.
+   */
+  if (framework === "nextjs" && ui !== "html") {
+    console.log("\nUsing UI:", ui, "\n");
+    const uiPath = path.join(compPath, "ui", ui);
+    const destUiPath = path.join(root, "app", "components", "ui");
+    // remove the default ui folder
+    await fs.rm(destUiPath, { recursive: true });
+    // copy the selected ui folder
+    await copy("**", destUiPath, {
+      parents: true,
+      cwd: uiPath,
+      rename,
+    });
+  }
+
+  /**
+   * Update the package.json scripts.
+   */
+  const packageJsonFile = path.join(root, "package.json");
+  const packageJson: any = JSON.parse(
+    await fs.readFile(packageJsonFile, "utf8"),
+  );
+  packageJson.name = appName;
+  packageJson.version = "0.1.0";
+
+  packageJson.dependencies = {
+    ...packageJson.dependencies,
+    llamaindex: version,
+  };
+
+  if (framework === "nextjs" && customApiPath) {
+    console.log(
+      "\nUsing external API with custom API path:",
+      customApiPath,
+      "\n",
+    );
+    // remove the default api folder
+    const apiPath = path.join(root, "app", "api");
+    await fs.rm(apiPath, { recursive: true });
+    // modify the dev script to use the custom api path
+    packageJson.scripts = {
+      ...packageJson.scripts,
+      dev: `NEXT_PUBLIC_CHAT_API=${customApiPath} next dev`,
+    };
+  }
+
+  if (engine === "context" && relativeEngineDestPath) {
+    // add generate script if using context engine
+    packageJson.scripts = {
+      ...packageJson.scripts,
+      generate: `node ${path.join(
+        relativeEngineDestPath,
+        "engine",
+        "generate.mjs",
+      )}`,
+    };
+  }
+
+  if (framework === "nextjs" && ui === "shadcn") {
+    // add shadcn dependencies to package.json
+    packageJson.dependencies = {
+      ...packageJson.dependencies,
+      "tailwind-merge": "^2",
+      "@radix-ui/react-slot": "^1",
+      "class-variance-authority": "^0.7",
+      "lucide-react": "^0.291",
+      remark: "^14.0.3",
+      "remark-code-import": "^1.2.0",
+      "remark-gfm": "^3.0.1",
+      "remark-math": "^5.1.1",
+      "react-markdown": "^8.0.7",
+      "react-syntax-highlighter": "^15.5.0",
+    };
+
+    packageJson.devDependencies = {
+      ...packageJson.devDependencies,
+      "@types/react-syntax-highlighter": "^15.5.6",
+    };
+  }
+
+  if (!eslint) {
+    // Remove packages starting with "eslint" from devDependencies
+    packageJson.devDependencies = Object.fromEntries(
+      Object.entries(packageJson.devDependencies).filter(
+        ([key]) => !key.startsWith("eslint"),
+      ),
+    );
+  }
+  await fs.writeFile(
+    packageJsonFile,
+    JSON.stringify(packageJson, null, 2) + os.EOL,
+  );
+
+  console.log("\nInstalling dependencies:");
+  for (const dependency in packageJson.dependencies)
+    console.log(`- ${cyan(dependency)}`);
+
+  console.log("\nInstalling devDependencies:");
+  for (const dependency in packageJson.devDependencies)
+    console.log(`- ${cyan(dependency)}`);
+
+  console.log();
+
+  await callPackageManager(packageManager, isOnline);
+};
+
+const installPythonTemplate = async ({
+  root,
+  template,
+  framework,
+}: Pick<InstallTemplateArgs, "root" | "framework" | "template">) => {
+  console.log("\nInitializing Python project with template:", template, "\n");
+  const templatePath = path.join(__dirname, "types", template, framework);
+  await copy("**", root, {
+    parents: true,
+    cwd: templatePath,
+    rename(name) {
+      switch (name) {
+        case "gitignore": {
+          return `.${name}`;
+        }
+        // README.md is ignored by webpack-asset-relocator-loader used by ncc:
+        // https://github.com/vercel/webpack-asset-relocator-loader/blob/e9308683d47ff507253e37c9bcbb99474603192b/src/asset-relocator.js#L227
+        case "README-template.md": {
+          return "README.md";
+        }
+        default: {
+          return name;
+        }
+      }
+    },
+  });
+
+  console.log(
+    "\nPython project, dependencies won't be installed automatically.\n",
+  );
+};
+
+export const installTemplate = async (
+  props: InstallTemplateArgs & { backend: boolean },
+) => {
+  process.chdir(props.root);
+  if (props.framework === "fastapi") {
+    await installPythonTemplate(props);
+  } else {
+    await installTSTemplate(props);
+  }
+
+  if (props.backend) {
+    // This is a backend, so we need to copy the test data and create the env file.
+
+    // Copy the environment file to the target directory.
+    await createEnvLocalFile(props.root, props.framework, props.openAIKey);
+
+    // Copy test pdf file
+    await copyTestData(
+      props.root,
+      props.framework,
+      props.packageManager,
+      props.engine,
+    );
+  }
+};
+
+export * from "./types";
diff --git a/packages/create-llama/templates/types.ts b/packages/create-llama/templates/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4bcaf5c7bb8464641e964e4fff74c36393f47fdb
--- /dev/null
+++ b/packages/create-llama/templates/types.ts
@@ -0,0 +1,20 @@
+import { PackageManager } from "../helpers/get-pkg-manager";
+
+export type TemplateType = "simple" | "streaming";
+export type TemplateFramework = "nextjs" | "express" | "fastapi";
+export type TemplateEngine = "simple" | "context";
+export type TemplateUI = "html" | "shadcn";
+
+export interface InstallTemplateArgs {
+  appName: string;
+  root: string;
+  packageManager: PackageManager;
+  isOnline: boolean;
+  template: TemplateType;
+  framework: TemplateFramework;
+  engine?: TemplateEngine;
+  ui: TemplateUI;
+  eslint: boolean;
+  customApiPath?: string;
+  openAIKey?: string;
+}
diff --git a/packages/create-llama/templates/types/simple/express/README-template.md b/packages/create-llama/templates/types/simple/express/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..7ea94ab755535aefd4d5edc9f20a5908d7fedead
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/README-template.md
@@ -0,0 +1,50 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Express](https://expressjs.com/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, install the dependencies:
+
+```
+npm install
+```
+
+Second, run the development server:
+
+```
+npm run dev
+```
+
+Then call the express API endpoint `/api/chat` to see the result:
+
+```
+curl --location 'localhost:8000/api/chat' \
+--header 'Content-Type: application/json' \
+--data '{ "messages": [{ "role": "user", "content": "Hello" }] }'
+```
+
+You can start editing the API by modifying `src/controllers/chat.controller.ts`. The endpoint auto-updates as you save the file.
+
+## Production
+
+First, build the project:
+
+```
+npm run build
+```
+
+You can then run the production server:
+
+```
+NODE_ENV=production npm run start
+```
+
+> Note that the `NODE_ENV` environment variable is set to `production`. This disables CORS for all origins.
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
+- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
+
+You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/types/simple/express/eslintrc.json b/packages/create-llama/templates/types/simple/express/eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..c19581799d886ce5be17def8ad268c2f4b175109
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/eslintrc.json
@@ -0,0 +1,3 @@
+{
+  "extends": "eslint:recommended"
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/types/simple/express/index.ts b/packages/create-llama/templates/types/simple/express/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..70a43ab580aff93df60948076464b9c35bb6f167
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/index.ts
@@ -0,0 +1,26 @@
+import cors from "cors";
+import "dotenv/config";
+import express, { Express, Request, Response } from "express";
+import chatRouter from "./src/routes/chat.route";
+
+const app: Express = express();
+const port = 8000;
+
+const env = process.env["NODE_ENV"];
+const isDevelopment = !env || env === "development";
+if (isDevelopment) {
+  console.warn("Running in development mode - allowing CORS for all origins");
+  app.use(cors());
+}
+
+app.use(express.json());
+
+app.get("/", (req: Request, res: Response) => {
+  res.send("LlamaIndex Express Server");
+});
+
+app.use("/api/chat", chatRouter);
+
+app.listen(port, () => {
+  console.log(`⚡️[server]: Server is running at http://localhost:${port}`);
+});
diff --git a/packages/create-llama/templates/types/simple/express/package.json b/packages/create-llama/templates/types/simple/express/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..55902071463959a02df82ad72ae516d0e385bc78
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/package.json
@@ -0,0 +1,27 @@
+{
+  "name": "llama-index-express",
+  "version": "1.0.0",
+  "main": "dist/index.js",
+  "type": "module",
+  "scripts": {
+    "build": "tsup index.ts --format esm --dts",
+    "start": "node dist/index.js",
+    "dev": "concurrently \"tsup index.ts --format esm --dts --watch\" \"nodemon -q dist/index.js\""
+  },
+  "dependencies": {
+    "cors": "^2.8.5",
+    "dotenv": "^16.3.1",
+    "express": "^4",
+    "llamaindex": "0.0.31"
+  },
+  "devDependencies": {
+    "@types/cors": "^2.8.16",
+    "@types/express": "^4",
+    "@types/node": "^20",
+    "concurrently": "^8",
+    "eslint": "^8",
+    "nodemon": "^3",
+    "tsup": "^7",
+    "typescript": "^5"
+  }
+}
diff --git a/packages/create-llama/templates/types/simple/express/src/controllers/chat.controller.ts b/packages/create-llama/templates/types/simple/express/src/controllers/chat.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..476c0c35d51b7c8259c59035e9cac82f66f37ac1
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/src/controllers/chat.controller.ts
@@ -0,0 +1,37 @@
+import { NextFunction, Request, Response } from "express";
+import { ChatMessage, OpenAI } from "llamaindex";
+import { createChatEngine } from "./engine";
+
+export const chat = async (req: Request, res: Response, next: NextFunction) => {
+  try {
+    const { messages }: { messages: ChatMessage[] } = req.body;
+    const lastMessage = messages.pop();
+    if (!messages || !lastMessage || lastMessage.role !== "user") {
+      return res.status(400).json({
+        error:
+          "messages are required in the request body and the last message must be from the user",
+      });
+    }
+
+    const llm = new OpenAI({
+      model: "gpt-3.5-turbo",
+    });
+
+    const chatEngine = await createChatEngine(llm);
+
+    const response = await chatEngine.chat(lastMessage.content, messages);
+    const result: ChatMessage = {
+      role: "assistant",
+      content: response.response,
+    };
+
+    return res.status(200).json({
+      result,
+    });
+  } catch (error) {
+    console.error("[LlamaIndex]", error);
+    return res.status(500).json({
+      error: (error as Error).message,
+    });
+  }
+};
diff --git a/packages/create-llama/templates/types/simple/express/src/controllers/engine/index.ts b/packages/create-llama/templates/types/simple/express/src/controllers/engine/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abb02e90cd2ce91096791bf10c4665afcbe11d38
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/src/controllers/engine/index.ts
@@ -0,0 +1,7 @@
+import { LLM, SimpleChatEngine } from "llamaindex";
+
+export async function createChatEngine(llm: LLM) {
+  return new SimpleChatEngine({
+    llm,
+  });
+}
diff --git a/packages/create-llama/templates/types/simple/express/src/routes/chat.route.ts b/packages/create-llama/templates/types/simple/express/src/routes/chat.route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bdfeb08534b9a2c987c4d23a4bb5c6df50075908
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/src/routes/chat.route.ts
@@ -0,0 +1,8 @@
+import express from "express";
+import { chat } from "../controllers/chat.controller";
+
+const llmRouter = express.Router();
+
+llmRouter.route("/").post(chat);
+
+export default llmRouter;
diff --git a/packages/create-llama/templates/types/simple/express/tsconfig.json b/packages/create-llama/templates/types/simple/express/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..e886da1ef324ab02d14102f3a4a36c8f90f70eef
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/express/tsconfig.json
@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "target": "es2016",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "skipLibCheck": true,
+    "moduleResolution": "node"
+  }
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/types/simple/fastapi/README-template.md b/packages/create-llama/templates/types/simple/fastapi/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..f0b92bdfce648a374bd2723fee4ceec69605db69
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/fastapi/README-template.md
@@ -0,0 +1,42 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project using [FastAPI](https://fastapi.tiangolo.com/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, setup the environment:
+
+```
+poetry install
+poetry shell
+```
+
+Second, run the development server:
+
+```
+python main.py
+```
+
+Then call the API endpoint `/api/chat` to see the result:
+
+```
+curl --location 'localhost:8000/api/chat' \
+--header 'Content-Type: application/json' \
+--data '{ "messages": [{ "role": "user", "content": "Hello" }] }'
+```
+
+You can start editing the API by modifying `app/api/routers/chat.py`. The endpoint auto-updates as you save the file.
+
+Open [http://localhost:8000/docs](http://localhost:8000/docs) with your browser to see the Swagger UI of the API.
+
+The API allows CORS for all origins to simplify development. You can change this behavior by setting the `ENVIRONMENT` environment variable to `prod`:
+
+```
+ENVIRONMENT=prod uvicorn main:app
+```
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex.
+
+You can check out [the LlamaIndex GitHub repository](https://github.com/run-llama/llama_index) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/types/simple/fastapi/app/__init__.py b/packages/create-llama/templates/types/simple/fastapi/app/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/simple/fastapi/app/api/__init__.py b/packages/create-llama/templates/types/simple/fastapi/app/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/simple/fastapi/app/api/routers/__init__.py b/packages/create-llama/templates/types/simple/fastapi/app/api/routers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/simple/fastapi/app/api/routers/chat.py b/packages/create-llama/templates/types/simple/fastapi/app/api/routers/chat.py
new file mode 100644
index 0000000000000000000000000000000000000000..81f602edbeae66c5850b30a6183c009ab4b1e014
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/fastapi/app/api/routers/chat.py
@@ -0,0 +1,56 @@
+from typing import List
+
+from app.utils.index import get_index
+from fastapi import APIRouter, Depends, HTTPException, status
+from llama_index import VectorStoreIndex
+from llama_index.llms.base import MessageRole, ChatMessage
+from pydantic import BaseModel
+
+chat_router = r = APIRouter()
+
+
+class _Message(BaseModel):
+    role: MessageRole
+    content: str
+
+
+class _ChatData(BaseModel):
+    messages: List[_Message]
+
+
+class _Result(BaseModel):
+    result: _Message
+
+
+@r.post("")
+async def chat(
+    data: _ChatData,
+    index: VectorStoreIndex = Depends(get_index),
+) -> _Result:
+    # check preconditions and get last message
+    if len(data.messages) == 0:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="No messages provided",
+        )
+    lastMessage = data.messages.pop()
+    if lastMessage.role != MessageRole.USER:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Last message must be from user",
+        )
+    # convert messages coming from the request to type ChatMessage
+    messages = [
+        ChatMessage(
+            role=m.role,
+            content=m.content,
+        )
+        for m in data.messages
+    ]
+
+    # query chat engine
+    chat_engine = index.as_chat_engine()
+    response = chat_engine.chat(lastMessage.content, messages)
+    return _Result(
+        result=_Message(role=MessageRole.ASSISTANT, content=response.response)
+    )
diff --git a/packages/create-llama/templates/types/simple/fastapi/app/utils/__init__.py b/packages/create-llama/templates/types/simple/fastapi/app/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/simple/fastapi/app/utils/index.py b/packages/create-llama/templates/types/simple/fastapi/app/utils/index.py
new file mode 100644
index 0000000000000000000000000000000000000000..076ca76631a6e0f752a420f0bc3f90286029796a
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/fastapi/app/utils/index.py
@@ -0,0 +1,33 @@
+import logging
+import os
+
+from llama_index import (
+    SimpleDirectoryReader,
+    StorageContext,
+    VectorStoreIndex,
+    load_index_from_storage,
+)
+
+
+STORAGE_DIR = "./storage"  # directory to cache the generated index
+DATA_DIR = "./data"  # directory containing the documents to index
+
+
+def get_index():
+    logger = logging.getLogger("uvicorn")
+    # check if storage already exists
+    if not os.path.exists(STORAGE_DIR):
+        logger.info("Creating new index")
+        # load the documents and create the index
+        documents = SimpleDirectoryReader(DATA_DIR).load_data()
+        index = VectorStoreIndex.from_documents(documents)
+        # store it for later
+        index.storage_context.persist(STORAGE_DIR)
+        logger.info(f"Finished creating new index. Stored in {STORAGE_DIR}")
+    else:
+        # load the existing index
+        logger.info(f"Loading index from {STORAGE_DIR}...")
+        storage_context = StorageContext.from_defaults(persist_dir=STORAGE_DIR)
+        index = load_index_from_storage(storage_context)
+        logger.info(f"Finished loading index from {STORAGE_DIR}")
+    return index
diff --git a/packages/create-llama/templates/types/simple/fastapi/gitignore b/packages/create-llama/templates/types/simple/fastapi/gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..069fcb4020566da83dbc398c06dce5bbe92aface
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/fastapi/gitignore
@@ -0,0 +1,2 @@
+__pycache__
+storage
diff --git a/packages/create-llama/templates/types/simple/fastapi/main.py b/packages/create-llama/templates/types/simple/fastapi/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9dc1a0afb6634f319538e8545bdd647693dffbfb
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/fastapi/main.py
@@ -0,0 +1,31 @@
+import logging
+import os
+import uvicorn
+from app.api.routers.chat import chat_router
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from dotenv import load_dotenv
+
+load_dotenv()
+
+app = FastAPI()
+
+environment = os.getenv("ENVIRONMENT", "dev")  # Default to 'development' if not set
+
+
+if environment == "dev":
+    logger = logging.getLogger("uvicorn")
+    logger.warning("Running in development mode - allowing CORS for all origins")
+    app.add_middleware(
+        CORSMiddleware,
+        allow_origins=["*"],
+        allow_credentials=True,
+        allow_methods=["*"],
+        allow_headers=["*"],
+    )
+
+app.include_router(chat_router, prefix="/api/chat")
+
+
+if __name__ == "__main__":
+    uvicorn.run(app="main:app", host="0.0.0.0", reload=True)
diff --git a/packages/create-llama/templates/types/simple/fastapi/pyproject.toml b/packages/create-llama/templates/types/simple/fastapi/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..59d182bbb47a8d0ee06de3550f2c7fbf954a3901
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/fastapi/pyproject.toml
@@ -0,0 +1,19 @@
+[tool.poetry]
+name = "llamaindex-fastapi"
+version = "0.1.0"
+description = ""
+authors = ["Marcus Schiesser <mail@marcusschiesser.de>"]
+readme = "README.md"
+
+[tool.poetry.dependencies]
+python = "^3.11,<3.12"
+fastapi = "^0.104.1"
+uvicorn = { extras = ["standard"], version = "^0.23.2" }
+llama-index = "^0.8.56"
+pypdf = "^3.17.0"
+python-dotenv = "^1.0.0"
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/packages/create-llama/templates/types/simple/fastapi/tests/__init__.py b/packages/create-llama/templates/types/simple/fastapi/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/simple/nextjs/.env.example b/packages/create-llama/templates/types/simple/nextjs/.env.example
new file mode 100644
index 0000000000000000000000000000000000000000..7ac0a01551a65a68003c2615d510269b5d6a77f6
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/.env.example
@@ -0,0 +1,3 @@
+# Rename this file to `.env.local` to use environment variables locally with `next dev`
+# https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables
+MY_HOST="example.com"
diff --git a/packages/create-llama/templates/types/simple/nextjs/README-template.md b/packages/create-llama/templates/types/simple/nextjs/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..1509ded7c3be489d369b94d6d6a286d496f488d8
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/README-template.md
@@ -0,0 +1,30 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, install the dependencies:
+
+```
+npm install
+```
+
+Second, run the development server:
+
+```
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
+- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
+
+You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/api/chat/engine/index.ts b/packages/create-llama/templates/types/simple/nextjs/app/api/chat/engine/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abb02e90cd2ce91096791bf10c4665afcbe11d38
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/api/chat/engine/index.ts
@@ -0,0 +1,7 @@
+import { LLM, SimpleChatEngine } from "llamaindex";
+
+export async function createChatEngine(llm: LLM) {
+  return new SimpleChatEngine({
+    llm,
+  });
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/api/chat/route.ts b/packages/create-llama/templates/types/simple/nextjs/app/api/chat/route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..097341ab43922058b9d06b53c215feb16a2a7e23
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/api/chat/route.ts
@@ -0,0 +1,47 @@
+import { ChatMessage, OpenAI } from "llamaindex";
+import { NextRequest, NextResponse } from "next/server";
+import { createChatEngine } from "./engine";
+
+export const runtime = "nodejs";
+export const dynamic = "force-dynamic";
+
+export async function POST(request: NextRequest) {
+  try {
+    const body = await request.json();
+    const { messages }: { messages: ChatMessage[] } = body;
+    const lastMessage = messages.pop();
+    if (!messages || !lastMessage || lastMessage.role !== "user") {
+      return NextResponse.json(
+        {
+          error:
+            "messages are required in the request body and the last message must be from the user",
+        },
+        { status: 400 },
+      );
+    }
+
+    const llm = new OpenAI({
+      model: "gpt-3.5-turbo",
+    });
+
+    const chatEngine = await createChatEngine(llm);
+
+    const response = await chatEngine.chat(lastMessage.content, messages);
+    const result: ChatMessage = {
+      role: "assistant",
+      content: response.response,
+    };
+
+    return NextResponse.json({ result });
+  } catch (error) {
+    console.error("[LlamaIndex]", error);
+    return NextResponse.json(
+      {
+        error: (error as Error).message,
+      },
+      {
+        status: 500,
+      },
+    );
+  }
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/chat-section.tsx b/packages/create-llama/templates/types/simple/nextjs/app/components/chat-section.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..133a0a884bb82fb1ed3c78553852b8ae9b169cd5
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/chat-section.tsx
@@ -0,0 +1,80 @@
+"use client";
+
+import { nanoid } from "nanoid";
+import { useState } from "react";
+import { ChatInput, ChatInputProps, ChatMessages, Message } from "./ui/chat";
+
+function useChat(): ChatInputProps & { messages: Message[] } {
+  const [messages, setMessages] = useState<Message[]>([]);
+  const [isLoading, setIsLoading] = useState(false);
+  const [input, setInput] = useState("");
+
+  const getAssistantMessage = async (messages: Message[]) => {
+    const response = await fetch(
+      process.env.NEXT_PUBLIC_CHAT_API ?? "/api/chat",
+      {
+        method: "POST",
+        headers: {
+          "Content-Type": "application/json",
+        },
+        body: JSON.stringify({
+          messages,
+        }),
+      },
+    );
+    const data = await response.json();
+    const assistantMessage = data.result as Message;
+    return assistantMessage;
+  };
+
+  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
+    e.preventDefault();
+    if (!input) return;
+
+    setIsLoading(true);
+
+    try {
+      const newMessages = [
+        ...messages,
+        { id: nanoid(), content: input, role: "user" },
+      ];
+      setMessages(newMessages);
+      setInput("");
+      const assistantMessage = await getAssistantMessage(newMessages);
+      setMessages([...newMessages, { ...assistantMessage, id: nanoid() }]);
+    } catch (error: any) {
+      console.log(error);
+      alert(error.message);
+    } finally {
+      setIsLoading(false);
+    }
+  };
+
+  const handleInputChange = (e: any): void => {
+    setInput(e.target.value);
+  };
+
+  return {
+    messages,
+    isLoading,
+    input,
+    handleSubmit,
+    handleInputChange,
+  };
+}
+
+export default function ChatSection() {
+  const { messages, isLoading, input, handleSubmit, handleInputChange } =
+    useChat();
+  return (
+    <div className="space-y-4 max-w-5xl w-full">
+      <ChatMessages messages={messages} />
+      <ChatInput
+        handleSubmit={handleSubmit}
+        isLoading={isLoading}
+        input={input}
+        handleInputChange={handleInputChange}
+      />
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/header.tsx b/packages/create-llama/templates/types/simple/nextjs/app/components/header.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2b0e488f769eff6700a282c2a6a77dd8d0a4dac8
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/header.tsx
@@ -0,0 +1,28 @@
+import Image from "next/image";
+
+export default function Header() {
+  return (
+    <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
+      <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto  lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
+        Get started by editing&nbsp;
+        <code className="font-mono font-bold">app/page.tsx</code>
+      </p>
+      <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
+        <a
+          href="https://www.llamaindex.ai/"
+          className="flex items-center justify-center font-nunito text-lg font-bold gap-2"
+        >
+          <span>Built by LlamaIndex</span>
+          <Image
+            className="rounded-xl"
+            src="/llama.png"
+            alt="Llama Logo"
+            width={40}
+            height={40}
+            priority
+          />
+        </a>
+      </div>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-avatar.tsx b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-avatar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cd241104e4ef210c728aec47a1ab8b0161ad6538
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-avatar.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import Image from "next/image";
+import { Message } from "./chat-messages";
+
+export default function ChatAvatar(message: Message) {
+  if (message.role === "user") {
+    return (
+      <div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow bg-background">
+        <svg
+          xmlns="http://www.w3.org/2000/svg"
+          viewBox="0 0 256 256"
+          fill="currentColor"
+          className="h-4 w-4"
+        >
+          <path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8ZM72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56Z"></path>
+        </svg>
+      </div>
+    );
+  }
+
+  return (
+    <div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border  bg-black text-white">
+      <Image
+        className="rounded-md"
+        src="/llama.png"
+        alt="Llama Logo"
+        width={24}
+        height={24}
+        priority
+      />
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-input.tsx b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-input.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3eb979b02735943f3f11290c78b84f0e37709438
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-input.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+export interface ChatInputProps {
+  /** The current value of the input */
+  input?: string;
+  /** An input/textarea-ready onChange handler to control the value of the input */
+  handleInputChange?: (
+    e:
+      | React.ChangeEvent<HTMLInputElement>
+      | React.ChangeEvent<HTMLTextAreaElement>,
+  ) => void;
+  /** Form submission handler to automatically reset input and append a user message  */
+  handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
+  isLoading: boolean;
+}
+
+export default function ChatInput(props: ChatInputProps) {
+  return (
+    <>
+      <form
+        onSubmit={props.handleSubmit}
+        className="flex items-start justify-between w-full max-w-5xl p-4 bg-white rounded-xl shadow-xl gap-4"
+      >
+        <input
+          autoFocus
+          name="message"
+          placeholder="Type a message"
+          className="w-full p-4 rounded-xl shadow-inner flex-1"
+          value={props.input}
+          onChange={props.handleInputChange}
+        />
+        <button
+          disabled={props.isLoading}
+          type="submit"
+          className="p-4 text-white rounded-xl shadow-xl bg-gradient-to-r from-cyan-500 to-sky-500 disabled:opacity-50 disabled:cursor-not-allowed"
+        >
+          Send message
+        </button>
+      </form>
+    </>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-item.tsx b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-item.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2244f729a8ba668121ab5ec0842963d22153ef92
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-item.tsx
@@ -0,0 +1,13 @@
+"use client";
+
+import ChatAvatar from "./chat-avatar";
+import { Message } from "./chat-messages";
+
+export default function ChatItem(message: Message) {
+  return (
+    <div className="flex items-start gap-4 pt-5">
+      <ChatAvatar {...message} />
+      <p className="break-words">{message.content}</p>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-messages.tsx b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-messages.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..65eacabbfb1b7bc99950b43e7fd07ba56b3ecdb0
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/chat-messages.tsx
@@ -0,0 +1,38 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import ChatItem from "./chat-item";
+
+export interface Message {
+  id: string;
+  content: string;
+  role: string;
+}
+
+export default function ChatMessages({ messages }: { messages: Message[] }) {
+  const scrollableChatContainerRef = useRef<HTMLDivElement>(null);
+
+  const scrollToBottom = () => {
+    if (scrollableChatContainerRef.current) {
+      scrollableChatContainerRef.current.scrollTop =
+        scrollableChatContainerRef.current.scrollHeight;
+    }
+  };
+
+  useEffect(() => {
+    scrollToBottom();
+  }, [messages.length]);
+
+  return (
+    <div className="w-full max-w-5xl p-4 bg-white rounded-xl shadow-xl">
+      <div
+        className="flex flex-col gap-5 divide-y h-[50vh] overflow-auto"
+        ref={scrollableChatContainerRef}
+      >
+        {messages.map((m: Message) => (
+          <ChatItem key={m.id} {...m} />
+        ))}
+      </div>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/index.ts b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ccc54926fbd450313f952747bc1ea720100f2df
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/components/ui/chat/index.ts
@@ -0,0 +1,6 @@
+import ChatInput from "./chat-input";
+import ChatMessages from "./chat-messages";
+
+export type { ChatInputProps } from "./chat-input";
+export type { Message } from "./chat-messages";
+export { ChatMessages, ChatInput };
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/favicon.ico b/packages/create-llama/templates/types/simple/nextjs/app/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..a1eaef62f2dfa895f1bbffc6595bb53d9604963e
Binary files /dev/null and b/packages/create-llama/templates/types/simple/nextjs/app/favicon.ico differ
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/globals.css b/packages/create-llama/templates/types/simple/nextjs/app/globals.css
new file mode 100644
index 0000000000000000000000000000000000000000..09b85ed2c912e25518ddebbfebaba69090f889f4
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/globals.css
@@ -0,0 +1,94 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+  :root {
+    --background: 0 0% 100%;
+    --foreground: 222.2 47.4% 11.2%;
+
+    --muted: 210 40% 96.1%;
+    --muted-foreground: 215.4 16.3% 46.9%;
+
+    --popover: 0 0% 100%;
+    --popover-foreground: 222.2 47.4% 11.2%;
+
+    --border: 214.3 31.8% 91.4%;
+    --input: 214.3 31.8% 91.4%;
+
+    --card: 0 0% 100%;
+    --card-foreground: 222.2 47.4% 11.2%;
+
+    --primary: 222.2 47.4% 11.2%;
+    --primary-foreground: 210 40% 98%;
+
+    --secondary: 210 40% 96.1%;
+    --secondary-foreground: 222.2 47.4% 11.2%;
+
+    --accent: 210 40% 96.1%;
+    --accent-foreground: 222.2 47.4% 11.2%;
+
+    --destructive: 0 100% 50%;
+    --destructive-foreground: 210 40% 98%;
+
+    --ring: 215 20.2% 65.1%;
+
+    --radius: 0.5rem;
+  }
+
+  .dark {
+    --background: 224 71% 4%;
+    --foreground: 213 31% 91%;
+
+    --muted: 223 47% 11%;
+    --muted-foreground: 215.4 16.3% 56.9%;
+
+    --accent: 216 34% 17%;
+    --accent-foreground: 210 40% 98%;
+
+    --popover: 224 71% 4%;
+    --popover-foreground: 215 20.2% 65.1%;
+
+    --border: 216 34% 17%;
+    --input: 216 34% 17%;
+
+    --card: 224 71% 4%;
+    --card-foreground: 213 31% 91%;
+
+    --primary: 210 40% 98%;
+    --primary-foreground: 222.2 47.4% 1.2%;
+
+    --secondary: 222.2 47.4% 11.2%;
+    --secondary-foreground: 210 40% 98%;
+
+    --destructive: 0 63% 31%;
+    --destructive-foreground: 210 40% 98%;
+
+    --ring: 216 34% 17%;
+
+    --radius: 0.5rem;
+  }
+}
+
+@layer base {
+  * {
+    @apply border-border;
+  }
+  body {
+    @apply bg-background text-foreground;
+    font-feature-settings:
+      "rlig" 1,
+      "calt" 1;
+  }
+  .background-gradient {
+    background-color: #fff;
+    background-image: radial-gradient(
+        at 21% 11%,
+        rgba(186, 186, 233, 0.53) 0,
+        transparent 50%
+      ),
+      radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%),
+      radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%),
+      radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%);
+  }
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/layout.tsx b/packages/create-llama/templates/types/simple/nextjs/app/layout.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fb097706274bdfe4690e4953bba24d7acea0a021
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/layout.tsx
@@ -0,0 +1,22 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+  title: "Create Llama App",
+  description: "Generated by create-llama",
+};
+
+export default function RootLayout({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  return (
+    <html lang="en">
+      <body className={inter.className}>{children}</body>
+    </html>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/app/page.tsx b/packages/create-llama/templates/types/simple/nextjs/app/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..31f51facb248687cf7e7c6b34493778634a8c59c
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/app/page.tsx
@@ -0,0 +1,11 @@
+import ChatSection from "@/app/components/chat-section";
+import Header from "@/app/components/header";
+
+export default function Home() {
+  return (
+    <main className="flex min-h-screen flex-col items-center gap-10 p-24 background-gradient">
+      <Header />
+      <ChatSection />
+    </main>
+  );
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/eslintrc.json b/packages/create-llama/templates/types/simple/nextjs/eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..bffb357a7122523ec94045523758c4b825b448ef
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/eslintrc.json
@@ -0,0 +1,3 @@
+{
+  "extends": "next/core-web-vitals"
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/gitignore b/packages/create-llama/templates/types/simple/nextjs/gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8f322f0d8f49570a594b865ef8916c428a01afc1
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/gitignore
@@ -0,0 +1,35 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/packages/create-llama/templates/types/simple/nextjs/next-env.d.ts b/packages/create-llama/templates/types/simple/nextjs/next-env.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f11a03dc6cc37f2b5105c08f2e7b24c603ab2f4
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/next-env.d.ts
@@ -0,0 +1,5 @@
+/// <reference types="next" />
+/// <reference types="next/image-types/global" />
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/packages/create-llama/templates/types/simple/nextjs/next.config.js b/packages/create-llama/templates/types/simple/nextjs/next.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..0b2c2bf17365d895255e161ffaf5714689d2d496
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/next.config.js
@@ -0,0 +1,8 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+  experimental: {
+		serverComponentsExternalPackages: ["llamaindex"],
+	},
+}
+
+module.exports = nextConfig
diff --git a/packages/create-llama/templates/types/simple/nextjs/package.json b/packages/create-llama/templates/types/simple/nextjs/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..990b41c832c69b81883c88b2d078c55bef15b42c
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/package.json
@@ -0,0 +1,28 @@
+{
+  "name": "llama-index-nextjs",
+  "version": "1.0.0",
+  "scripts": {
+    "dev": "next dev",
+    "build": "next build",
+    "start": "next start",
+    "lint": "next lint"
+  },
+  "dependencies": {
+    "llamaindex": "0.0.31",
+    "nanoid": "^5",
+    "next": "^13",
+    "react": "^18",
+    "react-dom": "^18"
+  },
+  "devDependencies": {
+    "@types/node": "^20",
+    "@types/react": "^18",
+    "@types/react-dom": "^18",
+    "autoprefixer": "^10",
+    "eslint": "^8",
+    "eslint-config-next": "^13",
+    "postcss": "^8",
+    "tailwindcss": "^3",
+    "typescript": "^5"
+  }
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/types/simple/nextjs/postcss.config.js b/packages/create-llama/templates/types/simple/nextjs/postcss.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..33ad091d26d8a9dc95ebdf616e217d985ec215b8
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+}
diff --git a/packages/create-llama/templates/types/simple/nextjs/public/llama.png b/packages/create-llama/templates/types/simple/nextjs/public/llama.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4efba3b816bf765439c6d01b322b02684e946c3
Binary files /dev/null and b/packages/create-llama/templates/types/simple/nextjs/public/llama.png differ
diff --git a/packages/create-llama/templates/types/simple/nextjs/tailwind.config.ts b/packages/create-llama/templates/types/simple/nextjs/tailwind.config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa5580affac868255fedb5a8ddc0dde7a105c454
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/tailwind.config.ts
@@ -0,0 +1,78 @@
+import type { Config } from "tailwindcss";
+import { fontFamily } from "tailwindcss/defaultTheme";
+
+const config: Config = {
+  darkMode: ["class"],
+  content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
+  theme: {
+    container: {
+      center: true,
+      padding: "2rem",
+      screens: {
+        "2xl": "1400px",
+      },
+    },
+    extend: {
+      colors: {
+        border: "hsl(var(--border))",
+        input: "hsl(var(--input))",
+        ring: "hsl(var(--ring))",
+        background: "hsl(var(--background))",
+        foreground: "hsl(var(--foreground))",
+        primary: {
+          DEFAULT: "hsl(var(--primary))",
+          foreground: "hsl(var(--primary-foreground))",
+        },
+        secondary: {
+          DEFAULT: "hsl(var(--secondary))",
+          foreground: "hsl(var(--secondary-foreground))",
+        },
+        destructive: {
+          DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
+          foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
+        },
+        muted: {
+          DEFAULT: "hsl(var(--muted))",
+          foreground: "hsl(var(--muted-foreground))",
+        },
+        accent: {
+          DEFAULT: "hsl(var(--accent))",
+          foreground: "hsl(var(--accent-foreground))",
+        },
+        popover: {
+          DEFAULT: "hsl(var(--popover))",
+          foreground: "hsl(var(--popover-foreground))",
+        },
+        card: {
+          DEFAULT: "hsl(var(--card))",
+          foreground: "hsl(var(--card-foreground))",
+        },
+      },
+      borderRadius: {
+        xl: `calc(var(--radius) + 4px)`,
+        lg: `var(--radius)`,
+        md: `calc(var(--radius) - 2px)`,
+        sm: "calc(var(--radius) - 4px)",
+      },
+      fontFamily: {
+        sans: ["var(--font-sans)", ...fontFamily.sans],
+      },
+      keyframes: {
+        "accordion-down": {
+          from: { height: "0" },
+          to: { height: "var(--radix-accordion-content-height)" },
+        },
+        "accordion-up": {
+          from: { height: "var(--radix-accordion-content-height)" },
+          to: { height: "0" },
+        },
+      },
+      animation: {
+        "accordion-down": "accordion-down 0.2s ease-out",
+        "accordion-up": "accordion-up 0.2s ease-out",
+      },
+    },
+  },
+  plugins: [],
+};
+export default config;
diff --git a/packages/create-llama/templates/types/simple/nextjs/tsconfig.json b/packages/create-llama/templates/types/simple/nextjs/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..c7146963787144d4861e149d8d233049b7daefc7
--- /dev/null
+++ b/packages/create-llama/templates/types/simple/nextjs/tsconfig.json
@@ -0,0 +1,27 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": ["dom", "dom.iterable", "esnext"],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "strict": true,
+    "noEmit": true,
+    "esModuleInterop": true,
+    "module": "esnext",
+    "moduleResolution": "bundler",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "jsx": "preserve",
+    "incremental": true,
+    "plugins": [
+      {
+        "name": "next"
+      }
+    ],
+    "paths": {
+      "@/*": ["./*"]
+    }
+  },
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+  "exclude": ["node_modules"]
+}
diff --git a/packages/create-llama/templates/types/streaming/express/README-template.md b/packages/create-llama/templates/types/streaming/express/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..7ea94ab755535aefd4d5edc9f20a5908d7fedead
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/README-template.md
@@ -0,0 +1,50 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Express](https://expressjs.com/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, install the dependencies:
+
+```
+npm install
+```
+
+Second, run the development server:
+
+```
+npm run dev
+```
+
+Then call the express API endpoint `/api/chat` to see the result:
+
+```
+curl --location 'localhost:8000/api/chat' \
+--header 'Content-Type: application/json' \
+--data '{ "messages": [{ "role": "user", "content": "Hello" }] }'
+```
+
+You can start editing the API by modifying `src/controllers/chat.controller.ts`. The endpoint auto-updates as you save the file.
+
+## Production
+
+First, build the project:
+
+```
+npm run build
+```
+
+You can then run the production server:
+
+```
+NODE_ENV=production npm run start
+```
+
+> Note that the `NODE_ENV` environment variable is set to `production`. This disables CORS for all origins.
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
+- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
+
+You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/types/streaming/express/eslintrc.json b/packages/create-llama/templates/types/streaming/express/eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..c19581799d886ce5be17def8ad268c2f4b175109
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/eslintrc.json
@@ -0,0 +1,3 @@
+{
+  "extends": "eslint:recommended"
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/types/streaming/express/index.ts b/packages/create-llama/templates/types/streaming/express/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..70a43ab580aff93df60948076464b9c35bb6f167
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/index.ts
@@ -0,0 +1,26 @@
+import cors from "cors";
+import "dotenv/config";
+import express, { Express, Request, Response } from "express";
+import chatRouter from "./src/routes/chat.route";
+
+const app: Express = express();
+const port = 8000;
+
+const env = process.env["NODE_ENV"];
+const isDevelopment = !env || env === "development";
+if (isDevelopment) {
+  console.warn("Running in development mode - allowing CORS for all origins");
+  app.use(cors());
+}
+
+app.use(express.json());
+
+app.get("/", (req: Request, res: Response) => {
+  res.send("LlamaIndex Express Server");
+});
+
+app.use("/api/chat", chatRouter);
+
+app.listen(port, () => {
+  console.log(`⚡️[server]: Server is running at http://localhost:${port}`);
+});
diff --git a/packages/create-llama/templates/types/streaming/express/package.json b/packages/create-llama/templates/types/streaming/express/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..72f127b407a8c7ce675bf4d634a42f57af78bb4c
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/package.json
@@ -0,0 +1,28 @@
+{
+  "name": "llama-index-express-streaming",
+  "version": "1.0.0",
+  "main": "dist/index.js",
+  "type": "module",
+  "scripts": {
+    "build": "tsup index.ts --format esm --dts",
+    "start": "node dist/index.js",
+    "dev": "concurrently \"tsup index.ts --format esm --dts --watch\" \"nodemon -q dist/index.js\""
+  },
+  "dependencies": {
+    "ai": "^2",
+    "cors": "^2.8.5",
+    "dotenv": "^16.3.1",
+    "express": "^4",
+    "llamaindex": "0.0.31"
+  },
+  "devDependencies": {
+    "@types/cors": "^2.8.16",
+    "@types/express": "^4",
+    "@types/node": "^20",
+    "concurrently": "^8",
+    "eslint": "^8",
+    "nodemon": "^3",
+    "tsup": "^7",
+    "typescript": "^5"
+  }
+}
diff --git a/packages/create-llama/templates/types/streaming/express/src/controllers/chat.controller.ts b/packages/create-llama/templates/types/streaming/express/src/controllers/chat.controller.ts
new file mode 100644
index 0000000000000000000000000000000000000000..162b5db74da1802749fe9168860bf0f5ff094a8c
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/src/controllers/chat.controller.ts
@@ -0,0 +1,36 @@
+import { streamToResponse } from "ai";
+import { NextFunction, Request, Response } from "express";
+import { ChatMessage, OpenAI } from "llamaindex";
+import { createChatEngine } from "./engine";
+import { LlamaIndexStream } from "./llamaindex-stream";
+
+export const chat = async (req: Request, res: Response, next: NextFunction) => {
+  try {
+    const { messages }: { messages: ChatMessage[] } = req.body;
+    const lastMessage = messages.pop();
+    if (!messages || !lastMessage || lastMessage.role !== "user") {
+      return res.status(400).json({
+        error:
+          "messages are required in the request body and the last message must be from the user",
+      });
+    }
+
+    const llm = new OpenAI({
+      model: "gpt-3.5-turbo",
+    });
+
+    const chatEngine = await createChatEngine(llm);
+
+    const response = await chatEngine.chat(lastMessage.content, messages, true);
+
+    // Transform the response into a readable stream
+    const stream = LlamaIndexStream(response);
+
+    streamToResponse(stream, res);
+  } catch (error) {
+    console.error("[LlamaIndex]", error);
+    return res.status(500).json({
+      error: (error as Error).message,
+    });
+  }
+};
diff --git a/packages/create-llama/templates/types/streaming/express/src/controllers/engine/index.ts b/packages/create-llama/templates/types/streaming/express/src/controllers/engine/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abb02e90cd2ce91096791bf10c4665afcbe11d38
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/src/controllers/engine/index.ts
@@ -0,0 +1,7 @@
+import { LLM, SimpleChatEngine } from "llamaindex";
+
+export async function createChatEngine(llm: LLM) {
+  return new SimpleChatEngine({
+    llm,
+  });
+}
diff --git a/packages/create-llama/templates/types/streaming/express/src/controllers/llamaindex-stream.ts b/packages/create-llama/templates/types/streaming/express/src/controllers/llamaindex-stream.ts
new file mode 100644
index 0000000000000000000000000000000000000000..12328de875d8a59a5501db7fbf2aa0d763a3dbaa
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/src/controllers/llamaindex-stream.ts
@@ -0,0 +1,35 @@
+import {
+  createCallbacksTransformer,
+  createStreamDataTransformer,
+  trimStartOfStreamHelper,
+  type AIStreamCallbacksAndOptions,
+} from "ai";
+
+function createParser(res: AsyncGenerator<any>) {
+  const trimStartOfStream = trimStartOfStreamHelper();
+  return new ReadableStream<string>({
+    async pull(controller): Promise<void> {
+      const { value, done } = await res.next();
+      if (done) {
+        controller.close();
+        return;
+      }
+
+      const text = trimStartOfStream(value ?? "");
+      if (text) {
+        controller.enqueue(text);
+      }
+    },
+  });
+}
+
+export function LlamaIndexStream(
+  res: AsyncGenerator<any>,
+  callbacks?: AIStreamCallbacksAndOptions,
+): ReadableStream {
+  return createParser(res)
+    .pipeThrough(createCallbacksTransformer(callbacks))
+    .pipeThrough(
+      createStreamDataTransformer(callbacks?.experimental_streamData),
+    );
+}
diff --git a/packages/create-llama/templates/types/streaming/express/src/routes/chat.route.ts b/packages/create-llama/templates/types/streaming/express/src/routes/chat.route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bdfeb08534b9a2c987c4d23a4bb5c6df50075908
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/src/routes/chat.route.ts
@@ -0,0 +1,8 @@
+import express from "express";
+import { chat } from "../controllers/chat.controller";
+
+const llmRouter = express.Router();
+
+llmRouter.route("/").post(chat);
+
+export default llmRouter;
diff --git a/packages/create-llama/templates/types/streaming/express/tsconfig.json b/packages/create-llama/templates/types/streaming/express/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..e886da1ef324ab02d14102f3a4a36c8f90f70eef
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/express/tsconfig.json
@@ -0,0 +1,10 @@
+{
+  "compilerOptions": {
+    "target": "es2016",
+    "esModuleInterop": true,
+    "forceConsistentCasingInFileNames": true,
+    "strict": true,
+    "skipLibCheck": true,
+    "moduleResolution": "node"
+  }
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/types/streaming/fastapi/README-template.md b/packages/create-llama/templates/types/streaming/fastapi/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..f0b92bdfce648a374bd2723fee4ceec69605db69
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/README-template.md
@@ -0,0 +1,42 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project using [FastAPI](https://fastapi.tiangolo.com/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, setup the environment:
+
+```
+poetry install
+poetry shell
+```
+
+Second, run the development server:
+
+```
+python main.py
+```
+
+Then call the API endpoint `/api/chat` to see the result:
+
+```
+curl --location 'localhost:8000/api/chat' \
+--header 'Content-Type: application/json' \
+--data '{ "messages": [{ "role": "user", "content": "Hello" }] }'
+```
+
+You can start editing the API by modifying `app/api/routers/chat.py`. The endpoint auto-updates as you save the file.
+
+Open [http://localhost:8000/docs](http://localhost:8000/docs) with your browser to see the Swagger UI of the API.
+
+The API allows CORS for all origins to simplify development. You can change this behavior by setting the `ENVIRONMENT` environment variable to `prod`:
+
+```
+ENVIRONMENT=prod uvicorn main:app
+```
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex.
+
+You can check out [the LlamaIndex GitHub repository](https://github.com/run-llama/llama_index) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/__init__.py b/packages/create-llama/templates/types/streaming/fastapi/app/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/api/__init__.py b/packages/create-llama/templates/types/streaming/fastapi/app/api/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/api/routers/__init__.py b/packages/create-llama/templates/types/streaming/fastapi/app/api/routers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/api/routers/chat.py b/packages/create-llama/templates/types/streaming/fastapi/app/api/routers/chat.py
new file mode 100644
index 0000000000000000000000000000000000000000..36b618e232a33fc882b3d4b42b885c29c64edd4e
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/app/api/routers/chat.py
@@ -0,0 +1,65 @@
+from typing import List
+
+from fastapi.responses import StreamingResponse
+
+from app.utils.json import json_to_model
+from app.utils.index import get_index
+from fastapi import APIRouter, Depends, HTTPException, Request, status
+from llama_index import VectorStoreIndex
+from llama_index.llms.base import MessageRole, ChatMessage
+from pydantic import BaseModel
+
+chat_router = r = APIRouter()
+
+
+class _Message(BaseModel):
+    role: MessageRole
+    content: str
+
+
+class _ChatData(BaseModel):
+    messages: List[_Message]
+
+
+@r.post("")
+async def chat(
+    request: Request,
+    # Note: To support clients sending a JSON object using content-type "text/plain",
+    # we need to use Depends(json_to_model(_ChatData)) here
+    data: _ChatData = Depends(json_to_model(_ChatData)),
+    index: VectorStoreIndex = Depends(get_index),
+):
+    # check preconditions and get last message
+    if len(data.messages) == 0:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="No messages provided",
+        )
+    lastMessage = data.messages.pop()
+    if lastMessage.role != MessageRole.USER:
+        raise HTTPException(
+            status_code=status.HTTP_400_BAD_REQUEST,
+            detail="Last message must be from user",
+        )
+    # convert messages coming from the request to type ChatMessage
+    messages = [
+        ChatMessage(
+            role=m.role,
+            content=m.content,
+        )
+        for m in data.messages
+    ]
+
+    # query chat engine
+    chat_engine = index.as_chat_engine()
+    response = chat_engine.stream_chat(lastMessage.content, messages)
+
+    # stream response
+    async def event_generator():
+        for token in response.response_gen:
+            # If client closes connection, stop sending events
+            if await request.is_disconnected():
+                break
+            yield token
+
+    return StreamingResponse(event_generator(), media_type="text/plain")
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/utils/__init__.py b/packages/create-llama/templates/types/streaming/fastapi/app/utils/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/utils/index.py b/packages/create-llama/templates/types/streaming/fastapi/app/utils/index.py
new file mode 100644
index 0000000000000000000000000000000000000000..076ca76631a6e0f752a420f0bc3f90286029796a
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/app/utils/index.py
@@ -0,0 +1,33 @@
+import logging
+import os
+
+from llama_index import (
+    SimpleDirectoryReader,
+    StorageContext,
+    VectorStoreIndex,
+    load_index_from_storage,
+)
+
+
+STORAGE_DIR = "./storage"  # directory to cache the generated index
+DATA_DIR = "./data"  # directory containing the documents to index
+
+
+def get_index():
+    logger = logging.getLogger("uvicorn")
+    # check if storage already exists
+    if not os.path.exists(STORAGE_DIR):
+        logger.info("Creating new index")
+        # load the documents and create the index
+        documents = SimpleDirectoryReader(DATA_DIR).load_data()
+        index = VectorStoreIndex.from_documents(documents)
+        # store it for later
+        index.storage_context.persist(STORAGE_DIR)
+        logger.info(f"Finished creating new index. Stored in {STORAGE_DIR}")
+    else:
+        # load the existing index
+        logger.info(f"Loading index from {STORAGE_DIR}...")
+        storage_context = StorageContext.from_defaults(persist_dir=STORAGE_DIR)
+        index = load_index_from_storage(storage_context)
+        logger.info(f"Finished loading index from {STORAGE_DIR}")
+    return index
diff --git a/packages/create-llama/templates/types/streaming/fastapi/app/utils/json.py b/packages/create-llama/templates/types/streaming/fastapi/app/utils/json.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9a847f53e107f665389f11ec005795e0fb8c5b3
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/app/utils/json.py
@@ -0,0 +1,22 @@
+import json
+from typing import TypeVar
+from fastapi import HTTPException, Request
+
+from pydantic import BaseModel, ValidationError
+
+
+T = TypeVar("T", bound=BaseModel)
+
+
+def json_to_model(cls: T):
+    async def get_json(request: Request) -> T:
+        body = await request.body()
+        try:
+            data_dict = json.loads(body.decode("utf-8"))
+            return cls(**data_dict)
+        except (json.JSONDecodeError, ValidationError) as e:
+            raise HTTPException(
+                status_code=400, detail=f"Could not decode JSON: {str(e)}"
+            )
+
+    return get_json
diff --git a/packages/create-llama/templates/types/streaming/fastapi/gitignore b/packages/create-llama/templates/types/streaming/fastapi/gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..069fcb4020566da83dbc398c06dce5bbe92aface
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/gitignore
@@ -0,0 +1,2 @@
+__pycache__
+storage
diff --git a/packages/create-llama/templates/types/streaming/fastapi/main.py b/packages/create-llama/templates/types/streaming/fastapi/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..9dc1a0afb6634f319538e8545bdd647693dffbfb
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/main.py
@@ -0,0 +1,31 @@
+import logging
+import os
+import uvicorn
+from app.api.routers.chat import chat_router
+from fastapi import FastAPI
+from fastapi.middleware.cors import CORSMiddleware
+from dotenv import load_dotenv
+
+load_dotenv()
+
+app = FastAPI()
+
+environment = os.getenv("ENVIRONMENT", "dev")  # Default to 'development' if not set
+
+
+if environment == "dev":
+    logger = logging.getLogger("uvicorn")
+    logger.warning("Running in development mode - allowing CORS for all origins")
+    app.add_middleware(
+        CORSMiddleware,
+        allow_origins=["*"],
+        allow_credentials=True,
+        allow_methods=["*"],
+        allow_headers=["*"],
+    )
+
+app.include_router(chat_router, prefix="/api/chat")
+
+
+if __name__ == "__main__":
+    uvicorn.run(app="main:app", host="0.0.0.0", reload=True)
diff --git a/packages/create-llama/templates/types/streaming/fastapi/pyproject.toml b/packages/create-llama/templates/types/streaming/fastapi/pyproject.toml
new file mode 100644
index 0000000000000000000000000000000000000000..f5b75b3cfdbe88001d4588faf27e07cec36212a9
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/fastapi/pyproject.toml
@@ -0,0 +1,19 @@
+[tool.poetry]
+name = "llamaindex-fastapi-streaming"
+version = "0.1.0"
+description = ""
+authors = ["Marcus Schiesser <mail@marcusschiesser.de>"]
+readme = "README.md"
+
+[tool.poetry.dependencies]
+python = "^3.11,<3.12"
+fastapi = "^0.104.1"
+uvicorn = { extras = ["standard"], version = "^0.23.2" }
+llama-index = "^0.8.56"
+pypdf = "^3.17.0"
+python-dotenv = "^1.0.0"
+
+
+[build-system]
+requires = ["poetry-core"]
+build-backend = "poetry.core.masonry.api"
diff --git a/packages/create-llama/templates/types/streaming/fastapi/tests/__init__.py b/packages/create-llama/templates/types/streaming/fastapi/tests/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/packages/create-llama/templates/types/streaming/nextjs/.env.example b/packages/create-llama/templates/types/streaming/nextjs/.env.example
new file mode 100644
index 0000000000000000000000000000000000000000..7ac0a01551a65a68003c2615d510269b5d6a77f6
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/.env.example
@@ -0,0 +1,3 @@
+# Rename this file to `.env.local` to use environment variables locally with `next dev`
+# https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables
+MY_HOST="example.com"
diff --git a/packages/create-llama/templates/types/streaming/nextjs/README-template.md b/packages/create-llama/templates/types/streaming/nextjs/README-template.md
new file mode 100644
index 0000000000000000000000000000000000000000..1509ded7c3be489d369b94d6d6a286d496f488d8
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/README-template.md
@@ -0,0 +1,30 @@
+This is a [LlamaIndex](https://www.llamaindex.ai/) project using [Next.js](https://nextjs.org/) bootstrapped with [`create-llama`](https://github.com/run-llama/LlamaIndexTS/tree/main/packages/create-llama).
+
+## Getting Started
+
+First, install the dependencies:
+
+```
+npm install
+```
+
+Second, run the development server:
+
+```
+npm run dev
+```
+
+Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
+
+You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
+
+This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.
+
+## Learn More
+
+To learn more about LlamaIndex, take a look at the following resources:
+
+- [LlamaIndex Documentation](https://docs.llamaindex.ai) - learn about LlamaIndex (Python features).
+- [LlamaIndexTS Documentation](https://ts.llamaindex.ai) - learn about LlamaIndex (Typescript features).
+
+You can check out [the LlamaIndexTS GitHub repository](https://github.com/run-llama/LlamaIndexTS) - your feedback and contributions are welcome!
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/engine/index.ts b/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/engine/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..abb02e90cd2ce91096791bf10c4665afcbe11d38
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/engine/index.ts
@@ -0,0 +1,7 @@
+import { LLM, SimpleChatEngine } from "llamaindex";
+
+export async function createChatEngine(llm: LLM) {
+  return new SimpleChatEngine({
+    llm,
+  });
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/llamaindex-stream.ts b/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/llamaindex-stream.ts
new file mode 100644
index 0000000000000000000000000000000000000000..12328de875d8a59a5501db7fbf2aa0d763a3dbaa
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/llamaindex-stream.ts
@@ -0,0 +1,35 @@
+import {
+  createCallbacksTransformer,
+  createStreamDataTransformer,
+  trimStartOfStreamHelper,
+  type AIStreamCallbacksAndOptions,
+} from "ai";
+
+function createParser(res: AsyncGenerator<any>) {
+  const trimStartOfStream = trimStartOfStreamHelper();
+  return new ReadableStream<string>({
+    async pull(controller): Promise<void> {
+      const { value, done } = await res.next();
+      if (done) {
+        controller.close();
+        return;
+      }
+
+      const text = trimStartOfStream(value ?? "");
+      if (text) {
+        controller.enqueue(text);
+      }
+    },
+  });
+}
+
+export function LlamaIndexStream(
+  res: AsyncGenerator<any>,
+  callbacks?: AIStreamCallbacksAndOptions,
+): ReadableStream {
+  return createParser(res)
+    .pipeThrough(createCallbacksTransformer(callbacks))
+    .pipeThrough(
+      createStreamDataTransformer(callbacks?.experimental_streamData),
+    );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/route.ts b/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/route.ts
new file mode 100644
index 0000000000000000000000000000000000000000..989a5fec484fae3b34060ed20b4c1d5f1e42773f
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/api/chat/route.ts
@@ -0,0 +1,49 @@
+import { Message, StreamingTextResponse } from "ai";
+import { OpenAI } from "llamaindex";
+import { NextRequest, NextResponse } from "next/server";
+import { createChatEngine } from "./engine";
+import { LlamaIndexStream } from "./llamaindex-stream";
+
+export const runtime = "nodejs";
+export const dynamic = "force-dynamic";
+
+export async function POST(request: NextRequest) {
+  try {
+    const body = await request.json();
+    const { messages }: { messages: Message[] } = body;
+    const lastMessage = messages.pop();
+    if (!messages || !lastMessage || lastMessage.role !== "user") {
+      return NextResponse.json(
+        {
+          error:
+            "messages are required in the request body and the last message must be from the user",
+        },
+        { status: 400 },
+      );
+    }
+
+    const llm = new OpenAI({
+      model: "gpt-3.5-turbo",
+    });
+
+    const chatEngine = await createChatEngine(llm);
+
+    const response = await chatEngine.chat(lastMessage.content, messages, true);
+
+    // Transform the response into a readable stream
+    const stream = LlamaIndexStream(response);
+
+    // Return a StreamingTextResponse, which can be consumed by the client
+    return new StreamingTextResponse(stream);
+  } catch (error) {
+    console.error("[LlamaIndex]", error);
+    return NextResponse.json(
+      {
+        error: (error as Error).message,
+      },
+      {
+        status: 500,
+      },
+    );
+  }
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/chat-section.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/components/chat-section.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..04098fcdf41a4e686459f0ce0d6f3ca311895a9e
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/chat-section.tsx
@@ -0,0 +1,33 @@
+"use client";
+
+import { useChat } from "ai/react";
+import { ChatInput, ChatMessages } from "./ui/chat";
+
+export default function ChatSection() {
+  const {
+    messages,
+    input,
+    isLoading,
+    handleSubmit,
+    handleInputChange,
+    reload,
+    stop,
+  } = useChat({ api: process.env.NEXT_PUBLIC_CHAT_API });
+
+  return (
+    <div className="space-y-4 max-w-5xl w-full">
+      <ChatMessages
+        messages={messages}
+        isLoading={isLoading}
+        reload={reload}
+        stop={stop}
+      />
+      <ChatInput
+        input={input}
+        handleSubmit={handleSubmit}
+        handleInputChange={handleInputChange}
+        isLoading={isLoading}
+      />
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/header.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/components/header.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2b0e488f769eff6700a282c2a6a77dd8d0a4dac8
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/header.tsx
@@ -0,0 +1,28 @@
+import Image from "next/image";
+
+export default function Header() {
+  return (
+    <div className="z-10 max-w-5xl w-full items-center justify-between font-mono text-sm lg:flex">
+      <p className="fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 bg-gradient-to-b from-zinc-200 pb-6 pt-8 backdrop-blur-2xl dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:static lg:w-auto  lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 lg:dark:bg-zinc-800/30">
+        Get started by editing&nbsp;
+        <code className="font-mono font-bold">app/page.tsx</code>
+      </p>
+      <div className="fixed bottom-0 left-0 flex h-48 w-full items-end justify-center bg-gradient-to-t from-white via-white dark:from-black dark:via-black lg:static lg:h-auto lg:w-auto lg:bg-none">
+        <a
+          href="https://www.llamaindex.ai/"
+          className="flex items-center justify-center font-nunito text-lg font-bold gap-2"
+        >
+          <span>Built by LlamaIndex</span>
+          <Image
+            className="rounded-xl"
+            src="/llama.png"
+            alt="Llama Logo"
+            width={40}
+            height={40}
+            priority
+          />
+        </a>
+      </div>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-avatar.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-avatar.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..cd241104e4ef210c728aec47a1ab8b0161ad6538
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-avatar.tsx
@@ -0,0 +1,34 @@
+"use client";
+
+import Image from "next/image";
+import { Message } from "./chat-messages";
+
+export default function ChatAvatar(message: Message) {
+  if (message.role === "user") {
+    return (
+      <div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow bg-background">
+        <svg
+          xmlns="http://www.w3.org/2000/svg"
+          viewBox="0 0 256 256"
+          fill="currentColor"
+          className="h-4 w-4"
+        >
+          <path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8ZM72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56Z"></path>
+        </svg>
+      </div>
+    );
+  }
+
+  return (
+    <div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border  bg-black text-white">
+      <Image
+        className="rounded-md"
+        src="/llama.png"
+        alt="Llama Logo"
+        width={24}
+        height={24}
+        priority
+      />
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-input.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-input.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..3eb979b02735943f3f11290c78b84f0e37709438
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-input.tsx
@@ -0,0 +1,42 @@
+"use client";
+
+export interface ChatInputProps {
+  /** The current value of the input */
+  input?: string;
+  /** An input/textarea-ready onChange handler to control the value of the input */
+  handleInputChange?: (
+    e:
+      | React.ChangeEvent<HTMLInputElement>
+      | React.ChangeEvent<HTMLTextAreaElement>,
+  ) => void;
+  /** Form submission handler to automatically reset input and append a user message  */
+  handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
+  isLoading: boolean;
+}
+
+export default function ChatInput(props: ChatInputProps) {
+  return (
+    <>
+      <form
+        onSubmit={props.handleSubmit}
+        className="flex items-start justify-between w-full max-w-5xl p-4 bg-white rounded-xl shadow-xl gap-4"
+      >
+        <input
+          autoFocus
+          name="message"
+          placeholder="Type a message"
+          className="w-full p-4 rounded-xl shadow-inner flex-1"
+          value={props.input}
+          onChange={props.handleInputChange}
+        />
+        <button
+          disabled={props.isLoading}
+          type="submit"
+          className="p-4 text-white rounded-xl shadow-xl bg-gradient-to-r from-cyan-500 to-sky-500 disabled:opacity-50 disabled:cursor-not-allowed"
+        >
+          Send message
+        </button>
+      </form>
+    </>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-item.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-item.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..2244f729a8ba668121ab5ec0842963d22153ef92
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-item.tsx
@@ -0,0 +1,13 @@
+"use client";
+
+import ChatAvatar from "./chat-avatar";
+import { Message } from "./chat-messages";
+
+export default function ChatItem(message: Message) {
+  return (
+    <div className="flex items-start gap-4 pt-5">
+      <ChatAvatar {...message} />
+      <p className="break-words">{message.content}</p>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-messages.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-messages.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..0e978394015bd985af40646e87fa6620e9001a2f
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/chat-messages.tsx
@@ -0,0 +1,48 @@
+"use client";
+
+import { useEffect, useRef } from "react";
+import ChatItem from "./chat-item";
+
+export interface Message {
+  id: string;
+  content: string;
+  role: string;
+}
+
+export default function ChatMessages({
+  messages,
+  isLoading,
+  reload,
+  stop,
+}: {
+  messages: Message[];
+  isLoading?: boolean;
+  stop?: () => void;
+  reload?: () => void;
+}) {
+  const scrollableChatContainerRef = useRef<HTMLDivElement>(null);
+
+  const scrollToBottom = () => {
+    if (scrollableChatContainerRef.current) {
+      scrollableChatContainerRef.current.scrollTop =
+        scrollableChatContainerRef.current.scrollHeight;
+    }
+  };
+
+  useEffect(() => {
+    scrollToBottom();
+  }, [messages.length]);
+
+  return (
+    <div className="w-full max-w-5xl p-4 bg-white rounded-xl shadow-xl">
+      <div
+        className="flex flex-col gap-5 divide-y h-[50vh] overflow-auto"
+        ref={scrollableChatContainerRef}
+      >
+        {messages.map((m: Message) => (
+          <ChatItem key={m.id} {...m} />
+        ))}
+      </div>
+    </div>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/index.ts b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4ccc54926fbd450313f952747bc1ea720100f2df
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/components/ui/chat/index.ts
@@ -0,0 +1,6 @@
+import ChatInput from "./chat-input";
+import ChatMessages from "./chat-messages";
+
+export type { ChatInputProps } from "./chat-input";
+export type { Message } from "./chat-messages";
+export { ChatMessages, ChatInput };
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/favicon.ico b/packages/create-llama/templates/types/streaming/nextjs/app/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..a1eaef62f2dfa895f1bbffc6595bb53d9604963e
Binary files /dev/null and b/packages/create-llama/templates/types/streaming/nextjs/app/favicon.ico differ
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/globals.css b/packages/create-llama/templates/types/streaming/nextjs/app/globals.css
new file mode 100644
index 0000000000000000000000000000000000000000..09b85ed2c912e25518ddebbfebaba69090f889f4
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/globals.css
@@ -0,0 +1,94 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+  :root {
+    --background: 0 0% 100%;
+    --foreground: 222.2 47.4% 11.2%;
+
+    --muted: 210 40% 96.1%;
+    --muted-foreground: 215.4 16.3% 46.9%;
+
+    --popover: 0 0% 100%;
+    --popover-foreground: 222.2 47.4% 11.2%;
+
+    --border: 214.3 31.8% 91.4%;
+    --input: 214.3 31.8% 91.4%;
+
+    --card: 0 0% 100%;
+    --card-foreground: 222.2 47.4% 11.2%;
+
+    --primary: 222.2 47.4% 11.2%;
+    --primary-foreground: 210 40% 98%;
+
+    --secondary: 210 40% 96.1%;
+    --secondary-foreground: 222.2 47.4% 11.2%;
+
+    --accent: 210 40% 96.1%;
+    --accent-foreground: 222.2 47.4% 11.2%;
+
+    --destructive: 0 100% 50%;
+    --destructive-foreground: 210 40% 98%;
+
+    --ring: 215 20.2% 65.1%;
+
+    --radius: 0.5rem;
+  }
+
+  .dark {
+    --background: 224 71% 4%;
+    --foreground: 213 31% 91%;
+
+    --muted: 223 47% 11%;
+    --muted-foreground: 215.4 16.3% 56.9%;
+
+    --accent: 216 34% 17%;
+    --accent-foreground: 210 40% 98%;
+
+    --popover: 224 71% 4%;
+    --popover-foreground: 215 20.2% 65.1%;
+
+    --border: 216 34% 17%;
+    --input: 216 34% 17%;
+
+    --card: 224 71% 4%;
+    --card-foreground: 213 31% 91%;
+
+    --primary: 210 40% 98%;
+    --primary-foreground: 222.2 47.4% 1.2%;
+
+    --secondary: 222.2 47.4% 11.2%;
+    --secondary-foreground: 210 40% 98%;
+
+    --destructive: 0 63% 31%;
+    --destructive-foreground: 210 40% 98%;
+
+    --ring: 216 34% 17%;
+
+    --radius: 0.5rem;
+  }
+}
+
+@layer base {
+  * {
+    @apply border-border;
+  }
+  body {
+    @apply bg-background text-foreground;
+    font-feature-settings:
+      "rlig" 1,
+      "calt" 1;
+  }
+  .background-gradient {
+    background-color: #fff;
+    background-image: radial-gradient(
+        at 21% 11%,
+        rgba(186, 186, 233, 0.53) 0,
+        transparent 50%
+      ),
+      radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%),
+      radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%),
+      radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%);
+  }
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/layout.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/layout.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..fb097706274bdfe4690e4953bba24d7acea0a021
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/layout.tsx
@@ -0,0 +1,22 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+  title: "Create Llama App",
+  description: "Generated by create-llama",
+};
+
+export default function RootLayout({
+  children,
+}: {
+  children: React.ReactNode;
+}) {
+  return (
+    <html lang="en">
+      <body className={inter.className}>{children}</body>
+    </html>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/app/page.tsx b/packages/create-llama/templates/types/streaming/nextjs/app/page.tsx
new file mode 100644
index 0000000000000000000000000000000000000000..ef00262b4a80049c70d66d1ceaced4afaabfd587
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/app/page.tsx
@@ -0,0 +1,11 @@
+import Header from "@/app/components/header";
+import ChatSection from "./components/chat-section";
+
+export default function Home() {
+  return (
+    <main className="flex min-h-screen flex-col items-center gap-10 p-24 background-gradient">
+      <Header />
+      <ChatSection />
+    </main>
+  );
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/eslintrc.json b/packages/create-llama/templates/types/streaming/nextjs/eslintrc.json
new file mode 100644
index 0000000000000000000000000000000000000000..bffb357a7122523ec94045523758c4b825b448ef
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/eslintrc.json
@@ -0,0 +1,3 @@
+{
+  "extends": "next/core-web-vitals"
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/gitignore b/packages/create-llama/templates/types/streaming/nextjs/gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..8f322f0d8f49570a594b865ef8916c428a01afc1
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/gitignore
@@ -0,0 +1,35 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/packages/create-llama/templates/types/streaming/nextjs/next-env.d.ts b/packages/create-llama/templates/types/streaming/nextjs/next-env.d.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4f11a03dc6cc37f2b5105c08f2e7b24c603ab2f4
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/next-env.d.ts
@@ -0,0 +1,5 @@
+/// <reference types="next" />
+/// <reference types="next/image-types/global" />
+
+// NOTE: This file should not be edited
+// see https://nextjs.org/docs/basic-features/typescript for more information.
diff --git a/packages/create-llama/templates/types/streaming/nextjs/next.config.js b/packages/create-llama/templates/types/streaming/nextjs/next.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..0b2c2bf17365d895255e161ffaf5714689d2d496
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/next.config.js
@@ -0,0 +1,8 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+  experimental: {
+		serverComponentsExternalPackages: ["llamaindex"],
+	},
+}
+
+module.exports = nextConfig
diff --git a/packages/create-llama/templates/types/streaming/nextjs/package.json b/packages/create-llama/templates/types/streaming/nextjs/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..e9f23201d893320cfdfcbd3fbac9ad48ebbb8d7c
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/package.json
@@ -0,0 +1,28 @@
+{
+  "name": "llama-index-nextjs-streaming",
+  "version": "1.0.0",
+  "scripts": {
+    "dev": "next dev",
+    "build": "next build",
+    "start": "next start",
+    "lint": "next lint"
+  },
+  "dependencies": {
+    "ai": "^2",
+    "llamaindex": "0.0.31",
+    "next": "^13",
+    "react": "^18",
+    "react-dom": "^18"
+  },
+  "devDependencies": {
+    "@types/node": "^20",
+    "@types/react": "^18",
+    "@types/react-dom": "^18",
+    "autoprefixer": "^10",
+    "eslint": "^8",
+    "eslint-config-next": "^13",
+    "postcss": "^8",
+    "tailwindcss": "^3",
+    "typescript": "^5"
+  }
+}
\ No newline at end of file
diff --git a/packages/create-llama/templates/types/streaming/nextjs/postcss.config.js b/packages/create-llama/templates/types/streaming/nextjs/postcss.config.js
new file mode 100644
index 0000000000000000000000000000000000000000..33ad091d26d8a9dc95ebdf616e217d985ec215b8
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/postcss.config.js
@@ -0,0 +1,6 @@
+module.exports = {
+  plugins: {
+    tailwindcss: {},
+    autoprefixer: {},
+  },
+}
diff --git a/packages/create-llama/templates/types/streaming/nextjs/public/llama.png b/packages/create-llama/templates/types/streaming/nextjs/public/llama.png
new file mode 100644
index 0000000000000000000000000000000000000000..d4efba3b816bf765439c6d01b322b02684e946c3
Binary files /dev/null and b/packages/create-llama/templates/types/streaming/nextjs/public/llama.png differ
diff --git a/packages/create-llama/templates/types/streaming/nextjs/tailwind.config.ts b/packages/create-llama/templates/types/streaming/nextjs/tailwind.config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa5580affac868255fedb5a8ddc0dde7a105c454
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/tailwind.config.ts
@@ -0,0 +1,78 @@
+import type { Config } from "tailwindcss";
+import { fontFamily } from "tailwindcss/defaultTheme";
+
+const config: Config = {
+  darkMode: ["class"],
+  content: ["app/**/*.{ts,tsx}", "components/**/*.{ts,tsx}"],
+  theme: {
+    container: {
+      center: true,
+      padding: "2rem",
+      screens: {
+        "2xl": "1400px",
+      },
+    },
+    extend: {
+      colors: {
+        border: "hsl(var(--border))",
+        input: "hsl(var(--input))",
+        ring: "hsl(var(--ring))",
+        background: "hsl(var(--background))",
+        foreground: "hsl(var(--foreground))",
+        primary: {
+          DEFAULT: "hsl(var(--primary))",
+          foreground: "hsl(var(--primary-foreground))",
+        },
+        secondary: {
+          DEFAULT: "hsl(var(--secondary))",
+          foreground: "hsl(var(--secondary-foreground))",
+        },
+        destructive: {
+          DEFAULT: "hsl(var(--destructive) / <alpha-value>)",
+          foreground: "hsl(var(--destructive-foreground) / <alpha-value>)",
+        },
+        muted: {
+          DEFAULT: "hsl(var(--muted))",
+          foreground: "hsl(var(--muted-foreground))",
+        },
+        accent: {
+          DEFAULT: "hsl(var(--accent))",
+          foreground: "hsl(var(--accent-foreground))",
+        },
+        popover: {
+          DEFAULT: "hsl(var(--popover))",
+          foreground: "hsl(var(--popover-foreground))",
+        },
+        card: {
+          DEFAULT: "hsl(var(--card))",
+          foreground: "hsl(var(--card-foreground))",
+        },
+      },
+      borderRadius: {
+        xl: `calc(var(--radius) + 4px)`,
+        lg: `var(--radius)`,
+        md: `calc(var(--radius) - 2px)`,
+        sm: "calc(var(--radius) - 4px)",
+      },
+      fontFamily: {
+        sans: ["var(--font-sans)", ...fontFamily.sans],
+      },
+      keyframes: {
+        "accordion-down": {
+          from: { height: "0" },
+          to: { height: "var(--radix-accordion-content-height)" },
+        },
+        "accordion-up": {
+          from: { height: "var(--radix-accordion-content-height)" },
+          to: { height: "0" },
+        },
+      },
+      animation: {
+        "accordion-down": "accordion-down 0.2s ease-out",
+        "accordion-up": "accordion-up 0.2s ease-out",
+      },
+    },
+  },
+  plugins: [],
+};
+export default config;
diff --git a/packages/create-llama/templates/types/streaming/nextjs/tsconfig.json b/packages/create-llama/templates/types/streaming/nextjs/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..c7146963787144d4861e149d8d233049b7daefc7
--- /dev/null
+++ b/packages/create-llama/templates/types/streaming/nextjs/tsconfig.json
@@ -0,0 +1,27 @@
+{
+  "compilerOptions": {
+    "target": "es5",
+    "lib": ["dom", "dom.iterable", "esnext"],
+    "allowJs": true,
+    "skipLibCheck": true,
+    "strict": true,
+    "noEmit": true,
+    "esModuleInterop": true,
+    "module": "esnext",
+    "moduleResolution": "bundler",
+    "resolveJsonModule": true,
+    "isolatedModules": true,
+    "jsx": "preserve",
+    "incremental": true,
+    "plugins": [
+      {
+        "name": "next"
+      }
+    ],
+    "paths": {
+      "@/*": ["./*"]
+    }
+  },
+  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+  "exclude": ["node_modules"]
+}
diff --git a/packages/create-llama/tsconfig.json b/packages/create-llama/tsconfig.json
new file mode 100644
index 0000000000000000000000000000000000000000..e4edad9e127c7de102ca7bdd6b243168a83bbc20
--- /dev/null
+++ b/packages/create-llama/tsconfig.json
@@ -0,0 +1,11 @@
+{
+  "compilerOptions": {
+    "target": "es2019",
+    "moduleResolution": "node",
+    "strict": true,
+    "resolveJsonModule": true,
+    "esModuleInterop": true,
+    "skipLibCheck": false
+  },
+  "exclude": ["templates", "dist"]
+}
diff --git a/packages/eslint-config-custom/index.js b/packages/eslint-config-custom/index.js
index a4d327e57c817abdb802dbc0de516b3d87401e4f..142321257509d7748ce9de170d9cae31406ba968 100644
--- a/packages/eslint-config-custom/index.js
+++ b/packages/eslint-config-custom/index.js
@@ -35,6 +35,10 @@ module.exports = {
 
           "NOTION_TOKEN",
           "MONGODB_URI",
+
+          "https_proxy",
+          "npm_config_user_agent",
+          "NEXT_PUBLIC_CHAT_API",
         ],
       },
     ],
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 3b0bcbb70cce50f042f69ad86883fcfc3df43344..1f72896b00b89f28dca9e66578760ecb51084a41 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -18,7 +18,7 @@ importers:
     devDependencies:
       '@turbo/gen':
         specifier: ^1.10.16
-        version: 1.10.16(@types/node@20.9.0)(typescript@5.2.2)
+        version: 1.10.16(@types/node@18.18.8)(typescript@5.2.2)
       '@types/jest':
         specifier: ^29.5.6
         version: 29.5.6
@@ -33,7 +33,7 @@ importers:
         version: 8.0.3
       jest:
         specifier: ^29.7.0
-        version: 29.7.0(@types/node@20.9.0)
+        version: 29.7.0(@types/node@18.18.8)
       prettier:
         specifier: ^3.0.3
         version: 3.0.3
@@ -203,6 +203,75 @@ importers:
         specifier: ^5.2.2
         version: 5.2.2
 
+  packages/create-llama:
+    devDependencies:
+      '@types/async-retry':
+        specifier: 1.4.2
+        version: 1.4.2
+      '@types/ci-info':
+        specifier: 2.0.0
+        version: 2.0.0
+      '@types/cross-spawn':
+        specifier: 6.0.0
+        version: 6.0.0
+      '@types/node':
+        specifier: ^20.2.5
+        version: 20.8.10
+      '@types/prompts':
+        specifier: 2.0.1
+        version: 2.0.1
+      '@types/tar':
+        specifier: 6.1.5
+        version: 6.1.5
+      '@types/validate-npm-package-name':
+        specifier: 3.0.0
+        version: 3.0.0
+      '@vercel/ncc':
+        specifier: 0.34.0
+        version: 0.34.0
+      async-retry:
+        specifier: 1.3.1
+        version: 1.3.1
+      async-sema:
+        specifier: 3.0.1
+        version: 3.0.1
+      ci-info:
+        specifier: watson/ci-info#f43f6a1cefff47fb361c88cf4b943fdbcaafe540
+        version: github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540
+      commander:
+        specifier: 2.20.0
+        version: 2.20.0
+      conf:
+        specifier: 10.2.0
+        version: 10.2.0
+      cross-spawn:
+        specifier: 7.0.3
+        version: 7.0.3
+      fast-glob:
+        specifier: 3.3.1
+        version: 3.3.1
+      got:
+        specifier: 10.7.0
+        version: 10.7.0
+      picocolors:
+        specifier: 1.0.0
+        version: 1.0.0
+      prompts:
+        specifier: 2.1.0
+        version: 2.1.0
+      tar:
+        specifier: 6.1.15
+        version: 6.1.15
+      terminal-link:
+        specifier: ^3.0.0
+        version: 3.0.0
+      update-check:
+        specifier: 1.5.4
+        version: 1.5.4
+      validate-npm-package-name:
+        specifier: 3.0.0
+        version: 3.0.0
+
   packages/eslint-config-custom:
     dependencies:
       eslint-config-next:
@@ -3127,7 +3196,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       chalk: 4.1.2
       jest-message-util: 29.7.0
       jest-util: 29.7.0
@@ -3148,14 +3217,14 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       ci-info: 3.9.0
       exit: 0.1.2
       graceful-fs: 4.2.11
       jest-changed-files: 29.7.0
-      jest-config: 29.7.0(@types/node@20.8.10)
+      jest-config: 29.7.0(@types/node@18.18.8)
       jest-haste-map: 29.7.0
       jest-message-util: 29.7.0
       jest-regex-util: 29.6.3
@@ -3183,7 +3252,7 @@ packages:
     dependencies:
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       jest-mock: 29.7.0
     dev: true
 
@@ -3210,7 +3279,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@sinonjs/fake-timers': 10.3.0
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       jest-message-util: 29.7.0
       jest-mock: 29.7.0
       jest-util: 29.7.0
@@ -3243,7 +3312,7 @@ packages:
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
       '@jridgewell/trace-mapping': 0.3.19
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       chalk: 4.1.2
       collect-v8-coverage: 1.0.2
       exit: 0.1.2
@@ -3330,7 +3399,7 @@ packages:
       '@jest/schemas': 29.6.3
       '@types/istanbul-lib-coverage': 2.0.4
       '@types/istanbul-reports': 3.0.2
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       '@types/yargs': 17.0.28
       chalk: 4.1.2
 
@@ -3621,6 +3690,11 @@ packages:
     engines: {node: '>=6'}
     dev: false
 
+  /@sindresorhus/is@2.1.1:
+    resolution: {integrity: sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==}
+    engines: {node: '>=10'}
+    dev: true
+
   /@sinonjs/commons@3.0.0:
     resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==}
     dependencies:
@@ -3808,6 +3882,13 @@ packages:
       defer-to-connect: 1.1.3
     dev: false
 
+  /@szmarczak/http-timer@4.0.6:
+    resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
+    engines: {node: '>=10'}
+    dependencies:
+      defer-to-connect: 2.0.1
+    dev: true
+
   /@tootallnate/quickjs-emscripten@0.23.0:
     resolution: {integrity: sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==}
     dev: true
@@ -3837,7 +3918,7 @@ packages:
     resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==}
     dev: true
 
-  /@turbo/gen@1.10.16(@types/node@20.9.0)(typescript@5.2.2):
+  /@turbo/gen@1.10.16(@types/node@18.18.8)(typescript@5.2.2):
     resolution: {integrity: sha512-PzyluADjVuy5OcIi+/aRcD70OElQpRVRDdfZ9fH8G5Fv75lQcNrjd1bBGKmhjSw+g+eTEkXMGnY7s6gsCYjYTQ==}
     hasBin: true
     dependencies:
@@ -3849,7 +3930,7 @@ packages:
       minimatch: 9.0.3
       node-plop: 0.26.3
       proxy-agent: 6.3.1
-      ts-node: 10.9.1(@types/node@20.9.0)(typescript@5.2.2)
+      ts-node: 10.9.1(@types/node@18.18.8)(typescript@5.2.2)
       update-check: 1.5.4
       validate-npm-package-name: 5.0.0
     transitivePeerDependencies:
@@ -3878,6 +3959,12 @@ packages:
       update-check: 1.5.4
     dev: true
 
+  /@types/async-retry@1.4.2:
+    resolution: {integrity: sha512-GUDuJURF0YiJZ+CBjNQA0+vbP/VHlJbB0sFqkzsV7EcOPRfurVonXpXKAt3w8qIjM1TEzpz6hc6POocPvHOS3w==}
+    dependencies:
+      '@types/retry': 0.12.0
+    dev: true
+
   /@types/babel__core@7.20.2:
     resolution: {integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==}
     dependencies:
@@ -3911,28 +3998,47 @@ packages:
     resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==}
     dependencies:
       '@types/connect': 3.4.36
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/bonjour@3.5.11:
     resolution: {integrity: sha512-isGhjmBtLIxdHBDl2xGwUzEM8AOyOvWsADWq7rqirdi/ZQoHnLWErHvsThcEzTX8juDRiZtzp2Qkv5bgNh6mAg==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
+  /@types/cacheable-request@6.0.3:
+    resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==}
+    dependencies:
+      '@types/http-cache-semantics': 4.0.3
+      '@types/keyv': 3.1.4
+      '@types/node': 18.18.8
+      '@types/responselike': 1.0.1
+    dev: true
+
+  /@types/ci-info@2.0.0:
+    resolution: {integrity: sha512-5R2/MHILQLDCzTuhs1j4Qqq8AaKUf7Ma4KSSkCtc12+fMs47zfa34qhto9goxpyX00tQK1zxB885VCiawZ5Qhg==}
+    dev: true
+
   /@types/connect-history-api-fallback@1.5.1:
     resolution: {integrity: sha512-iaQslNbARe8fctL5Lk+DsmgWOM83lM+7FzP0eQUJs1jd3kBE8NWqBTIT2S8SqQOJjxvt2eyIjpOuYeRXq2AdMw==}
     dependencies:
       '@types/express-serve-static-core': 4.17.37
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/connect@3.4.36:
     resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
+  /@types/cross-spawn@6.0.0:
+    resolution: {integrity: sha512-evp2ZGsFw9YKprDbg8ySgC9NA15g3YgiI8ANkGmKKvvi0P2aDGYLPxQIC5qfeKNUOe3TjABVGuah6omPRpIYhg==}
+    dependencies:
+      '@types/node': 18.18.8
+    dev: true
+
   /@types/eslint-scope@3.7.5:
     resolution: {integrity: sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==}
     dependencies:
@@ -3969,7 +4075,7 @@ packages:
   /@types/express-serve-static-core@4.17.37:
     resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
       '@types/qs': 6.9.8
       '@types/range-parser': 1.2.5
       '@types/send': 0.17.2
@@ -3988,13 +4094,13 @@ packages:
     resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
     dependencies:
       '@types/minimatch': 5.1.2
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: true
 
   /@types/graceful-fs@4.1.7:
     resolution: {integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==}
     dependencies:
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
     dev: true
 
   /@types/hast@2.3.6:
@@ -4010,6 +4116,10 @@ packages:
     resolution: {integrity: sha512-oh/6byDPnL1zeNXFrDXFLyZjkr1MsBG667IM792caf1L2UPOOMf65NFzjUH/ltyfwjAGfs1rsX1eftK0jC/KIg==}
     dev: false
 
+  /@types/http-cache-semantics@4.0.3:
+    resolution: {integrity: sha512-V46MYLFp08Wf2mmaBhvgjStM3tPa+2GAdy/iqoX+noX1//zje2x4XmrIU0cAwyClATsTmahbtoQ2EwP7I5WSiA==}
+    dev: true
+
   /@types/http-errors@2.0.2:
     resolution: {integrity: sha512-lPG6KlZs88gef6aD85z3HNkztpj7w2R7HmR3gygjfXCQmsLloWNARFkMuzKiiY8FGdh1XDpgBdrSf4aKDiA7Kg==}
     dev: false
@@ -4017,7 +4127,7 @@ packages:
   /@types/http-proxy@1.17.12:
     resolution: {integrity: sha512-kQtujO08dVtQ2wXAuSFfk9ASy3sug4+ogFR8Kd8UgP8PEuc1/G/8yjYRmp//PcDNJEUKOza/MrQu15bouEUCiw==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/inquirer@6.5.0:
@@ -4067,8 +4177,7 @@ packages:
   /@types/keyv@3.1.4:
     resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
     dependencies:
-      '@types/node': 20.9.0
-    dev: false
+      '@types/node': 18.18.8
 
   /@types/lodash-es@4.17.10:
     resolution: {integrity: sha512-YJP+w/2khSBwbUSFdGsSqmDvmnN3cCKoPOL7Zjle6s30ZtemkkqhjVfFqGwPN7ASil5VyjE2GtyU/yqYY6mC0A==}
@@ -4104,7 +4213,7 @@ packages:
   /@types/node-fetch@2.6.6:
     resolution: {integrity: sha512-95X8guJYhfqiuVVhRFxVQcf4hW/2bCuoPwDasMf/531STFoNoWTT7YDnWdXHEZKqAGUigmpG31r2FE70LwnzJw==}
     dependencies:
-      '@types/node': 18.18.7
+      '@types/node': 18.18.8
       form-data: 4.0.0
     dev: false
 
@@ -4144,11 +4253,7 @@ packages:
     resolution: {integrity: sha512-TlgT8JntpcbmKUFzjhsyhGfP2fsiz1Mv56im6enJ905xG1DAYesxJaeSbGqQmAw8OWPdhyJGhGSQGKRNJ45u9w==}
     dependencies:
       undici-types: 5.26.5
-
-  /@types/node@20.9.0:
-    resolution: {integrity: sha512-nekiGu2NDb1BcVofVcEKMIwzlx4NjHlcjhoxxKBNLtz15Y1z7MYf549DFvkHSId02Ax6kGwWntIBPC3l/JZcmw==}
-    dependencies:
-      undici-types: 5.26.5
+    dev: true
 
   /@types/normalize-package-data@2.4.2:
     resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==}
@@ -4172,6 +4277,10 @@ packages:
     resolution: {integrity: sha512-+DDIKtFsGMajapzc5A+jL9V1dpLZ5lShAd6Oq0yRu2qFHFr2hhHlZ2rkFiInXOoFSxjxGmyGdCjjHghoHj/x0w==}
     dev: true
 
+  /@types/prompts@2.0.1:
+    resolution: {integrity: sha512-AhtMcmETelF8wFDV1ucbChKhLgsc+ytXZXkNz/nnTAMSDeqsjALknEFxi7ZtLgS/G8bV2rp90LhDW5SGACimIQ==}
+    dev: true
+
   /@types/prop-types@15.7.8:
     resolution: {integrity: sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==}
 
@@ -4213,17 +4322,15 @@ packages:
   /@types/responselike@1.0.1:
     resolution: {integrity: sha512-TiGnitEDxj2X0j+98Eqk5lv/Cij8oHd32bU4D/Yw6AOq7vvTk0gSD2GPj0G/HkvhMoVsdlhYF4yqqlyPBTM6Sg==}
     dependencies:
-      '@types/node': 20.9.0
-    dev: false
+      '@types/node': 18.18.8
 
   /@types/retry@0.12.0:
     resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==}
-    dev: false
 
   /@types/sax@1.2.5:
     resolution: {integrity: sha512-9jWta97bBVC027/MShr3gLab8gPhKy4l6qpb+UJLF5pDm3501NvA7uvqVCW+REFtx00oTi6Cq9JzLwgq6evVgw==}
     dependencies:
-      '@types/node': 17.0.45
+      '@types/node': 18.18.8
     dev: false
 
   /@types/scheduler@0.16.4:
@@ -4237,7 +4344,7 @@ packages:
     resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==}
     dependencies:
       '@types/mime': 1.3.3
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/serve-index@1.9.2:
@@ -4251,23 +4358,30 @@ packages:
     dependencies:
       '@types/http-errors': 2.0.2
       '@types/mime': 3.0.2
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/sockjs@0.3.34:
     resolution: {integrity: sha512-R+n7qBFnm/6jinlteC9DBL5dGiDGjWAvjo4viUanpnc/dG1y7uDoacXPIQ/PQEg1fI912SMHIa014ZjRpvDw4g==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/stack-utils@2.0.1:
     resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
     dev: true
 
+  /@types/tar@6.1.5:
+    resolution: {integrity: sha512-qm2I/RlZij5RofuY7vohTpYNaYcrSQlN2MyjucQc7ZweDwaEWkdN/EeNh6e9zjK6uEm6PwjdMXkcj05BxZdX1Q==}
+    dependencies:
+      '@types/node': 18.18.8
+      minipass: 4.2.8
+    dev: true
+
   /@types/through@0.0.31:
     resolution: {integrity: sha512-LpKpmb7FGevYgXnBXYs6HWnmiFyVG07Pt1cnbgM1IhEacITTiUaBXXvOR3Y50ksaJWGSfhbEvQFivQEFGCC55w==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: true
 
   /@types/tinycolor2@1.4.4:
@@ -4282,6 +4396,10 @@ packages:
     resolution: {integrity: sha512-BT2Krtx4xaO6iwzwMFUYvWBWkV2pr37zD68Vmp1CDV196MzczBRxuEpD6Pr395HAgebC/co7hOphs53r8V7jew==}
     dev: true
 
+  /@types/validate-npm-package-name@3.0.0:
+    resolution: {integrity: sha512-iFNNIrEaJH1lbPiyX+O/QyxSbKxrTjdNBVZGckt+iEL9So0hdZNBL68sOfHnt2txuUD8UJXvmKv/1DkgkebgUg==}
+    dev: true
+
   /@types/webidl-conversions@7.0.2:
     resolution: {integrity: sha512-uNv6b/uGRLlCVmelat2rA8bcVd3k/42mV2EmjhPh6JLkd35T5bgwR/t6xy7a9MWhd9sixIeBUzhBenvk3NO+DQ==}
     dev: false
@@ -4296,7 +4414,7 @@ packages:
   /@types/ws@8.5.6:
     resolution: {integrity: sha512-8B5EO9jLVCy+B58PLHvLDuOD8DRVMgQzq8d55SjLCOn9kqGyqOvy27exVaTio1q1nX5zLu8/6N0n2ThSxOM6tg==}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
     dev: false
 
   /@types/yargs-parser@21.0.1:
@@ -4372,6 +4490,11 @@ packages:
   /@ungap/structured-clone@1.2.0:
     resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
 
+  /@vercel/ncc@0.34.0:
+    resolution: {integrity: sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==}
+    hasBin: true
+    dev: true
+
   /@webassemblyjs/ast@1.11.6:
     resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
     dependencies:
@@ -4563,7 +4686,6 @@ packages:
         optional: true
     dependencies:
       ajv: 8.12.0
-    dev: false
 
   /ajv-keywords@3.5.2(ajv@6.12.6):
     resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
@@ -4596,7 +4718,6 @@ packages:
       json-schema-traverse: 1.0.0
       require-from-string: 2.0.2
       uri-js: 4.4.1
-    dev: false
 
   /algoliasearch-helper@3.14.2(algoliasearch@4.20.0):
     resolution: {integrity: sha512-FjDSrjvQvJT/SKMW74nPgFpsoPUwZCzGbCqbp8HhBFfSk/OvNFxzCaCmuO0p7AWeLy1gD+muFwQEkBwcl5H4pg==}
@@ -4644,6 +4765,13 @@ packages:
       type-fest: 0.21.3
     dev: true
 
+  /ansi-escapes@5.0.0:
+    resolution: {integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==}
+    engines: {node: '>=12'}
+    dependencies:
+      type-fest: 1.4.0
+    dev: true
+
   /ansi-html-community@0.0.8:
     resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==}
     engines: {'0': node >= 0.8.0}
@@ -4839,6 +4967,16 @@ packages:
       tslib: 2.6.2
     dev: true
 
+  /async-retry@1.3.1:
+    resolution: {integrity: sha512-aiieFW/7h3hY0Bq5d+ktDBejxuwR78vRu9hDUdR8rNhSaQ29VzPL4AoIRG7D/c7tdenwOcKvgPM6tIxB3cB6HA==}
+    dependencies:
+      retry: 0.12.0
+    dev: true
+
+  /async-sema@3.0.1:
+    resolution: {integrity: sha512-fKT2riE8EHAvJEfLJXZiATQWqZttjx1+tfgnVshCDrH8vlw4YC8aECe0B8MU184g+aVRFVgmfxFlKZKaozSrNw==}
+    dev: true
+
   /asynckit@0.4.0:
     resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
     dev: false
@@ -4848,6 +4986,11 @@ packages:
     engines: {node: '>= 4.0.0'}
     dev: false
 
+  /atomically@1.7.0:
+    resolution: {integrity: sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==}
+    engines: {node: '>=10.12.0'}
+    dev: true
+
   /autoprefixer@10.4.16(postcss@8.4.31):
     resolution: {integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==}
     engines: {node: ^10 || ^12 || >=14}
@@ -5313,6 +5456,10 @@ packages:
     resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==}
     dev: true
 
+  /builtins@1.0.3:
+    resolution: {integrity: sha512-uYBjakWipfaO/bXI7E8rq6kpwHRZK5cNYrUv2OzZSI/FvmdMyXJ2tG9dKcjEC5YHmHpUAwsargWIZNWdxb/bnQ==}
+    dev: true
+
   /builtins@5.0.1:
     resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==}
     dependencies:
@@ -5358,6 +5505,14 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /cacheable-lookup@2.0.1:
+    resolution: {integrity: sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==}
+    engines: {node: '>=10'}
+    dependencies:
+      '@types/keyv': 3.1.4
+      keyv: 4.5.4
+    dev: true
+
   /cacheable-request@6.1.0:
     resolution: {integrity: sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==}
     engines: {node: '>=8'}
@@ -5371,6 +5526,19 @@ packages:
       responselike: 1.0.2
     dev: false
 
+  /cacheable-request@7.0.4:
+    resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==}
+    engines: {node: '>=8'}
+    dependencies:
+      clone-response: 1.0.3
+      get-stream: 5.2.0
+      http-cache-semantics: 4.1.1
+      keyv: 4.5.4
+      lowercase-keys: 2.0.0
+      normalize-url: 6.1.0
+      responselike: 2.0.1
+    dev: true
+
   /call-bind@1.0.2:
     resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
     dependencies:
@@ -5555,6 +5723,11 @@ packages:
     optionalDependencies:
       fsevents: 2.3.3
 
+  /chownr@2.0.0:
+    resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
+    engines: {node: '>=10'}
+    dev: true
+
   /chrome-trace-event@1.0.3:
     resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
     engines: {node: '>=6.0'}
@@ -5657,7 +5830,6 @@ packages:
     resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==}
     dependencies:
       mimic-response: 1.0.1
-    dev: false
 
   /clone@1.0.4:
     resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==}
@@ -5739,8 +5911,8 @@ packages:
     engines: {node: '>=16'}
     dev: false
 
-  /commander@2.20.3:
-    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+  /commander@2.20.0:
+    resolution: {integrity: sha512-7j2y+40w61zy6YC2iRNpUe/NwhNyoXrYpHMrSunaMG64nRnaf96zO/KMQR4OyN/UnE5KLyEBnKHd4aG3rskjpQ==}
 
   /commander@4.1.1:
     resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
@@ -5790,6 +5962,22 @@ packages:
   /concat-map@0.0.1:
     resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
 
+  /conf@10.2.0:
+    resolution: {integrity: sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==}
+    engines: {node: '>=12'}
+    dependencies:
+      ajv: 8.12.0
+      ajv-formats: 2.1.1(ajv@8.12.0)
+      atomically: 1.7.0
+      debounce-fn: 4.0.0
+      dot-prop: 6.0.1
+      env-paths: 2.2.1
+      json-schema-typed: 7.0.3
+      onetime: 5.1.2
+      pkg-up: 3.1.0
+      semver: 7.5.4
+    dev: true
+
   /configstore@5.0.1:
     resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==}
     engines: {node: '>=8'}
@@ -5964,7 +6152,7 @@ packages:
       sha.js: 2.4.11
     dev: true
 
-  /create-jest@29.7.0(@types/node@20.9.0):
+  /create-jest@29.7.0(@types/node@18.18.8):
     resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -5973,9 +6161,9 @@ packages:
       chalk: 4.1.2
       exit: 0.1.2
       graceful-fs: 4.2.11
-      jest-config: 29.7.0(@types/node@20.9.0)
+      jest-config: 29.7.0(@types/node@18.18.8)
       jest-util: 29.7.0
-      prompts: 2.4.2
+      prompts: 2.1.0
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -6251,6 +6439,13 @@ packages:
     engines: {node: '>= 14'}
     dev: true
 
+  /debounce-fn@4.0.0:
+    resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      mimic-fn: 3.1.0
+    dev: true
+
   /debug@2.6.9:
     resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
     peerDependencies:
@@ -6304,6 +6499,13 @@ packages:
       mimic-response: 1.0.1
     dev: false
 
+  /decompress-response@5.0.0:
+    resolution: {integrity: sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==}
+    engines: {node: '>=10'}
+    dependencies:
+      mimic-response: 2.1.0
+    dev: true
+
   /dedent@1.5.1:
     resolution: {integrity: sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==}
     peerDependencies:
@@ -6381,6 +6583,11 @@ packages:
     resolution: {integrity: sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==}
     dev: false
 
+  /defer-to-connect@2.0.1:
+    resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
+    engines: {node: '>=10'}
+    dev: true
+
   /define-data-property@1.1.0:
     resolution: {integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==}
     engines: {node: '>= 0.4'}
@@ -6678,6 +6885,13 @@ packages:
       is-obj: 2.0.0
     dev: false
 
+  /dot-prop@6.0.1:
+    resolution: {integrity: sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==}
+    engines: {node: '>=10'}
+    dependencies:
+      is-obj: 2.0.0
+    dev: true
+
   /duck@0.1.12:
     resolution: {integrity: sha512-wkctla1O6VfP89gQ+J/yDesM0S7B7XLXjKGzXxMDVFg7uEn706niAtyYovKbyq1oT9YwDcly721/iUWoc8MVRg==}
     dependencies:
@@ -6686,7 +6900,6 @@ packages:
 
   /duplexer3@0.1.5:
     resolution: {integrity: sha512-1A8za6ws41LQgv9HrE/66jyC5yuSjQ3L/KOpFtoBilsAK2iA2wuS5rTt1OCzIvtS2V7nVmedsUU+DGRcjBmOYA==}
-    dev: false
 
   /duplexer@0.1.2:
     resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==}
@@ -6754,7 +6967,6 @@ packages:
     resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
     dependencies:
       once: 1.4.0
-    dev: false
 
   /enhanced-resolve@5.13.0:
     resolution: {integrity: sha512-eyV8f0y1+bzyfh8xAwW/WTSZpLbjhqc4ne9eGSH4Zo2ejdyiNG9pU6mf9DG8a7+Auk6MFTlNOT4Y2y/9k8GKVg==}
@@ -6788,6 +7000,11 @@ packages:
     engines: {node: '>=0.12'}
     dev: false
 
+  /env-paths@2.2.1:
+    resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
+    engines: {node: '>=6'}
+    dev: true
+
   /error-ex@1.3.2:
     resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
     dependencies:
@@ -7119,7 +7336,7 @@ packages:
       minimatch: 3.1.2
       object.values: 1.1.6
       resolve: 1.22.4
-      semver: 6.3.0
+      semver: 6.3.1
       tsconfig-paths: 3.14.2
     transitivePeerDependencies:
       - eslint-import-resolver-typescript
@@ -7149,7 +7366,7 @@ packages:
       minimatch: 3.1.2
       object.entries: 1.1.6
       object.fromentries: 2.0.6
-      semver: 6.3.0
+      semver: 6.3.1
     dev: false
 
   /eslint-plugin-react-hooks@4.6.0(eslint@8.53.0):
@@ -7204,7 +7421,7 @@ packages:
       object.values: 1.1.6
       prop-types: 15.8.1
       resolve: 2.0.0-next.4
-      semver: 6.3.0
+      semver: 6.3.1
       string.prototype.matchall: 4.0.8
     dev: false
 
@@ -7383,7 +7600,7 @@ packages:
     resolution: {integrity: sha512-EzV94NYKoO09GLXGjXj9JIlXijVck4ONSr5wiCWDvhsvj5jxSrzTmRU/9C1DyB6uToszLs8aifA6NQ7lEQdvFw==}
     engines: {node: '>= 0.8'}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
       require-like: 0.1.2
     dev: false
 
@@ -7664,7 +7881,6 @@ packages:
     engines: {node: '>=6'}
     dependencies:
       locate-path: 3.0.0
-    dev: false
 
   /find-up@4.1.0:
     resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
@@ -7834,6 +8050,13 @@ packages:
       universalify: 2.0.0
     dev: false
 
+  /fs-minipass@2.1.0:
+    resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.3.6
+    dev: true
+
   /fs-monkey@1.0.5:
     resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==}
     dev: false
@@ -7940,7 +8163,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       pump: 3.0.0
-    dev: false
 
   /get-stream@6.0.1:
     resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
@@ -8113,6 +8335,29 @@ packages:
     dependencies:
       get-intrinsic: 1.2.2
 
+  /got@10.7.0:
+    resolution: {integrity: sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==}
+    engines: {node: '>=10'}
+    dependencies:
+      '@sindresorhus/is': 2.1.1
+      '@szmarczak/http-timer': 4.0.6
+      '@types/cacheable-request': 6.0.3
+      '@types/keyv': 3.1.4
+      '@types/responselike': 1.0.1
+      cacheable-lookup: 2.0.1
+      cacheable-request: 7.0.4
+      decompress-response: 5.0.0
+      duplexer3: 0.1.5
+      get-stream: 5.2.0
+      lowercase-keys: 2.0.0
+      mimic-response: 2.1.0
+      p-cancelable: 2.1.1
+      p-event: 4.2.0
+      responselike: 2.0.1
+      to-readable-stream: 2.1.0
+      type-fest: 0.10.0
+    dev: true
+
   /got@9.6.0:
     resolution: {integrity: sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==}
     engines: {node: '>=8.6'}
@@ -8440,7 +8685,6 @@ packages:
 
   /http-cache-semantics@4.1.1:
     resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==}
-    dev: false
 
   /http-deceiver@1.2.7:
     resolution: {integrity: sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==}
@@ -8948,7 +9192,6 @@ packages:
   /is-obj@2.0.0:
     resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==}
     engines: {node: '>=8'}
-    dev: false
 
   /is-path-cwd@2.2.0:
     resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
@@ -9209,7 +9452,7 @@ packages:
       '@jest/expect': 29.7.0
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       chalk: 4.1.2
       co: 4.6.0
       dedent: 1.5.1
@@ -9230,7 +9473,7 @@ packages:
       - supports-color
     dev: true
 
-  /jest-cli@29.7.0(@types/node@20.9.0):
+  /jest-cli@29.7.0(@types/node@18.18.8):
     resolution: {integrity: sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -9244,10 +9487,10 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
       chalk: 4.1.2
-      create-jest: 29.7.0(@types/node@20.9.0)
+      create-jest: 29.7.0(@types/node@18.18.8)
       exit: 0.1.2
       import-local: 3.1.0
-      jest-config: 29.7.0(@types/node@20.9.0)
+      jest-config: 29.7.0(@types/node@18.18.8)
       jest-util: 29.7.0
       jest-validate: 29.7.0
       yargs: 17.7.2
@@ -9258,7 +9501,7 @@ packages:
       - ts-node
     dev: true
 
-  /jest-config@29.7.0(@types/node@20.8.10):
+  /jest-config@29.7.0(@types/node@18.18.8):
     resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     peerDependencies:
@@ -9273,47 +9516,7 @@ packages:
       '@babel/core': 7.23.0
       '@jest/test-sequencer': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
-      babel-jest: 29.7.0(@babel/core@7.23.0)
-      chalk: 4.1.2
-      ci-info: 3.9.0
-      deepmerge: 4.3.1
-      glob: 7.2.3
-      graceful-fs: 4.2.11
-      jest-circus: 29.7.0
-      jest-environment-node: 29.7.0
-      jest-get-type: 29.6.3
-      jest-regex-util: 29.6.3
-      jest-resolve: 29.7.0
-      jest-runner: 29.7.0
-      jest-util: 29.7.0
-      jest-validate: 29.7.0
-      micromatch: 4.0.5
-      parse-json: 5.2.0
-      pretty-format: 29.7.0
-      slash: 3.0.0
-      strip-json-comments: 3.1.1
-    transitivePeerDependencies:
-      - babel-plugin-macros
-      - supports-color
-    dev: true
-
-  /jest-config@29.7.0(@types/node@20.9.0):
-    resolution: {integrity: sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==}
-    engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
-    peerDependencies:
-      '@types/node': '*'
-      ts-node: '>=9.0.0'
-    peerDependenciesMeta:
-      '@types/node':
-        optional: true
-      ts-node:
-        optional: true
-    dependencies:
-      '@babel/core': 7.23.0
-      '@jest/test-sequencer': 29.7.0
-      '@jest/types': 29.6.3
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
       babel-jest: 29.7.0(@babel/core@7.23.0)
       chalk: 4.1.2
       ci-info: 3.9.0
@@ -9373,7 +9576,7 @@ packages:
       '@jest/environment': 29.7.0
       '@jest/fake-timers': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       jest-mock: 29.7.0
       jest-util: 29.7.0
     dev: true
@@ -9389,7 +9592,7 @@ packages:
     dependencies:
       '@jest/types': 29.6.3
       '@types/graceful-fs': 4.1.7
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       anymatch: 3.1.3
       fb-watchman: 2.0.2
       graceful-fs: 4.2.11
@@ -9440,7 +9643,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       jest-util: 29.7.0
     dev: true
 
@@ -9495,7 +9698,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       chalk: 4.1.2
       emittery: 0.13.1
       graceful-fs: 4.2.11
@@ -9526,7 +9729,7 @@ packages:
       '@jest/test-result': 29.7.0
       '@jest/transform': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       chalk: 4.1.2
       cjs-module-lexer: 1.2.3
       collect-v8-coverage: 1.0.2
@@ -9578,7 +9781,7 @@ packages:
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       chalk: 4.1.2
       ci-info: 3.9.0
       graceful-fs: 4.2.11
@@ -9602,7 +9805,7 @@ packages:
     dependencies:
       '@jest/test-result': 29.7.0
       '@jest/types': 29.6.3
-      '@types/node': 20.8.10
+      '@types/node': 18.18.8
       ansi-escapes: 4.3.2
       chalk: 4.1.2
       emittery: 0.13.1
@@ -9614,7 +9817,7 @@ packages:
     resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
     engines: {node: '>= 10.13.0'}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
@@ -9622,12 +9825,12 @@ packages:
     resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     dependencies:
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
       jest-util: 29.7.0
       merge-stream: 2.0.0
       supports-color: 8.1.1
 
-  /jest@29.7.0(@types/node@20.9.0):
+  /jest@29.7.0(@types/node@18.18.8):
     resolution: {integrity: sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==}
     engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
     hasBin: true
@@ -9640,7 +9843,7 @@ packages:
       '@jest/core': 29.7.0
       '@jest/types': 29.6.3
       import-local: 3.1.0
-      jest-cli: 29.7.0(@types/node@20.9.0)
+      jest-cli: 29.7.0(@types/node@18.18.8)
     transitivePeerDependencies:
       - '@types/node'
       - babel-plugin-macros
@@ -9714,7 +9917,10 @@ packages:
 
   /json-schema-traverse@1.0.0:
     resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==}
-    dev: false
+
+  /json-schema-typed@7.0.3:
+    resolution: {integrity: sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==}
+    dev: true
 
   /json-stable-stringify-without-jsonify@1.0.1:
     resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
@@ -9881,7 +10087,6 @@ packages:
     dependencies:
       p-locate: 3.0.0
       path-exists: 3.0.0
-    dev: false
 
   /locate-path@5.0.0:
     resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
@@ -10009,7 +10214,6 @@ packages:
   /lowercase-keys@2.0.0:
     resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
     engines: {node: '>=8'}
-    dev: false
 
   /lru-cache@4.1.5:
     resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
@@ -10252,6 +10456,11 @@ packages:
     resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
     engines: {node: '>=6'}
 
+  /mimic-fn@3.1.0:
+    resolution: {integrity: sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==}
+    engines: {node: '>=8'}
+    dev: true
+
   /mimic-fn@4.0.0:
     resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
     engines: {node: '>=12'}
@@ -10260,7 +10469,11 @@ packages:
   /mimic-response@1.0.1:
     resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
     engines: {node: '>=4'}
-    dev: false
+
+  /mimic-response@2.1.0:
+    resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
+    engines: {node: '>=8'}
+    dev: true
 
   /min-indent@1.0.1:
     resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
@@ -10308,6 +10521,31 @@ packages:
   /minimist@1.2.8:
     resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
 
+  /minipass@3.3.6:
+    resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==}
+    engines: {node: '>=8'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /minipass@4.2.8:
+    resolution: {integrity: sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /minipass@5.0.0:
+    resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /minizlib@2.1.2:
+    resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      minipass: 3.3.6
+      yallist: 4.0.0
+    dev: true
+
   /mixme@0.5.9:
     resolution: {integrity: sha512-VC5fg6ySUscaWUpI4gxCBTQMH2RdUpNrk+MsbpCYtIvf9SBJdiUey4qE7BXviJsJR4nDQxCZ+3yaYNW3guz/Pw==}
     engines: {node: '>= 8.0.0'}
@@ -10320,6 +10558,12 @@ packages:
       minimist: 1.2.8
     dev: true
 
+  /mkdirp@1.0.4:
+    resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dev: true
+
   /mongodb-connection-string-url@2.6.0:
     resolution: {integrity: sha512-WvTZlI9ab0QYtTYnuMLgobULWhokRjtC7db9LtcVfJ+Hsnyr5eo6ZtNAt3Ly24XZScGMelOcGtm7lSn0332tPQ==}
     dependencies:
@@ -10587,7 +10831,6 @@ packages:
   /normalize-url@6.1.0:
     resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
     engines: {node: '>=10'}
-    dev: false
 
   /notion-md-crawler@0.0.2:
     resolution: {integrity: sha512-lE3/DFMrg7GSbl1sBfDuLVLyxw+yjdarPVm1JGfQ6eONEbNGgO+BdZxpwwZQ1uYeEJurAXMXb/AXT8GKYjKAyg==}
@@ -10823,6 +11066,18 @@ packages:
     engines: {node: '>=6'}
     dev: false
 
+  /p-cancelable@2.1.1:
+    resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /p-event@4.2.0:
+    resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-timeout: 3.2.0
+    dev: true
+
   /p-filter@2.1.0:
     resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==}
     engines: {node: '>=8'}
@@ -10830,6 +11085,11 @@ packages:
       p-map: 2.1.0
     dev: false
 
+  /p-finally@1.0.0:
+    resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
+    engines: {node: '>=4'}
+    dev: true
+
   /p-limit@2.3.0:
     resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
     engines: {node: '>=6'}
@@ -10847,7 +11107,6 @@ packages:
     engines: {node: '>=6'}
     dependencies:
       p-limit: 2.3.0
-    dev: false
 
   /p-locate@4.1.0:
     resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
@@ -10888,6 +11147,13 @@ packages:
       retry: 0.13.1
     dev: false
 
+  /p-timeout@3.2.0:
+    resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-finally: 1.0.0
+    dev: true
+
   /p-try@2.2.0:
     resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
     engines: {node: '>=6'}
@@ -11036,7 +11302,6 @@ packages:
   /path-exists@3.0.0:
     resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
     engines: {node: '>=4'}
-    dev: false
 
   /path-exists@4.0.0:
     resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
@@ -11136,7 +11401,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       find-up: 3.0.0
-    dev: false
 
   /portkey-ai@0.1.16:
     resolution: {integrity: sha512-EY4FRp6PZSD75Q1o1qc08DfPNTG9FnkUPN3Z1/lEvaq9iFpSO5UekcagUZaKSVhao311qjBjns+kF0rS9ht7iA==}
@@ -11615,6 +11879,62 @@ packages:
       typescript: 5.2.2
     dev: true
 
+  /prettier-plugin-tailwindcss@0.3.0(prettier-plugin-organize-imports@3.2.3)(prettier@3.0.3):
+    resolution: {integrity: sha512-009/Xqdy7UmkcTBpwlq7jsViDqXAYSOMLDrHAdTMlVZOrKfM2o9Ci7EMWTMZ7SkKBFTG04UM9F9iM2+4i6boDA==}
+    engines: {node: '>=12.17.0'}
+    peerDependencies:
+      '@ianvs/prettier-plugin-sort-imports': '*'
+      '@prettier/plugin-pug': '*'
+      '@shopify/prettier-plugin-liquid': '*'
+      '@shufo/prettier-plugin-blade': '*'
+      '@trivago/prettier-plugin-sort-imports': '*'
+      prettier: '>=2.2.0'
+      prettier-plugin-astro: '*'
+      prettier-plugin-css-order: '*'
+      prettier-plugin-import-sort: '*'
+      prettier-plugin-jsdoc: '*'
+      prettier-plugin-marko: '*'
+      prettier-plugin-organize-attributes: '*'
+      prettier-plugin-organize-imports: '*'
+      prettier-plugin-style-order: '*'
+      prettier-plugin-svelte: '*'
+      prettier-plugin-twig-melody: '*'
+    peerDependenciesMeta:
+      '@ianvs/prettier-plugin-sort-imports':
+        optional: true
+      '@prettier/plugin-pug':
+        optional: true
+      '@shopify/prettier-plugin-liquid':
+        optional: true
+      '@shufo/prettier-plugin-blade':
+        optional: true
+      '@trivago/prettier-plugin-sort-imports':
+        optional: true
+      prettier-plugin-astro:
+        optional: true
+      prettier-plugin-css-order:
+        optional: true
+      prettier-plugin-import-sort:
+        optional: true
+      prettier-plugin-jsdoc:
+        optional: true
+      prettier-plugin-marko:
+        optional: true
+      prettier-plugin-organize-attributes:
+        optional: true
+      prettier-plugin-organize-imports:
+        optional: true
+      prettier-plugin-style-order:
+        optional: true
+      prettier-plugin-svelte:
+        optional: true
+      prettier-plugin-twig-melody:
+        optional: true
+    dependencies:
+      prettier: 3.0.3
+      prettier-plugin-organize-imports: 3.2.3(prettier@3.0.3)(typescript@5.2.2)
+    dev: true
+
   /prettier@2.8.8:
     resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
     engines: {node: '>=10.13.0'}
@@ -11676,12 +11996,21 @@ packages:
       asap: 2.0.6
     dev: false
 
+  /prompts@2.1.0:
+    resolution: {integrity: sha512-+x5TozgqYdOwWsQFZizE/Tra3fKvAoy037kOyU6cgz84n8f6zxngLOV4O32kTwt9FcLCxAqw0P/c8rOr9y+Gfg==}
+    engines: {node: '>= 6'}
+    dependencies:
+      kleur: 3.0.3
+      sisteransi: 1.0.5
+    dev: true
+
   /prompts@2.4.2:
     resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
     engines: {node: '>= 6'}
     dependencies:
       kleur: 3.0.3
       sisteransi: 1.0.5
+    dev: false
 
   /prop-types@15.8.1:
     resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
@@ -11744,7 +12073,6 @@ packages:
     dependencies:
       end-of-stream: 1.4.4
       once: 1.4.0
-    dev: false
 
   /punycode@1.4.1:
     resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==}
@@ -12354,7 +12682,6 @@ packages:
   /require-from-string@2.0.2:
     resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
     engines: {node: '>=0.10.0'}
-    dev: false
 
   /require-like@0.1.2:
     resolution: {integrity: sha512-oyrU88skkMtDdauHDuKVrgR+zuItqr6/c//FXzvmxRGMexSDc6hNvJInGW3LL46n+8b50RykrvwSUIIQH2LQ5A==}
@@ -12441,6 +12768,12 @@ packages:
       lowercase-keys: 1.0.1
     dev: false
 
+  /responselike@2.0.1:
+    resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==}
+    dependencies:
+      lowercase-keys: 2.0.0
+    dev: true
+
   /restore-cursor@3.1.0:
     resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
     engines: {node: '>=8'}
@@ -12449,6 +12782,11 @@ packages:
       signal-exit: 3.0.7
     dev: true
 
+  /retry@0.12.0:
+    resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==}
+    engines: {node: '>= 4'}
+    dev: true
+
   /retry@0.13.1:
     resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==}
     engines: {node: '>= 4'}
@@ -13346,6 +13684,14 @@ packages:
     dependencies:
       has-flag: 4.0.0
 
+  /supports-hyperlinks@2.3.0:
+    resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+      supports-color: 7.2.0
+    dev: true
+
   /supports-preserve-symlinks-flag@1.0.0:
     resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
     engines: {node: '>= 0.4'}
@@ -13392,11 +13738,31 @@ packages:
     resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
     engines: {node: '>=6'}
 
+  /tar@6.1.15:
+    resolution: {integrity: sha512-/zKt9UyngnxIT/EAGYuxaMYgOIJiP81ab9ZfkILq4oNLPFX50qyYmu7jRj9qeXoxmJHjGlbH0+cm2uy1WCs10A==}
+    engines: {node: '>=10'}
+    dependencies:
+      chownr: 2.0.0
+      fs-minipass: 2.1.0
+      minipass: 5.0.0
+      minizlib: 2.1.2
+      mkdirp: 1.0.4
+      yallist: 4.0.0
+    dev: true
+
   /term-size@2.2.1:
     resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
     engines: {node: '>=8'}
     dev: false
 
+  /terminal-link@3.0.0:
+    resolution: {integrity: sha512-flFL3m4wuixmf6IfhFJd1YPiLiMuxEc8uHRM1buzIeZPm22Au2pDqBJQgdo7n1WfPU1ONFGv7YDwpFBmHGF6lg==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-escapes: 5.0.0
+      supports-hyperlinks: 2.3.0
+    dev: true
+
   /terser-webpack-plugin@5.3.9(webpack@5.88.2):
     resolution: {integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==}
     engines: {node: '>= 10.13.0'}
@@ -13451,7 +13817,7 @@ packages:
     dependencies:
       '@jridgewell/source-map': 0.3.5
       acorn: 8.10.0
-      commander: 2.20.3
+      commander: 2.20.0
       source-map-support: 0.5.21
 
   /test-exclude@6.0.0:
@@ -13542,6 +13908,11 @@ packages:
     engines: {node: '>=6'}
     dev: false
 
+  /to-readable-stream@2.1.0:
+    resolution: {integrity: sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==}
+    engines: {node: '>=8'}
+    dev: true
+
   /to-regex-range@5.0.1:
     resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
     engines: {node: '>=8.0'}
@@ -13626,7 +13997,7 @@ packages:
       '@babel/core': 7.23.2
       bs-logger: 0.2.6
       fast-json-stable-stringify: 2.1.0
-      jest: 29.7.0(@types/node@20.9.0)
+      jest: 29.7.0(@types/node@18.18.8)
       jest-util: 29.7.0
       json5: 2.2.3
       lodash.memoize: 4.1.2
@@ -13667,7 +14038,7 @@ packages:
       yn: 3.1.1
     dev: true
 
-  /ts-node@10.9.1(@types/node@20.9.0)(typescript@5.2.2):
+  /ts-node@10.9.1(@types/node@18.18.8)(typescript@5.2.2):
     resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==}
     hasBin: true
     peerDependencies:
@@ -13686,7 +14057,7 @@ packages:
       '@tsconfig/node12': 1.0.11
       '@tsconfig/node14': 1.0.3
       '@tsconfig/node16': 1.0.4
-      '@types/node': 20.9.0
+      '@types/node': 18.18.8
       acorn: 8.10.0
       acorn-walk: 8.2.0
       arg: 4.1.3
@@ -13852,6 +14223,11 @@ packages:
     engines: {node: '>=4'}
     dev: true
 
+  /type-fest@0.10.0:
+    resolution: {integrity: sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==}
+    engines: {node: '>=8'}
+    dev: true
+
   /type-fest@0.13.1:
     resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==}
     engines: {node: '>=10'}
@@ -13876,6 +14252,11 @@ packages:
     engines: {node: '>=8'}
     dev: false
 
+  /type-fest@1.4.0:
+    resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
+    engines: {node: '>=10'}
+    dev: true
+
   /type-fest@2.19.0:
     resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==}
     engines: {node: '>=12.20'}
@@ -14312,6 +14693,12 @@ packages:
       spdx-expression-parse: 3.0.1
     dev: false
 
+  /validate-npm-package-name@3.0.0:
+    resolution: {integrity: sha512-M6w37eVCMMouJ9V/sdPGnC5H4uDr73/+xdq0FBLO3TFFX1+7wiUY6Es328NN+y43tmY+doUdN9g9J21vqB7iLw==}
+    dependencies:
+      builtins: 1.0.3
+    dev: true
+
   /validate-npm-package-name@5.0.0:
     resolution: {integrity: sha512-YuKoXDAhBYxY7SfOKxHBDoSyENFeW5VvIIQp2TGQuit8gpK6MnWaQelBKxso72DoxTZfZdcP3W90LqpSkgPzLQ==}
     engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
@@ -14925,3 +15312,9 @@ packages:
   /zwitch@1.0.5:
     resolution: {integrity: sha512-V50KMwwzqJV0NpZIZFwfOD5/lyny3WlSzRiXgA0G7VUnRlqttta1L6UQIHzd6EuBY/cHGfwTIck7w1yH6Q5zUw==}
     dev: false
+
+  github.com/watson/ci-info/f43f6a1cefff47fb361c88cf4b943fdbcaafe540:
+    resolution: {tarball: https://codeload.github.com/watson/ci-info/tar.gz/f43f6a1cefff47fb361c88cf4b943fdbcaafe540}
+    name: ci-info
+    version: 2.0.0
+    dev: true