diff --git a/packages/cli/src/cli/commands/project/create.ts b/packages/cli/src/cli/commands/project/create.ts index 2ebe6bb7..a6c5fb82 100644 --- a/packages/cli/src/cli/commands/project/create.ts +++ b/packages/cli/src/cli/commands/project/create.ts @@ -49,7 +49,9 @@ function validateNonInteractiveFlags(command: Command): void { const { path } = command.opts(); if (path && !command.args.length) { - command.error("Non-interactive mode requires all flags: --name, --path"); + command.error( + "--path requires a project name argument. Usage: base44 create --path ", + ); } } @@ -113,6 +115,8 @@ async function createInteractive( async function createNonInteractive( options: CreateOptions, ): Promise { + log.info(`Creating a new project at ${resolve(options.path!)}`); + const template = await getTemplateById( options.template ?? DEFAULT_TEMPLATE_ID, ); @@ -290,8 +294,20 @@ export function getCreateCommand(context: CLIContext): Command { ) .option("--deploy", "Build and deploy the site") .option("--no-skills", "Skip AI agent skills installation") + .addHelpText( + "after", + ` +Examples: + $ base44 create my-app Creates a base44 project at ./my-app + $ base44 create my-todo-app --template backend-and-client Creates a base44 backend-and-client project at ./my-todo-app + $ base44 create my-app --path ./projects/my-app --deploy Creates a base44 project at ./project/my-app and deploys it`, + ) .hook("preAction", validateNonInteractiveFlags) .action(async (name: string | undefined, options: CreateOptions) => { + if (name && !options.path) { + options.path = `./${kebabCase(name)}`; + } + const isNonInteractive = !!(options.name ?? name) && !!options.path; if (isNonInteractive) { diff --git a/packages/cli/tests/cli/create.spec.ts b/packages/cli/tests/cli/create.spec.ts index 10fa5b42..c254cd86 100644 --- a/packages/cli/tests/cli/create.spec.ts +++ b/packages/cli/tests/cli/create.spec.ts @@ -13,7 +13,7 @@ describe("create command", () => { const result = await t.run("create", "--path", "./my-project"); t.expectResult(result).toFail(); - t.expectResult(result).toContain("Non-interactive mode requires all flags"); + t.expectResult(result).toContain("--path requires a project name argument"); }); it("creates project in non-interactive mode", async () => { @@ -36,6 +36,17 @@ describe("create command", () => { t.expectResult(result).toContain("new-project-id"); }); + it("infers path from name when --path is not provided", async () => { + await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); + t.api.mockCreateApp({ id: "inferred-path-id", name: "My App" }); + + const result = await t.run("create", "My App", "--no-skills"); + + t.expectResult(result).toSucceed(); + t.expectResult(result).toContain("Creating a new project at"); + t.expectResult(result).toContain("Project created successfully"); + }); + it("creates project with custom template", async () => { await t.givenLoggedIn({ email: "test@example.com", name: "Test User" }); t.api.mockCreateApp({