Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .github/workflows/publish-npm-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ on:
workflow_dispatch:
inputs:
package:
description: "Package to publish (react-ui, react-headless, or lang-react)"
description: "Package to publish (react-ui, react-headless, openui-cli, or lang-react)"
required: true
type: choice
options:
- react-ui
- react-headless
- lang-react
- openui-cli

jobs:
publish:
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ At the center of OpenUI is **OpenUI Lang**: a compact, streaming-first language
## Quick Start

```bash
npx create-openui-chat my-chat-app
cd my-chat-app
npx @openuidev/cli@latest create
cd genui-chat-app
npm run dev
```

Expand Down Expand Up @@ -95,7 +95,7 @@ OpenUI also includes ready-made chat surfaces and integration patterns, but chat
If you want a complete starting point with backend wiring, streaming, and a built-in UI, use:

```bash
npx create-openui-chat my-chat-app
npx @openuidev/cli@latest create
```

From there, you can keep the built-in experience or move toward more custom OpenUI Lang-driven interfaces.
Expand Down
4 changes: 2 additions & 2 deletions docs/app/(home)/components/BuildChatSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ function CtaButton() {
if (copied) return;

try {
await navigator.clipboard.writeText("npx create openui-app chat");
await navigator.clipboard.writeText("npx @openuidev/cli@latest create");
setCopied(true);
resetTimeoutRef.current = setTimeout(() => {
setCopied(false);
Expand Down Expand Up @@ -87,7 +87,7 @@ function CtaButton() {
</svg>
</span>
<span className="font-['Inter_Display',sans-serif] font-medium text-[18px] leading-6 text-white relative whitespace-nowrap">
npx create openui-app chat
npx @openuidev/cli@latest create
</span>
</button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion docs/content/docs/chat/installation.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ This page covers package installation, style imports, and a basic render check f
<Callout type="info">
**Starting a new project?**
Skip this guide and use our scaffold command instead:
`npx create-openui-chat my-app`
`npx @openuidev/cli@latest create --name my-app`
</Callout>

## Prerequisites
Expand Down
16 changes: 8 additions & 8 deletions docs/content/docs/chat/quick-start.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,26 @@ Run the create command. This scaffolds a Next.js app with OpenUI Chat already wi
<Tabs groupId="pkg" items={['npx', 'pnpm', 'yarn', 'bun']} persist>
<Tab value="npx">
```bash
npx create-openui-chat my-chat-app
cd my-chat-app
npx @openuidev/cli@latest create
cd genui-chat-app
```
</Tab>
<Tab value="pnpm">
```bash
pnpm create openui-chat my-chat-app
cd my-chat-app
pnpm dlx @openuidev/cli@latest create
cd genui-chat-app
```
</Tab>
<Tab value="yarn">
```bash
yarn create openui-chat my-chat-app
cd my-chat-app
yarn dlx @openuidev/cli@latest create
cd genui-chat-app
```
</Tab>
<Tab value="bun">
```bash
bun create openui-chat my-chat-app
cd my-chat-app
bunx @openuidev/cli@latest create
cd genui-chat-app
```
</Tab>
</Tabs>
Expand Down
2 changes: 1 addition & 1 deletion examples/openui-chat/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This is an [OpenUI](https://openui.com) Agent Chat project bootstrapped with [`create-openui-app`](https://openui.com/docs/chat/quick-start).
This is an [OpenUI](https://openui.com) Agent Chat project bootstrapped with [`openui-cli`](https://openui.com/docs/chat/quick-start).

## Getting Started

Expand Down
20 changes: 0 additions & 20 deletions packages/create-openui-app/src/index.ts

This file was deleted.

137 changes: 137 additions & 0 deletions packages/openui-cli/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# OpenUI CLI

The OpenUI CLI helps you get started with OpenUI faster.

It currently supports two workflows:

- scaffolding a new OpenUI Chat app
- generating a system prompt or JSON Schema from a `createLibrary()` export

## Install

Run the CLI with your package manager of choice:

```bash
npx @openuidev/cli --help
pnpm dlx @openuidev/cli --help
bunx @openuidev/cli --help
```

## Quick Start

Create a new chat app:

```bash
npx @openuidev/cli@latest create
```

Generate a prompt from a library file:

```bash
npx @openuidev/cli generate ./src/library.ts
```

Generate JSON Schema instead:

```bash
npx @openuidev/cli generate ./src/library.ts --json-schema
```

## Commands

### `openui create`

Scaffolds a new Next.js app with OpenUI Chat.

```bash
openui create [options]
```

Options:

- `-n, --name <string>`: Project name
- `--no-interactive`: Fail instead of prompting for missing required input

What it does:

- prompts for the project name if you do not pass `--name`
- copies the bundled `openui-chat` template into a new directory
- rewrites `workspace:*` dependencies in the generated `package.json` to `latest`
- installs dependencies automatically using the detected package manager

Examples:

```bash
openui create
openui create
openui create --no-interactive
```

### `openui generate`

Generates a system prompt or JSON Schema from a file that exports a `createLibrary()` result.

```bash
openui generate [options] [entry]
```

Arguments:

- `entry`: Path to a `.ts`, `.tsx`, `.js`, or `.jsx` file that exports a library

Options:

- `-o, --out <file>`: Write output to a file instead of stdout
- `--json-schema`: Output JSON Schema instead of the system prompt
- `--export <name>`: Use a specific export name instead of auto-detecting the library export
- `--no-interactive`: Fail instead of prompting for a missing `entry`

What it does:

- prompts for the entry file path if you do not pass one
- bundles the entry with `esbuild` before evaluating it in Node
- supports both TypeScript and JavaScript entry files
- stubs common asset imports such as CSS, SVG, images, and fonts during bundling
- auto-detects the exported library by checking `library`, `default`, and then all exports

Examples:

```bash
openui generate ./src/library.ts
openui generate ./src/library.ts --json-schema
openui generate ./src/library.ts --export library
openui generate ./src/library.ts --out ./artifacts/system-prompt.txt
openui generate --no-interactive ./src/library.ts
```

## How `generate` resolves exports

`openui generate` expects the target module to export a library object with both `prompt()` and `toJSONSchema()` methods.

If `--export` is not provided, it looks for exports in this order:

1. `library`
2. `default`
3. any other export that matches the expected library shape

## Local Development

Build the CLI locally:

```bash
pnpm run build
```

Run the built CLI:

```bash
node dist/index.js --help
node dist/index.js create --help
node dist/index.js generate --help
```

## Notes

- interactive prompts can be cancelled without creating output
- `create` requires the template files to be present in the built package
- `generate` exits with a non-zero code if the file is missing or no valid library export is found
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
{
"name": "create-openui-app",
"version": "0.0.1",
"description": "CLI for creating OpenUI apps",
"name": "@openuidev/cli",
"version": "0.0.2",
"description": "CLI for OpenUI",
"bin": {
"openui": "dist/index.js"
},
"files": [
"dist"
"dist",
"README.md"
],
"scripts": {
"build:cli": "tsc -p .",
"build:templates": "mkdir -p dist/templates && cp -r src/templates/openui-chat dist/templates/openui-chat",
"build:templates": "rm -rf dist/templates/openui-chat && mkdir -p dist/templates && cp -R src/templates/openui-chat dist/templates/openui-chat",
"build": "pnpm run build:cli && pnpm run build:templates",
"build:exec": "node dist/index.js",
"lint:check": "eslint ./src --ignore-pattern 'src/templates/**'",
Expand All @@ -20,15 +21,15 @@
"ci": "pnpm run lint:check && pnpm run format:check"
},
"devDependencies": {
"@types/node": "^22.15.32",
"tsx": "^4.19.2"
"@types/node": "^22.15.32"
},
"keywords": [],
"author": "engineering@thesys.dev",
"license": "MIT",
"dependencies": {
"@inquirer/core": "^11.1.5",
"@inquirer/prompts": "^8.3.0",
"commander": "^14.0.3"
"commander": "^14.0.3",
"esbuild": "^0.25.10"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,14 @@ export interface CreateChatAppOptions {
noInteractive?: boolean;
}

function shouldCopyTemplatePath(templateDir: string, src: string): boolean {
const relativePath = path.relative(templateDir, src);

if (!relativePath) return true;

return relativePath !== "openui-chat" && !relativePath.startsWith(`openui-chat${path.sep}`);
}

export async function runCreateChatApp(options: CreateChatAppOptions): Promise<void> {
const args = await resolveArgs(
{
Expand Down Expand Up @@ -41,7 +49,15 @@ export async function runCreateChatApp(options: CreateChatAppOptions): Promise<v

console.info(`\nScaffolding OpenUI Chat app into "${name}"...\n`);

fs.cpSync(templateDir, targetDir, { recursive: true });
const nestedTemplateDir = path.join(templateDir, "openui-chat");
if (fs.existsSync(nestedTemplateDir)) {
console.warn("Warning: Ignoring nested template directory left by a previous CLI build.");
}

fs.cpSync(templateDir, targetDir, {
recursive: true,
filter: (src) => shouldCopyTemplatePath(templateDir, src),
});

const pkgPath = path.join(targetDir, "package.json");
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as {
Expand Down
Loading