create-kai 0.2.2 → 0.3.0

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.
Files changed (2) hide show
  1. package/dist/index.js +277 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -159,9 +159,9 @@ var require_picocolors = __commonJS({
159
159
 
160
160
  // src/index.ts
161
161
  import { spawn } from "node:child_process";
162
- import { readdir as readdir2 } from "node:fs/promises";
163
- import { existsSync as existsSync2 } from "node:fs";
164
- import path2 from "node:path";
162
+ import { readdir as readdir3 } from "node:fs/promises";
163
+ import { existsSync as existsSync3 } from "node:fs";
164
+ import path4 from "node:path";
165
165
  import process2 from "node:process";
166
166
 
167
167
  // node_modules/@clack/prompts/dist/index.mjs
@@ -864,6 +864,9 @@ var Y2 = ({ indicator: t = "dots" } = {}) => {
864
864
  // src/index.ts
865
865
  var import_picocolors3 = __toESM(require_picocolors(), 1);
866
866
 
867
+ // src/args.ts
868
+ import path from "node:path";
869
+
867
870
  // ../ui/src/agent-tooling/integrations/openai.ts
868
871
  var openai = {
869
872
  id: "openai",
@@ -3677,7 +3680,8 @@ var TAKES_VALUE = /* @__PURE__ */ new Set([
3677
3680
  "--widget-style",
3678
3681
  "--features",
3679
3682
  "--gateway",
3680
- "--kit"
3683
+ "--kit",
3684
+ "--shape"
3681
3685
  ]);
3682
3686
  function parseArgs(argv) {
3683
3687
  const out = { yes: false, list: false, json: false, help: false, version: false, errors: [] };
@@ -3706,6 +3710,9 @@ function parseArgs(argv) {
3706
3710
  case "--widget-style":
3707
3711
  out.widgetStyle = value;
3708
3712
  break;
3713
+ case "--shape":
3714
+ out.shape = value;
3715
+ break;
3709
3716
  case "--features":
3710
3717
  out.features = value === void 0 || value === "" || value === "none" ? [] : value.split(",").map((f) => f.trim()).filter(Boolean);
3711
3718
  break;
@@ -3758,6 +3765,15 @@ function normalizeGateway(value) {
3758
3765
  if (value === void 0) return void 0;
3759
3766
  return value === "none" ? "mock" : value;
3760
3767
  }
3768
+ function sanitizeProjectName(raw) {
3769
+ return raw.toLowerCase().replace(/[^a-z0-9-._~]/g, "-").replace(/^[._]+/, "");
3770
+ }
3771
+ function defaultNameForTarget(dirArg, cwd) {
3772
+ if (dirArg === void 0) return ZERO_CONFIG.name;
3773
+ const resolved = path.resolve(cwd, dirArg);
3774
+ const sanitized = sanitizeProjectName(path.basename(resolved));
3775
+ return sanitized.length > 0 && validateProjectName(sanitized) === null ? sanitized : ZERO_CONFIG.name;
3776
+ }
3761
3777
  function validateProjectName(name) {
3762
3778
  if (name.length === 0) return "Project name cannot be empty";
3763
3779
  if (name.length > 214) return "Project name must be 214 characters or fewer";
@@ -3841,7 +3857,7 @@ async function answerAxis(axis, opts, io) {
3841
3857
  // src/generate.ts
3842
3858
  import { cp, mkdir, mkdtemp, readFile, readdir, rename, rm, stat, writeFile } from "node:fs/promises";
3843
3859
  import { existsSync } from "node:fs";
3844
- import path from "node:path";
3860
+ import path2 from "node:path";
3845
3861
  import { fileURLToPath } from "node:url";
3846
3862
 
3847
3863
  // src/kai-json.ts
@@ -4329,7 +4345,7 @@ function travellingName(dotfile) {
4329
4345
  // src/generate.ts
4330
4346
  var GITIGNORE_TEMPLATE_NAME = travellingName(GITIGNORE_SOURCE_NAME);
4331
4347
  function defaultTemplateRoot() {
4332
- return path.join(path.dirname(fileURLToPath(import.meta.url)), "templates");
4348
+ return path2.join(path2.dirname(fileURLToPath(import.meta.url)), "templates");
4333
4349
  }
4334
4350
  async function generate(plan, options = {}) {
4335
4351
  const framework = getFramework(plan.frameworkId);
@@ -4343,27 +4359,27 @@ async function generate(plan, options = {}) {
4343
4359
  }
4344
4360
  const emittedFeatures = surface.surface.features;
4345
4361
  const templateRoot = options.templateRoot ?? defaultTemplateRoot();
4346
- const templateDir = path.join(templateRoot, framework.templateDir);
4362
+ const templateDir = path2.join(templateRoot, framework.templateDir);
4347
4363
  if (!existsSync(templateDir)) {
4348
4364
  throw new Error(
4349
4365
  `create-kai: no template for '${framework.id}' at ${templateDir}. Run the package build (it copies examples/starters/* into dist/templates).`
4350
4366
  );
4351
4367
  }
4352
- const parent = path.dirname(plan.dir);
4368
+ const parent = path2.dirname(plan.dir);
4353
4369
  await mkdir(parent, { recursive: true });
4354
- const out = await mkdtemp(path.join(parent, ".create-kai-"));
4370
+ const out = await mkdtemp(path2.join(parent, ".create-kai-"));
4355
4371
  let previousKitSpec;
4356
4372
  try {
4357
4373
  await cp(templateDir, out, { recursive: true });
4358
4374
  for (const dotfile of STRIPPED_DOTFILES) {
4359
- const travelling = path.join(out, travellingName(dotfile));
4360
- if (existsSync(travelling)) await rename(travelling, path.join(out, dotfile));
4375
+ const travelling = path2.join(out, travellingName(dotfile));
4376
+ if (existsSync(travelling)) await rename(travelling, path2.join(out, dotfile));
4361
4377
  }
4362
4378
  for (const patch of patchesFor(framework.templateDir)) {
4363
- const file = path.join(out, patch.file);
4379
+ const file = path2.join(out, patch.file);
4364
4380
  await writeFile(file, applyPatch(patch, await readFile(file, "utf8"), plan.name), "utf8");
4365
4381
  }
4366
- const pkgPath = path.join(out, "package.json");
4382
+ const pkgPath = path2.join(out, "package.json");
4367
4383
  const rewritten = rewritePackageJson(await readFile(pkgPath, "utf8"), {
4368
4384
  name: plan.name,
4369
4385
  kit: plan.kit,
@@ -4371,16 +4387,16 @@ async function generate(plan, options = {}) {
4371
4387
  });
4372
4388
  previousKitSpec = rewritten.previousKitSpec;
4373
4389
  await writeFile(pkgPath, stringifyPackageJson(rewritten.json), "utf8");
4374
- const appSource = await readFile(path.join(out, framework.paths.app), "utf8");
4390
+ const appSource = await readFile(path2.join(out, framework.paths.app), "utf8");
4375
4391
  const thread = goLiveThread(appSource, framework);
4376
4392
  const routeFiles = await writeGateway(plan, framework, integration, out, thread);
4377
4393
  await writeFile(
4378
- path.join(out, "kai.json"),
4394
+ path2.join(out, "kai.json"),
4379
4395
  stringifyKaiJson(buildKaiJson({ ...plan, featureIds: emittedFeatures }, framework)),
4380
4396
  "utf8"
4381
4397
  );
4382
4398
  await writeFile(
4383
- path.join(out, "README.md"),
4399
+ path2.join(out, "README.md"),
4384
4400
  plan.gatewayId === "mock" ? renderMockReadme(plan, framework, thread, integration.docsSlug) : renderGatewayReadme(plan, framework, integration, routeFiles[0].path),
4385
4401
  "utf8"
4386
4402
  );
@@ -4419,12 +4435,12 @@ async function writeGateway(plan, framework, integration, out, thread) {
4419
4435
  );
4420
4436
  }
4421
4437
  for (const file of routeFiles) {
4422
- const abs = path.join(out, file.path);
4423
- await mkdir(path.dirname(abs), { recursive: true });
4438
+ const abs = path2.join(out, file.path);
4439
+ await mkdir(path2.dirname(abs), { recursive: true });
4424
4440
  await writeFile(abs, file.contents, "utf8");
4425
4441
  }
4426
4442
  for (const patch of gatewayPatchesFor(framework.templateDir)) {
4427
- const file = path.join(out, patch.file);
4443
+ const file = path2.join(out, patch.file);
4428
4444
  await writeFile(
4429
4445
  file,
4430
4446
  applyGatewayPatch(patch, await readFile(file, "utf8"), {
@@ -4438,7 +4454,7 @@ async function writeGateway(plan, framework, integration, out, thread) {
4438
4454
  }
4439
4455
  if (integration.envVars.length > 0) {
4440
4456
  await writeFile(
4441
- path.join(out, framework.paths.env),
4457
+ path2.join(out, framework.paths.env),
4442
4458
  renderEnvFile(integration.envVars, integration.runNote),
4443
4459
  "utf8"
4444
4460
  );
@@ -4553,7 +4569,7 @@ async function listFiles(dir, prefix = "") {
4553
4569
  const out = [];
4554
4570
  for (const entry of await readdir(dir)) {
4555
4571
  if (entry === "node_modules") continue;
4556
- const abs = path.join(dir, entry);
4572
+ const abs = path2.join(dir, entry);
4557
4573
  const rel = prefix ? `${prefix}/${entry}` : entry;
4558
4574
  if ((await stat(abs)).isDirectory()) out.push(...await listFiles(abs, rel));
4559
4575
  else out.push(rel);
@@ -4574,11 +4590,166 @@ function detectPackageManager(userAgent = process.env.npm_config_user_agent) {
4574
4590
  return KNOWN.npm;
4575
4591
  }
4576
4592
 
4593
+ // src/wizard.ts
4594
+ import { existsSync as existsSync2 } from "node:fs";
4595
+ import { mkdir as mkdir2, readdir as readdir2, writeFile as writeFile2 } from "node:fs/promises";
4596
+ import path3 from "node:path";
4597
+ var SCHEMA_URL = "https://ui.kitn.ai/schemas/construct/v1.json";
4598
+ function shapeAxis() {
4599
+ const options = [
4600
+ {
4601
+ id: "widget",
4602
+ label: "Embedded widget",
4603
+ hint: "a floating launcher that sits on top of an existing page"
4604
+ },
4605
+ {
4606
+ id: "fullscreen",
4607
+ label: "Full-screen chat",
4608
+ hint: "the chat is the whole page"
4609
+ },
4610
+ {
4611
+ id: "app",
4612
+ label: "Full app",
4613
+ hint: "a scaffolded project with routing and a shell, not just a chat construct"
4614
+ }
4615
+ ];
4616
+ return {
4617
+ id: "shape",
4618
+ label: "Shape",
4619
+ question: "What are you building?",
4620
+ options,
4621
+ because: 'each shape needs a different tool: "widget" and "fullscreen" compose a construct (this wizard), while "app" needs the project scaffold this wizard does not build'
4622
+ };
4623
+ }
4624
+ var DEFAULT_ATTACHMENTS_ACCEPT = ["image/*", "application/pdf"];
4625
+ var DEFAULT_HOME_GREETING_TITLE = "How can we help?";
4626
+ var CONSTRUCT_TAG_RE = /^[a-z][a-z0-9]*-[a-z0-9-]+$/;
4627
+ function constructTagName(projectName, shape) {
4628
+ if (CONSTRUCT_TAG_RE.test(projectName)) return projectName;
4629
+ let base = projectName.toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
4630
+ if (!/^[a-z]/.test(base)) {
4631
+ base = base.length > 0 ? `k-${base}` : "k";
4632
+ }
4633
+ const suffix = shape === "widget" ? "widget" : "chat";
4634
+ return `${base}-${suffix}`;
4635
+ }
4636
+ function composeConstruct(a) {
4637
+ const capabilities = {};
4638
+ if (a.starters.length > 0) capabilities.starters = a.starters;
4639
+ if (a.attachments) capabilities.attachments = { accept: DEFAULT_ATTACHMENTS_ACCEPT };
4640
+ if (a.history) {
4641
+ capabilities.history = { persistence: "local" };
4642
+ capabilities.conversations = true;
4643
+ }
4644
+ const construct = {
4645
+ $schema: SCHEMA_URL,
4646
+ // NOT `a.name` — see `constructTagName`'s header. The project directory
4647
+ // name and the construct's own `Construct.name` are different
4648
+ // vocabularies (directory rules vs. a custom-element tag rule), and this
4649
+ // is the one place they're allowed to diverge.
4650
+ name: constructTagName(a.name, a.shape),
4651
+ layout: a.shape,
4652
+ provider: { mode: "mock" }
4653
+ };
4654
+ if (a.headerTitle && a.headerTitle.length > 0) {
4655
+ construct.header = { title: a.headerTitle };
4656
+ }
4657
+ if (a.home) {
4658
+ const title = a.homeGreeting && a.homeGreeting.length > 0 ? a.homeGreeting : DEFAULT_HOME_GREETING_TITLE;
4659
+ construct.home = { greeting: { title } };
4660
+ }
4661
+ if (a.accent && a.accent.length > 0) {
4662
+ construct.theme = { accent: a.accent };
4663
+ }
4664
+ if (Object.keys(capabilities).length > 0) {
4665
+ construct.capabilities = capabilities;
4666
+ }
4667
+ return construct;
4668
+ }
4669
+ async function emitConstruct(dir, answers) {
4670
+ if (existsSync2(dir) && (await readdir2(dir)).length > 0) {
4671
+ throw new Error(`${dir} already exists and is not empty`);
4672
+ }
4673
+ await mkdir2(dir, { recursive: true });
4674
+ const construct = composeConstruct(answers);
4675
+ const fileName = `${answers.name}.construct.json`;
4676
+ const file = path3.join(dir, fileName);
4677
+ await writeFile2(file, `${JSON.stringify(construct, null, 2)}
4678
+ `, "utf8");
4679
+ return {
4680
+ file,
4681
+ devCommand: `npx @kitn.ai/ui dev ${fileName}`,
4682
+ constructName: constructTagName(answers.name, answers.shape)
4683
+ };
4684
+ }
4685
+ function runDevPreview(devCommand, cwd, spawnFn) {
4686
+ return new Promise((resolve) => {
4687
+ const [command, ...args] = devCommand.split(" ");
4688
+ const child = spawnFn(command, args, {
4689
+ cwd,
4690
+ stdio: "inherit",
4691
+ shell: process.platform === "win32"
4692
+ });
4693
+ child.on("error", (err) => {
4694
+ resolve({ ok: false, message: `could not start the preview: ${err.message}` });
4695
+ });
4696
+ child.on("close", (code, signal) => {
4697
+ if (code === 0 || code === null) {
4698
+ resolve({ ok: true, message: null });
4699
+ return;
4700
+ }
4701
+ resolve({
4702
+ ok: false,
4703
+ message: `preview exited with code ${code}${signal ? ` (${signal})` : ""}`
4704
+ });
4705
+ });
4706
+ });
4707
+ }
4708
+ async function runWizard(shape, name, io, nonInteractive) {
4709
+ if (nonInteractive) {
4710
+ return {
4711
+ name,
4712
+ shape,
4713
+ headerTitle: "",
4714
+ home: false,
4715
+ homeGreeting: "",
4716
+ starters: [],
4717
+ attachments: false,
4718
+ history: false,
4719
+ accent: ""
4720
+ };
4721
+ }
4722
+ io.state("Schema", `${SCHEMA_URL} \u2014 every construct the wizard emits stamps this so tooling can validate it`);
4723
+ io.state("Name", `${name} \u2014 the project directory already fixed this`);
4724
+ io.state("Provider", "mock \u2014 a keyless first run; switch providers in the construct file after");
4725
+ const headerTitle = await io.text("Header title? (leave blank for none)", "");
4726
+ const accent = await io.text("Accent color? (leave blank for the kit default)", "");
4727
+ const home = await io.confirm("Show a home/greeting screen?", true);
4728
+ const homeGreeting = home ? await io.text("Greeting title? (leave blank for the default)", DEFAULT_HOME_GREETING_TITLE) : "";
4729
+ const starters = await io.multilineList("Starter prompts (comma-separated, up to 6, blank to skip)");
4730
+ const attachments = await io.confirm("Allow file attachments?", false);
4731
+ const history = await io.confirm("Persist conversation history in this browser?", true);
4732
+ if (history) {
4733
+ io.state("Conversations", "enabled \u2014 turned on automatically because history is on");
4734
+ }
4735
+ return {
4736
+ name,
4737
+ shape,
4738
+ headerTitle,
4739
+ home,
4740
+ homeGreeting,
4741
+ starters: starters.slice(0, 6),
4742
+ attachments,
4743
+ history,
4744
+ accent
4745
+ };
4746
+ }
4747
+
4577
4748
  // src/index.ts
4578
4749
  var OFFERABLE_FEATURE_IDS = [
4579
4750
  ...new Set(readyFrameworks().flatMap((f) => availableFeatures(f).map((feature) => feature.id)))
4580
4751
  ];
4581
- var DEFAULT_KIT_RANGE = "^0.28.0";
4752
+ var DEFAULT_KIT_RANGE = "^0.29.0";
4582
4753
  var HELP = `
4583
4754
  ${import_picocolors3.default.bold("create-kai")} \u2014 scaffold a runnable @kitn.ai/ui chat app
4584
4755
 
@@ -4609,7 +4780,7 @@ async function main() {
4609
4780
  return 0;
4610
4781
  }
4611
4782
  if (args.version) {
4612
- console.log("0.2.2");
4783
+ console.log("0.3.0");
4613
4784
  return 0;
4614
4785
  }
4615
4786
  if (args.list) {
@@ -4618,7 +4789,7 @@ async function main() {
4618
4789
  }
4619
4790
  const nonInteractive = args.yes || !process2.stdout.isTTY;
4620
4791
  Ie(import_picocolors3.default.bgMagenta(import_picocolors3.default.black(" create-kai ")));
4621
- const defaultName = args.dir ?? ZERO_CONFIG.name;
4792
+ const defaultName = defaultNameForTarget(args.dir, process2.cwd());
4622
4793
  const name = nonInteractive ? defaultName : await ask(
4623
4794
  he({
4624
4795
  message: "Project name",
@@ -4629,10 +4800,24 @@ async function main() {
4629
4800
  );
4630
4801
  const nameError = validateProjectName(name);
4631
4802
  if (nameError) return fail(nameError);
4632
- const dir = path2.resolve(process2.cwd(), args.dir ?? name);
4633
- if (existsSync2(dir) && (await readdir2(dir)).length > 0) {
4803
+ const dir = path4.resolve(process2.cwd(), args.dir ?? name);
4804
+ if (existsSync3(dir) && (await readdir3(dir)).length > 0) {
4634
4805
  return fail(`${dir} already exists and is not empty`);
4635
4806
  }
4807
+ const shapes = shapeAxis();
4808
+ if (args.shape !== void 0 && !shapes.options.some((o2) => o2.id === args.shape)) {
4809
+ return fail(
4810
+ `unknown shape '${args.shape}'. Available: ${shapes.options.map((o2) => o2.id).join(", ")}`
4811
+ );
4812
+ }
4813
+ const shapeId = await answerAxis(
4814
+ shapes,
4815
+ { override: args.shape, nonInteractive, fallback: "app" },
4816
+ clackAxisIo
4817
+ );
4818
+ if (shapeId !== "app") {
4819
+ return runConstructFlow(shapeId, name, dir, nonInteractive);
4820
+ }
4636
4821
  const frameworkId = args.framework ?? (nonInteractive ? ZERO_CONFIG.framework : await ask(
4637
4822
  ve({
4638
4823
  message: "Which framework?",
@@ -4729,7 +4914,7 @@ Available for ${framework.label}: ${featureList(offered)}`);
4729
4914
  // which kit the CLI was built against, which stays true when `--kit` sends
4730
4915
  // the dependency somewhere else — the emitted files are this version's shape
4731
4916
  // whatever the project ends up installing.
4732
- kitBuiltAgainst: "0.28.0"
4917
+ kitBuiltAgainst: "0.29.0"
4733
4918
  };
4734
4919
  const spinner = Y2();
4735
4920
  spinner.start("Scaffolding");
@@ -4740,7 +4925,7 @@ Available for ${framework.label}: ${featureList(offered)}`);
4740
4925
  spinner.stop("Scaffolding failed");
4741
4926
  return fail(error instanceof Error ? error.message : String(error));
4742
4927
  }
4743
- spinner.stop(`Scaffolded ${import_picocolors3.default.cyan(path2.relative(process2.cwd(), dir) || ".")}`);
4928
+ spinner.stop(`Scaffolded ${import_picocolors3.default.cyan(path4.relative(process2.cwd(), dir) || ".")}`);
4744
4929
  const pm = detectPackageManager();
4745
4930
  const shouldInstall = args.install ?? (nonInteractive ? false : await ask(ye({ message: `Install dependencies with ${pm.name}?`, initialValue: true })));
4746
4931
  let installed = false;
@@ -4755,7 +4940,7 @@ Available for ${framework.label}: ${featureList(offered)}`);
4755
4940
  install.stop(import_picocolors3.default.yellow(`${pm.name} install exited ${code} \u2014 run it yourself before ${pm.run}`));
4756
4941
  }
4757
4942
  }
4758
- const relative = path2.relative(process2.cwd(), dir);
4943
+ const relative = path4.relative(process2.cwd(), dir);
4759
4944
  const integration = gateways.find((g2) => g2.integration.id === gatewayId)?.integration;
4760
4945
  const keyStep = gatewayId !== "mock" && integration && integration.envVars.length > 0 ? `# put your key in ${framework.paths.env} (${integration.envVars.join(", ")})` : null;
4761
4946
  const steps = [
@@ -4782,6 +4967,67 @@ async function ask(prompt) {
4782
4967
  }
4783
4968
  return value;
4784
4969
  }
4970
+ async function runConstructFlow(shape, name, dir, nonInteractive) {
4971
+ const wizardIo = {
4972
+ text: (msg, initial) => ask(he({ message: msg, defaultValue: initial ?? "", placeholder: initial })),
4973
+ confirm: (msg, initial) => ask(ye({ message: msg, initialValue: initial })),
4974
+ // A single comma-separated line rather than a repeated prompt, to keep the
4975
+ // whole wizard to one screen — the brief's own call, not a schema
4976
+ // requirement.
4977
+ multilineList: async (msg) => {
4978
+ const raw = await ask(
4979
+ he({
4980
+ message: msg,
4981
+ placeholder: "e.g. Summarize this page, What can you help with?",
4982
+ defaultValue: ""
4983
+ })
4984
+ );
4985
+ return raw.split(",").map((s) => s.trim()).filter(Boolean).slice(0, 6);
4986
+ },
4987
+ state: stated
4988
+ };
4989
+ const answers = await runWizard(shape, name, wizardIo, nonInteractive);
4990
+ const spinner = Y2();
4991
+ spinner.start("Writing construct");
4992
+ let result;
4993
+ try {
4994
+ result = await emitConstruct(dir, answers);
4995
+ } catch (error) {
4996
+ spinner.stop("Writing construct failed");
4997
+ return fail(error instanceof Error ? error.message : String(error));
4998
+ }
4999
+ spinner.stop(`Wrote ${import_picocolors3.default.cyan(path4.relative(process2.cwd(), result.file))}`);
5000
+ if (result.constructName !== name) {
5001
+ stated(
5002
+ "Construct name",
5003
+ `${result.constructName} \u2014 "${name}" is not a valid custom-element tag; the directory and file name still use your name as typed`
5004
+ );
5005
+ }
5006
+ const relative = path4.relative(process2.cwd(), dir);
5007
+ const startPreview = !nonInteractive && await ask(ye({ message: "Start the live preview now?", initialValue: true }));
5008
+ const printFallbackSteps = () => {
5009
+ const steps = [relative ? `cd ${relative}` : null, result.devCommand].filter(Boolean);
5010
+ Me(steps.join("\n"), "Next steps");
5011
+ };
5012
+ if (!startPreview) {
5013
+ printFallbackSteps();
5014
+ }
5015
+ Se(
5016
+ [
5017
+ `${import_picocolors3.default.dim("Shape:")} ${shape}`,
5018
+ `${import_picocolors3.default.dim("File:")} ${path4.basename(result.file)}`
5019
+ ].join("\n")
5020
+ );
5021
+ if (startPreview) {
5022
+ const outcome = await runDevPreview(result.devCommand, dir, spawn);
5023
+ if (!outcome.ok) {
5024
+ console.error(import_picocolors3.default.red(`
5025
+ Live preview failed: ${outcome.message}`));
5026
+ printFallbackSteps();
5027
+ }
5028
+ }
5029
+ return 0;
5030
+ }
4785
5031
  function fail(message) {
4786
5032
  xe(import_picocolors3.default.red(message));
4787
5033
  return 1;
@@ -4811,12 +5057,12 @@ function run(command, cwd) {
4811
5057
  }
4812
5058
  function printMatrix(asJson) {
4813
5059
  const matrix = {
4814
- cli: "0.2.2",
5060
+ cli: "0.3.0",
4815
5061
  kit: DEFAULT_KIT_RANGE,
4816
5062
  // The same pair `kai.json` carries. An agent reading this to decide what to
4817
5063
  // install gets the range; one reasoning about which kit the templates match
4818
5064
  // gets the exact version, without having to parse the range for a floor.
4819
- kitBuiltAgainst: "0.28.0",
5065
+ kitBuiltAgainst: "0.29.0",
4820
5066
  frameworks: FRAMEWORKS.map((f) => ({
4821
5067
  id: f.id,
4822
5068
  label: f.label,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-kai",
3
- "version": "0.2.2",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Scaffold a runnable @kitn.ai/ui chat app. `npm create kai@latest`",
6
6
  "license": "MIT",