create-kai 0.2.1 → 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.
- package/dist/index.js +277 -31
- 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
|
|
163
|
-
import { existsSync as
|
|
164
|
-
import
|
|
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
|
|
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
|
|
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 =
|
|
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 =
|
|
4368
|
+
const parent = path2.dirname(plan.dir);
|
|
4353
4369
|
await mkdir(parent, { recursive: true });
|
|
4354
|
-
const out = await mkdtemp(
|
|
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 =
|
|
4360
|
-
if (existsSync(travelling)) await rename(travelling,
|
|
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 =
|
|
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 =
|
|
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(
|
|
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
|
-
|
|
4394
|
+
path2.join(out, "kai.json"),
|
|
4379
4395
|
stringifyKaiJson(buildKaiJson({ ...plan, featureIds: emittedFeatures }, framework)),
|
|
4380
4396
|
"utf8"
|
|
4381
4397
|
);
|
|
4382
4398
|
await writeFile(
|
|
4383
|
-
|
|
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 =
|
|
4423
|
-
await mkdir(
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
4633
|
-
if (
|
|
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.
|
|
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(
|
|
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 =
|
|
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.
|
|
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.
|
|
5065
|
+
kitBuiltAgainst: "0.29.0",
|
|
4820
5066
|
frameworks: FRAMEWORKS.map((f) => ({
|
|
4821
5067
|
id: f.id,
|
|
4822
5068
|
label: f.label,
|