@samy-clivolt/create-pi-ag-ui 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -0
- package/dist/index.js +236 -0
- package/package.json +40 -0
- package/template/README.md +408 -0
- package/template/_env.example +27 -0
- package/template/_eslintrc.json +3 -0
- package/template/_gitignore +37 -0
- package/template/justfile +19 -0
- package/template/next-env.d.ts +6 -0
- package/template/next.config.ts +13 -0
- package/template/package.json +42 -0
- package/template/postcss.config.js +6 -0
- package/template/src/app/api/copilotkit/route.ts +63 -0
- package/template/src/app/api/models/route.ts +77 -0
- package/template/src/app/api/thinking/route.ts +38 -0
- package/template/src/app/globals.css +130 -0
- package/template/src/app/layout.tsx +19 -0
- package/template/src/app/page.tsx +214 -0
- package/template/src/components/ActivityPanel.tsx +83 -0
- package/template/src/components/AgentExtrasPanel.tsx +21 -0
- package/template/src/components/AgentStatePanel.tsx +194 -0
- package/template/src/components/ChatToolbar.tsx +42 -0
- package/template/src/components/ChatUI.tsx +258 -0
- package/template/src/components/FrontendTools.tsx +509 -0
- package/template/src/components/InterruptHandler.tsx +183 -0
- package/template/src/components/ModelPicker.tsx +179 -0
- package/template/src/components/PiAgentProvider.tsx +28 -0
- package/template/src/components/ThinkingBlock.tsx +70 -0
- package/template/src/components/ThinkingPicker.tsx +112 -0
- package/template/src/components/ThreadSwitcher.tsx +51 -0
- package/template/src/components/ToolRenderers.tsx +409 -0
- package/template/src/components/chat/MessageSkeleton.tsx +17 -0
- package/template/src/components/chat/StreamingIndicator.tsx +37 -0
- package/template/src/components/layout/MobileSidebarDrawer.tsx +82 -0
- package/template/src/components/state/MetricsCard.tsx +36 -0
- package/template/src/components/state/StepTimeline.tsx +56 -0
- package/template/src/lib/agent-state-context.tsx +183 -0
- package/template/tailwind.config.ts +11 -0
- package/template/tsconfig.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @samy-clivolt/create-pi-ag-ui
|
|
2
|
+
|
|
3
|
+
Scaffold a new **Pi AG-UI** project — an AG-UI protocol bridge with a CopilotKit-powered frontend.
|
|
4
|
+
|
|
5
|
+
## Usage
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npx @samy-clivolt/create-pi-ag-ui my-app
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Then follow the prompts.
|
|
12
|
+
|
|
13
|
+
### Options
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx @samy-clivolt/create-pi-ag-ui my-app --pm pnpm # Use pnpm
|
|
17
|
+
npx @samy-clivolt/create-pi-ag-ui my-app --skip-install # Don't install deps
|
|
18
|
+
npx @samy-clivolt/create-pi-ag-ui --help # Show help
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### What you get
|
|
22
|
+
|
|
23
|
+
A fully-featured AG-UI project with:
|
|
24
|
+
|
|
25
|
+
- 🔌 **24/24 AG-UI events** — full protocol coverage
|
|
26
|
+
- 💬 **CopilotKit chat** — streaming, reasoning, tool calls
|
|
27
|
+
- 🛠️ **Frontend tools** — 7 example tools with generative UI
|
|
28
|
+
- 🧠 **Thinking/Reasoning** — collapsible reasoning blocks
|
|
29
|
+
- 📊 **Agent state panel** — live status, metrics, activities
|
|
30
|
+
- 🔄 **Multi-thread** — isolated sessions per conversation
|
|
31
|
+
- 💾 **Persistence** — optional JSONL session storage
|
|
32
|
+
- 📱 **Responsive** — mobile-friendly layout
|
|
33
|
+
|
|
34
|
+
### Quick start after scaffolding
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
cd my-app
|
|
38
|
+
|
|
39
|
+
# Add your API key
|
|
40
|
+
echo "ANTHROPIC_API_KEY=sk-ant-..." >> .env.local
|
|
41
|
+
|
|
42
|
+
# Start
|
|
43
|
+
npm run dev
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Open http://localhost:3000.
|
|
47
|
+
|
|
48
|
+
## License
|
|
49
|
+
|
|
50
|
+
MIT
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { cp, mkdir, readdir, readFile, writeFile } from "fs/promises";
|
|
4
|
+
import { resolve, join, dirname } from "path";
|
|
5
|
+
import { fileURLToPath } from "url";
|
|
6
|
+
import { createInterface } from "readline";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
// ── ANSI helpers ──────────────────────────────────────────────
|
|
9
|
+
const bold = (s) => `\x1b[1m${s}\x1b[0m`;
|
|
10
|
+
const green = (s) => `\x1b[32m${s}\x1b[0m`;
|
|
11
|
+
const cyan = (s) => `\x1b[36m${s}\x1b[0m`;
|
|
12
|
+
const red = (s) => `\x1b[31m${s}\x1b[0m`;
|
|
13
|
+
const dim = (s) => `\x1b[2m${s}\x1b[0m`;
|
|
14
|
+
const yellow = (s) => `\x1b[33m${s}\x1b[0m`;
|
|
15
|
+
function parseArgs(argv) {
|
|
16
|
+
const args = argv.slice(2);
|
|
17
|
+
let projectName = "";
|
|
18
|
+
let pm = "";
|
|
19
|
+
let skipInstall = false;
|
|
20
|
+
let help = false;
|
|
21
|
+
for (let i = 0; i < args.length; i++) {
|
|
22
|
+
const arg = args[i];
|
|
23
|
+
if (arg === "--help" || arg === "-h") {
|
|
24
|
+
help = true;
|
|
25
|
+
}
|
|
26
|
+
else if (arg === "--skip-install") {
|
|
27
|
+
skipInstall = true;
|
|
28
|
+
}
|
|
29
|
+
else if (arg === "--pm" && i + 1 < args.length) {
|
|
30
|
+
pm = args[++i];
|
|
31
|
+
}
|
|
32
|
+
else if (!arg.startsWith("-")) {
|
|
33
|
+
projectName = arg;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return { projectName, pm, skipInstall, help };
|
|
37
|
+
}
|
|
38
|
+
// ── Interactive prompts (zero deps) ──────────────────────────
|
|
39
|
+
function ask(message, fallback) {
|
|
40
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
41
|
+
const hint = fallback ? dim(` (${fallback})`) : "";
|
|
42
|
+
return new Promise((resolve) => {
|
|
43
|
+
rl.question(` ${message}${hint}: `, (answer) => {
|
|
44
|
+
rl.close();
|
|
45
|
+
resolve(answer.trim() || fallback || "");
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function choose(message, options, defaultIdx = 0) {
|
|
50
|
+
return new Promise(async (res) => {
|
|
51
|
+
console.log(`\n ${message}`);
|
|
52
|
+
for (let i = 0; i < options.length; i++) {
|
|
53
|
+
const marker = i === defaultIdx ? green("❯") : " ";
|
|
54
|
+
console.log(` ${marker} ${i + 1}. ${options[i]}`);
|
|
55
|
+
}
|
|
56
|
+
const answer = await ask(` Choose [1-${options.length}]`, String(defaultIdx + 1));
|
|
57
|
+
const idx = parseInt(answer) - 1;
|
|
58
|
+
res(options[idx] ?? options[defaultIdx]);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
// ── Package manager detection ────────────────────────────────
|
|
62
|
+
const VALID_PMS = ["npm", "pnpm", "yarn", "bun"];
|
|
63
|
+
function detectPM() {
|
|
64
|
+
for (const pm of ["pnpm", "yarn", "bun"]) {
|
|
65
|
+
try {
|
|
66
|
+
execSync(`${pm} --version`, { stdio: "ignore" });
|
|
67
|
+
return pm;
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
/* not installed */
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return "npm";
|
|
74
|
+
}
|
|
75
|
+
function installCmd(pm) {
|
|
76
|
+
return pm === "yarn" ? "yarn" : `${pm} install`;
|
|
77
|
+
}
|
|
78
|
+
function runCmd(pm, script) {
|
|
79
|
+
if (pm === "yarn")
|
|
80
|
+
return `yarn ${script}`;
|
|
81
|
+
if (pm === "bun")
|
|
82
|
+
return `bun run ${script}`;
|
|
83
|
+
return `${pm} run ${script}`;
|
|
84
|
+
}
|
|
85
|
+
// ── Template copy with _dotfile → .dotfile renaming ──────────
|
|
86
|
+
async function copyDir(src, dest) {
|
|
87
|
+
await mkdir(dest, { recursive: true });
|
|
88
|
+
const entries = await readdir(src, { withFileTypes: true });
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
const srcPath = join(src, entry.name);
|
|
91
|
+
// _gitignore → .gitignore, _env.example → .env.example
|
|
92
|
+
// But keep __tests__, __mocks__, etc. (double underscore = convention)
|
|
93
|
+
const destName = entry.name.startsWith("_") && !entry.name.startsWith("__")
|
|
94
|
+
? `.${entry.name.slice(1)}`
|
|
95
|
+
: entry.name;
|
|
96
|
+
const destPath = join(dest, destName);
|
|
97
|
+
if (entry.isDirectory()) {
|
|
98
|
+
await copyDir(srcPath, destPath);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
await cp(srcPath, destPath);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// ── Main ─────────────────────────────────────────────────────
|
|
106
|
+
async function main() {
|
|
107
|
+
const { projectName: argName, pm: argPM, skipInstall, help } = parseArgs(process.argv);
|
|
108
|
+
// ── Help ──
|
|
109
|
+
if (help) {
|
|
110
|
+
console.log(`
|
|
111
|
+
${bold("@samy-clivolt/create-pi-ag-ui")} — Scaffold a new Pi AG-UI project
|
|
112
|
+
|
|
113
|
+
${bold("Usage:")}
|
|
114
|
+
npx @samy-clivolt/create-pi-ag-ui ${dim("[project-name]")} ${dim("[options]")}
|
|
115
|
+
|
|
116
|
+
${bold("Options:")}
|
|
117
|
+
--pm ${dim("<npm|pnpm|yarn|bun>")} Package manager (default: auto-detect)
|
|
118
|
+
--skip-install Skip dependency installation
|
|
119
|
+
-h, --help Show this help
|
|
120
|
+
|
|
121
|
+
${bold("Examples:")}
|
|
122
|
+
npx @samy-clivolt/create-pi-ag-ui my-app
|
|
123
|
+
npx @samy-clivolt/create-pi-ag-ui my-app --pm pnpm
|
|
124
|
+
npx @samy-clivolt/create-pi-ag-ui my-app --skip-install
|
|
125
|
+
`);
|
|
126
|
+
process.exit(0);
|
|
127
|
+
}
|
|
128
|
+
// ── Banner ──
|
|
129
|
+
console.log(`
|
|
130
|
+
${bold("🚀 @samy-clivolt/create-pi-ag-ui")}
|
|
131
|
+
${dim("AG-UI protocol bridge with CopilotKit frontend")}
|
|
132
|
+
`);
|
|
133
|
+
// ── 1. Project name ──
|
|
134
|
+
let projectName = argName;
|
|
135
|
+
if (!projectName) {
|
|
136
|
+
if (!process.stdin.isTTY) {
|
|
137
|
+
console.error(red(" ✖ Project name is required in non-interactive mode."));
|
|
138
|
+
console.error(dim(" Usage: npx @samy-clivolt/create-pi-ag-ui <project-name>"));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
projectName = await ask("Project name", "my-pi-ag-ui-app");
|
|
142
|
+
}
|
|
143
|
+
if (!projectName) {
|
|
144
|
+
console.error(red(" ✖ Project name is required."));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
// Validate name
|
|
148
|
+
if (!/^[a-zA-Z0-9_@][a-zA-Z0-9._@/-]*$/.test(projectName)) {
|
|
149
|
+
console.error(red(` ✖ Invalid project name: "${projectName}"`));
|
|
150
|
+
process.exit(1);
|
|
151
|
+
}
|
|
152
|
+
const projectDir = resolve(process.cwd(), projectName);
|
|
153
|
+
if (existsSync(projectDir)) {
|
|
154
|
+
console.error(red(` ✖ Directory "${projectName}" already exists.`));
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
// ── 2. Package manager ──
|
|
158
|
+
let pm;
|
|
159
|
+
if (argPM && VALID_PMS.includes(argPM)) {
|
|
160
|
+
pm = argPM;
|
|
161
|
+
}
|
|
162
|
+
else if (process.stdin.isTTY) {
|
|
163
|
+
const detected = detectPM();
|
|
164
|
+
const choice = await choose("Package manager:", [...VALID_PMS], VALID_PMS.indexOf(detected));
|
|
165
|
+
pm = choice;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
pm = detectPM();
|
|
169
|
+
}
|
|
170
|
+
// ── 3. Scaffolding ──
|
|
171
|
+
console.log(`\n ${dim("Scaffolding")} ${bold(projectName)} ${dim("...")}`);
|
|
172
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
173
|
+
const __dirname = dirname(__filename);
|
|
174
|
+
const templateDir = resolve(__dirname, "..", "template");
|
|
175
|
+
if (!existsSync(templateDir)) {
|
|
176
|
+
console.error(red(" ✖ Template directory not found. Package may be corrupted."));
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
await copyDir(templateDir, projectDir);
|
|
180
|
+
// ── 4. Patch package.json ──
|
|
181
|
+
const pkgPath = join(projectDir, "package.json");
|
|
182
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf-8"));
|
|
183
|
+
pkg.name = projectName;
|
|
184
|
+
await writeFile(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
185
|
+
// ── 5. Create .env.local ──
|
|
186
|
+
const envExamplePath = join(projectDir, ".env.example");
|
|
187
|
+
const envLocalPath = join(projectDir, ".env.local");
|
|
188
|
+
if (existsSync(envExamplePath)) {
|
|
189
|
+
const envContent = await readFile(envExamplePath, "utf-8");
|
|
190
|
+
await writeFile(envLocalPath, envContent);
|
|
191
|
+
}
|
|
192
|
+
// ── 6. Init git repo ──
|
|
193
|
+
try {
|
|
194
|
+
execSync("git init", { cwd: projectDir, stdio: "ignore" });
|
|
195
|
+
execSync("git add -A", { cwd: projectDir, stdio: "ignore" });
|
|
196
|
+
execSync('git commit -m "Initial commit from @samy-clivolt/create-pi-ag-ui"', {
|
|
197
|
+
cwd: projectDir,
|
|
198
|
+
stdio: "ignore",
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
// git not available — skip silently
|
|
203
|
+
}
|
|
204
|
+
// ── 7. Install deps ──
|
|
205
|
+
if (!skipInstall) {
|
|
206
|
+
console.log(` ${dim(`Installing dependencies with ${pm}...`)}\n`);
|
|
207
|
+
try {
|
|
208
|
+
execSync(installCmd(pm), { cwd: projectDir, stdio: "inherit" });
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
console.warn(`\n ${yellow("⚠")} Failed to install dependencies. Run manually:`);
|
|
212
|
+
console.warn(` cd ${projectName} && ${installCmd(pm)}\n`);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
// ── 8. Done ──
|
|
216
|
+
console.log(`
|
|
217
|
+
${green("✅ Project created successfully!")}
|
|
218
|
+
|
|
219
|
+
${bold("Next steps:")}
|
|
220
|
+
|
|
221
|
+
${cyan(`cd ${projectName}`)}
|
|
222
|
+
${skipInstall ? ` ${cyan(installCmd(pm))}\n` : ""} ${dim("# Add your API key to .env.local")}
|
|
223
|
+
${yellow("ANTHROPIC_API_KEY=sk-ant-...")}
|
|
224
|
+
|
|
225
|
+
${cyan(runCmd(pm, "dev"))}
|
|
226
|
+
|
|
227
|
+
${dim("Then open")} ${cyan("http://localhost:3000")}
|
|
228
|
+
|
|
229
|
+
${dim("─────────────────────────────────────────")}
|
|
230
|
+
${dim("Docs: https://github.com/samy-clivolt/pi-ag-ui")}
|
|
231
|
+
`);
|
|
232
|
+
}
|
|
233
|
+
main().catch((err) => {
|
|
234
|
+
console.error(red(`\n ✖ ${err.message}`));
|
|
235
|
+
process.exit(1);
|
|
236
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@samy-clivolt/create-pi-ag-ui",
|
|
3
|
+
"version": "0.1.2",
|
|
4
|
+
"description": "Scaffold a Pi AG-UI project — AG-UI protocol bridge with CopilotKit frontend",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-pi-ag-ui": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"template"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "tsc",
|
|
15
|
+
"dev": "tsc --watch",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
17
|
+
},
|
|
18
|
+
"keywords": [
|
|
19
|
+
"ag-ui",
|
|
20
|
+
"copilotkit",
|
|
21
|
+
"pi",
|
|
22
|
+
"agent",
|
|
23
|
+
"scaffold",
|
|
24
|
+
"create",
|
|
25
|
+
"template"
|
|
26
|
+
],
|
|
27
|
+
"author": "",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"registry": "https://registry.npmjs.org",
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"typescript": "^5.6.0"
|
|
39
|
+
}
|
|
40
|
+
}
|