@kya-os/create-xmcpi-app
Version:
Bootstrap XMCP applications with identity features
290 lines • 11.5 kB
JavaScript
import path from "path";
import fs from "fs-extra";
import chalk from "chalk";
import { Command } from "commander";
import inquirer from "inquirer";
import ora from "ora";
import { fileURLToPath } from "url";
import { checkNodeVersion } from "./utils/check-node.js";
import { createProject } from "./helpers/create.js";
import { isFolderEmpty } from "./utils/is-folder-empty.js";
import { createKYAOSBanner } from "./effects/index.js";
checkNodeVersion();
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const packageJson = JSON.parse(fs.readFileSync(path.join(__dirname, "../package.json"), "utf8"));
const program = new Command()
.name("create-xmcpi-app")
.description("Create a new xmcp application with identity features built-in")
.version(packageJson.version, "-v, --version", "Output the current version of create-xmcpi-app.")
.argument("[directory]")
.usage("[directory] [options]")
.helpOption("-h, --help", "Display help message.")
.option("-y, --yes", "Skip confirmation prompt", false)
.option("--use-npm", "Use npm as package manager (default: use npm)")
.option("--use-yarn", "Use yarn as package manager")
.option("--use-pnpm", "Use pnpm as package manager")
.option("--skip-install", "Skip installing dependencies", false)
.option("--skip-identity", "Skip identity generation", false)
.option("--vercel", "Add Vercel support for deployment", false)
.option("--http", "Enable HTTP transport", false)
.option("--stdio", "Enable STDIO transport", false)
.option("--local", "Use local xmcp-i dependency", false)
.option("--no-animation", "Skip the black hole animation", false)
.option("--fast", "Use shorter animation duration", false)
.action(async (projectDir, options) => {
console.log(chalk.bold(`\ncreate-xmcpi-app@${packageJson.version}`));
// Show KYA-OS banner
const banner = await createKYAOSBanner();
console.log(chalk.cyan(banner));
console.log(chalk.dim("\nEnhanced with identity features by MCP-I\n"));
// If project directory wasn't specified, ask for it
if (!projectDir) {
const answers = await inquirer.prompt([
{
type: "input",
name: "projectDir",
message: "What is your project named?",
default: "my-xmcpi-app",
},
]);
projectDir = answers.projectDir;
}
// Normalize project directory
const resolvedProjectPath = path.resolve(process.cwd(), projectDir);
const projectName = path.basename(resolvedProjectPath);
// Check if directory exists
if (fs.existsSync(resolvedProjectPath)) {
const stats = fs.statSync(resolvedProjectPath);
if (!stats.isDirectory()) {
console.error(chalk.red(`Error: ${projectName} exists but is not a directory.`));
process.exit(1);
}
// Check if directory is empty
if (!isFolderEmpty(resolvedProjectPath, projectName)) {
console.error(chalk.red(`The directory ${resolvedProjectPath} is not empty.`));
process.exit(1);
}
}
let packageManager = "npm";
let useLocalXmcp = options.local;
let deployToVercel = options.vercel;
let skipInstall = options.skipInstall;
let skipIdentity = options.skipIdentity;
let transports = ["http"];
let agentName = projectName;
let agentDescription = "XMCP-I server with identity features";
let agentRepository = "";
// Handle transport selection from CLI options
if (options.http || options.stdio) {
transports = [];
if (options.http)
transports.push("http");
if (options.stdio)
transports.push("stdio");
}
if (!options.yes) {
// Package manager selection
if (options.useYarn)
packageManager = "yarn";
if (options.usePnpm)
packageManager = "pnpm";
if (!options.useYarn && !options.usePnpm && !options.useNpm) {
const pmAnswers = await inquirer.prompt([
{
type: "list",
name: "packageManager",
message: "Select a package manager:",
choices: [
{ name: "npm", value: "npm" },
{ name: "yarn", value: "yarn" },
{ name: "pnpm", value: "pnpm" },
],
default: "npm",
},
]);
packageManager = pmAnswers.packageManager;
}
// Transport selection (skip if already specified via CLI options)
if (!options.http && !options.stdio) {
const transportAnswers = await inquirer.prompt([
{
type: "checkbox",
name: "transports",
message: "Select the transports you want to use:",
choices: [
{
name: "HTTP (runs on a server)",
value: "http",
checked: true,
},
{
name: "STDIO (runs on the user's machine)",
value: "stdio",
checked: false,
},
],
validate: (input) => {
if (input.length === 0) {
return "You must select at least one transport.";
}
return true;
},
},
]);
transports = transportAnswers.transports;
}
// Vercel deployment option
if (!options.vercel && transports.includes("http")) {
const vercelAnswers = await inquirer.prompt([
{
type: "confirm",
name: "deployToVercel",
message: "Add Vercel deployment support?",
default: true,
},
]);
deployToVercel = vercelAnswers.deployToVercel;
}
// Agent information for identity generation
if (!skipIdentity) {
console.log(chalk.blue("\n🤖 Agent Configuration:"));
const agentInfo = await inquirer.prompt([
{
type: "input",
name: "name",
message: "What's your agent name?",
default: projectName,
},
{
type: "input",
name: "description",
message: "Brief description of your agent:",
default: "XMCP-I server with identity features",
},
{
type: "input",
name: "repository",
message: "Repository URL (optional):",
default: "",
},
]);
agentName = agentInfo.name;
agentDescription = agentInfo.description;
agentRepository = agentInfo.repository;
}
console.log();
console.log(`Creating a new xmcp app in ${chalk.green(resolvedProjectPath)}.\n`);
const { confirmed } = await inquirer.prompt([
{
type: "confirm",
name: "confirmed",
message: "Ok to continue?",
default: true,
},
]);
if (!confirmed) {
console.log(chalk.yellow("Aborting installation."));
process.exit(0);
}
}
else {
// Use command-line options when --yes is provided
if (options.useYarn)
packageManager = "yarn";
if (options.usePnpm)
packageManager = "pnpm";
}
const spinner = ora("Creating your xmcp-i app with identity features...").start();
try {
spinner.text = "Scaffolding project structure...";
await createProject({
projectPath: resolvedProjectPath,
projectName,
packageManager,
transports: transports,
packageVersion: packageJson.version,
useLocalXmcp,
deployToVercel,
skipInstall,
agentName,
agentDescription,
agentRepository,
skipIdentity,
skipAnimation: options.noAnimation === false ? true : false, // Handle --no-animation flag
fastAnimation: options.fast,
spinner, // Pass spinner to stop it before animation
});
spinner.succeed(chalk.green("Your xmcp-i app is ready"));
console.log();
console.log("Next Steps:");
// Show agent management with claim URL first
if (!skipIdentity) {
console.log("Agent management:");
console.log(" Follow the Claim URL above to claim your agent");
if (resolvedProjectPath !== process.cwd()) {
console.log(` cd ${chalk.cyan(projectDir)}`);
}
console.log(` ${chalk.cyan(`${packageManager} run kya-check`)} - Check agent status`);
console.log(` ${chalk.cyan(`${packageManager} run kya-env`)} - Show environment variables`);
console.log();
}
console.log("Start agent:");
if (resolvedProjectPath !== process.cwd()) {
console.log(` cd ${chalk.cyan(projectDir)}`);
}
if (skipInstall) {
if (packageManager === "yarn") {
console.log(` ${chalk.cyan("yarn install")}`);
}
else if (packageManager === "pnpm") {
console.log(` ${chalk.cyan("pnpm install")}`);
}
else {
console.log(` ${chalk.cyan("npm install")}`);
}
}
// Show appropriate command based on transport
if (transports.includes("stdio")) {
console.log(` ${chalk.cyan("npx xmcp build")}`);
}
else {
// HTTP transport - show dev command
if (packageManager === "yarn") {
console.log(` ${chalk.cyan("yarn dev")}`);
}
else if (packageManager === "pnpm") {
console.log(` ${chalk.cyan("pnpm dev")}`);
}
else {
console.log(` ${chalk.cyan("npm run dev")}`);
}
}
console.log();
console.log("Test your agent:");
if (resolvedProjectPath !== process.cwd()) {
console.log(` cd ${chalk.cyan(projectDir)}`);
}
// Show test:greet command
if (packageManager === "yarn") {
console.log(` ${chalk.cyan("yarn test:greet")}`);
}
else if (packageManager === "pnpm") {
console.log(` ${chalk.cyan("pnpm test:greet")}`);
}
else {
console.log(` ${chalk.cyan("npm run test:greet")}`);
}
console.log();
// Explicitly exit after successful completion
process.exit(0);
}
catch (error) {
spinner.fail(chalk.red("Failed to create the project."));
console.error(error);
process.exit(1);
}
});
program.parse(process.argv);
//# sourceMappingURL=index.js.map