@jay-framework/jay-stack-cli 0.17.2 → 0.17.4

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 CHANGED
@@ -7,18 +7,18 @@ import getPort from "get-port";
7
7
  import path from "path";
8
8
  import fs, { promises } from "fs";
9
9
  import YAML from "yaml";
10
- import { getLogger, createDevLogger, setDevLogger } from "@jay-framework/logger";
10
+ import { getLogger, setDevLogger, createDevLogger } from "@jay-framework/logger";
11
11
  import { parseJayFile, JAY_IMPORT_RESOLVER, generateElementDefinitionFile, ContractTagType, parseContract, generateElementFile, generateServerElementFile, htmlElementTagNameMap } from "@jay-framework/compiler-jay-html";
12
12
  import { JAY_CONTRACT_EXTENSION, JAY_EXTENSION, resolvePluginManifest, LOCAL_PLUGIN_PATH, JayAtomicType, JayEnumType, loadPluginManifest, RuntimeMode, GenerateTarget } from "@jay-framework/compiler-shared";
13
- import { scanPlugins as scanPlugins$1, listContracts, materializeContracts } from "@jay-framework/stack-server-runtime";
13
+ import { listContracts, materializeContracts, scanPlugins as scanPlugins$1 } from "@jay-framework/stack-server-runtime";
14
14
  import { listContracts as listContracts2, materializeContracts as materializeContracts2 } from "@jay-framework/stack-server-runtime";
15
15
  import { Command } from "commander";
16
16
  import chalk from "chalk";
17
+ import path$1 from "node:path";
18
+ import fs$1 from "node:fs/promises";
17
19
  import { createRequire } from "module";
18
20
  import { glob } from "glob";
19
21
  import { parse } from "node-html-parser";
20
- import path$1 from "node:path";
21
- import fs$1 from "node:fs/promises";
22
22
  import fsSync from "node:fs";
23
23
  import { fileURLToPath } from "node:url";
24
24
  const DEFAULT_CONFIG = {
@@ -2583,6 +2583,177 @@ async function startDevServer(options = {}) {
2583
2583
  process.on("SIGTERM", shutdown);
2584
2584
  process.on("SIGINT", shutdown);
2585
2585
  }
2586
+ async function initializeServicesForCli(projectRoot, viteServer) {
2587
+ const path2 = await import("node:path");
2588
+ const fs2 = await import("node:fs");
2589
+ const {
2590
+ runInitCallbacks,
2591
+ getServiceRegistry,
2592
+ discoverPluginsWithInit,
2593
+ sortPluginsByDependencies,
2594
+ executePluginServerInits
2595
+ } = await import("@jay-framework/stack-server-runtime");
2596
+ let initErrors = /* @__PURE__ */ new Map();
2597
+ try {
2598
+ const discoveredPlugins = await discoverPluginsWithInit({
2599
+ projectRoot,
2600
+ verbose: false
2601
+ });
2602
+ const pluginsWithInit = sortPluginsByDependencies(discoveredPlugins);
2603
+ try {
2604
+ initErrors = await executePluginServerInits(pluginsWithInit, viteServer, false);
2605
+ } catch (error) {
2606
+ getLogger().warn(chalk.yellow(`⚠️ Plugin initialization skipped: ${error.message}`));
2607
+ }
2608
+ const initPathTs = path2.join(projectRoot, "src", "init.ts");
2609
+ const initPathJs = path2.join(projectRoot, "src", "init.js");
2610
+ let initModule;
2611
+ if (fs2.existsSync(initPathTs) && viteServer) {
2612
+ initModule = await viteServer.ssrLoadModule(initPathTs);
2613
+ } else if (fs2.existsSync(initPathJs)) {
2614
+ initModule = await import(initPathJs);
2615
+ }
2616
+ if (initModule?.init?._serverInit) {
2617
+ await initModule.init._serverInit();
2618
+ }
2619
+ await runInitCallbacks();
2620
+ } catch (error) {
2621
+ getLogger().warn(chalk.yellow(`⚠️ Service initialization failed: ${error.message}`));
2622
+ getLogger().warn(chalk.gray(" Static contracts will still be listed."));
2623
+ }
2624
+ return { services: getServiceRegistry(), initErrors };
2625
+ }
2626
+ async function runDev(projectPath, options) {
2627
+ const logLevel = options.quiet ? "silent" : options.verbose ? "verbose" : "info";
2628
+ setDevLogger(createDevLogger(logLevel));
2629
+ const testMode = options.testMode || options.timeout !== void 0;
2630
+ await startDevServer({
2631
+ projectPath: projectPath || process.cwd(),
2632
+ testMode,
2633
+ timeout: options.timeout,
2634
+ logLevel
2635
+ });
2636
+ }
2637
+ async function resolveProductionContext(projectPath, versionOverride) {
2638
+ const resolvedPath = path$1.resolve(projectPath || process.cwd());
2639
+ const jayConfigPath = path$1.join(resolvedPath, ".jay");
2640
+ let pagesBase = "./src/pages";
2641
+ try {
2642
+ const jayConfig = YAML.parse(await fs$1.readFile(jayConfigPath, "utf-8"));
2643
+ pagesBase = jayConfig?.devServer?.pagesBase || pagesBase;
2644
+ } catch {
2645
+ }
2646
+ const version = versionOverride ? parseInt(versionOverride, 10) : await resolveVersionFromPackageJson(resolvedPath);
2647
+ return {
2648
+ resolvedPath,
2649
+ pagesRoot: path$1.resolve(resolvedPath, pagesBase),
2650
+ buildRoot: path$1.join(resolvedPath, "build"),
2651
+ version,
2652
+ tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json")
2653
+ };
2654
+ }
2655
+ async function resolveVersionFromPackageJson(projectRoot) {
2656
+ try {
2657
+ const pkgJson = JSON.parse(
2658
+ await fs$1.readFile(path$1.join(projectRoot, "package.json"), "utf-8")
2659
+ );
2660
+ if (pkgJson.version) {
2661
+ const major = parseInt(pkgJson.version.split(".")[0], 10);
2662
+ const minor = parseInt(pkgJson.version.split(".")[1] || "0", 10);
2663
+ const patch = parseInt(pkgJson.version.split(".")[2] || "0", 10);
2664
+ return major * 1e4 + minor * 100 + patch;
2665
+ }
2666
+ } catch {
2667
+ }
2668
+ return 1;
2669
+ }
2670
+ function initLogger(verbose) {
2671
+ const logLevel = verbose ? "verbose" : "info";
2672
+ setDevLogger(createDevLogger(logLevel));
2673
+ }
2674
+ async function runBuild(projectPath, options) {
2675
+ initLogger(options.verbose);
2676
+ const ctx = await resolveProductionContext(projectPath, options.version);
2677
+ const { buildVersion } = await import("@jay-framework/production-server");
2678
+ await buildVersion({
2679
+ version: ctx.version,
2680
+ projectRoot: ctx.resolvedPath,
2681
+ pagesRoot: ctx.pagesRoot,
2682
+ buildRoot: ctx.buildRoot,
2683
+ concurrency: 4,
2684
+ tsConfigFilePath: ctx.tsConfigFilePath,
2685
+ minify: options.minify
2686
+ });
2687
+ }
2688
+ async function runServe(projectPath, options) {
2689
+ initLogger(options.verbose);
2690
+ const ctx = await resolveProductionContext(projectPath, options.version);
2691
+ if (options.role === "renderer") {
2692
+ const { startRendererServer } = await import("@jay-framework/production-server");
2693
+ await startRendererServer({
2694
+ buildRoot: ctx.buildRoot,
2695
+ version: ctx.version,
2696
+ port: parseInt(options.port, 10),
2697
+ projectRoot: ctx.resolvedPath,
2698
+ pagesRoot: ctx.pagesRoot,
2699
+ tsConfigFilePath: ctx.tsConfigFilePath
2700
+ });
2701
+ } else {
2702
+ const { startMainServer } = await import("@jay-framework/production-server");
2703
+ await startMainServer({
2704
+ buildRoot: ctx.buildRoot,
2705
+ version: ctx.version,
2706
+ port: parseInt(options.port, 10),
2707
+ testMode: options.testMode,
2708
+ publicBasePath: options.staticBaseUrl,
2709
+ serveStatic: options.serveStatic
2710
+ });
2711
+ }
2712
+ }
2713
+ async function runRebuild(projectPath, options) {
2714
+ initLogger(options.verbose);
2715
+ const ctx = await resolveProductionContext(projectPath, options.version);
2716
+ let params;
2717
+ if (options.params) {
2718
+ params = JSON.parse(options.params);
2719
+ }
2720
+ const { rebuild } = await import("@jay-framework/production-server");
2721
+ let target;
2722
+ if (options.contract) {
2723
+ target = { mode: "contract", contractName: options.contract, params };
2724
+ } else if (options.route) {
2725
+ target = { mode: "route", routePattern: options.route, params };
2726
+ } else if (options.url) {
2727
+ target = { mode: "url", url: options.url };
2728
+ } else {
2729
+ getLogger().error(chalk.red("One of --contract, --route, or --url is required"));
2730
+ process.exit(1);
2731
+ }
2732
+ const result = await rebuild({
2733
+ projectRoot: ctx.resolvedPath,
2734
+ pagesRoot: ctx.pagesRoot,
2735
+ buildRoot: ctx.buildRoot,
2736
+ version: ctx.version,
2737
+ target,
2738
+ tsConfigFilePath: ctx.tsConfigFilePath
2739
+ });
2740
+ if (result.errors.length > 0) {
2741
+ for (const err of result.errors) {
2742
+ getLogger().error(
2743
+ chalk.red(` Error: ${err.route} ${JSON.stringify(err.params)}: ${err.error}`)
2744
+ );
2745
+ }
2746
+ process.exit(1);
2747
+ }
2748
+ }
2749
+ const runProduction = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
2750
+ __proto__: null,
2751
+ initLogger,
2752
+ resolveProductionContext,
2753
+ runBuild,
2754
+ runRebuild,
2755
+ runServe
2756
+ }, Symbol.toStringTag, { value: "Module" }));
2586
2757
  const s = createRequire(import.meta.url), e = s("typescript"), u = e;
2587
2758
  new Proxy(e, {
2588
2759
  get(t, r) {
@@ -4260,96 +4431,370 @@ function printJayValidationResult(result, options) {
4260
4431
  }
4261
4432
  }
4262
4433
  }
4263
- async function initializeServicesForCli(projectRoot, viteServer) {
4264
- const path2 = await import("node:path");
4265
- const fs2 = await import("node:fs");
4266
- const {
4267
- runInitCallbacks,
4268
- getServiceRegistry,
4269
- discoverPluginsWithInit,
4270
- sortPluginsByDependencies,
4271
- executePluginServerInits
4272
- } = await import("@jay-framework/stack-server-runtime");
4273
- let initErrors = /* @__PURE__ */ new Map();
4274
- try {
4275
- const discoveredPlugins = await discoverPluginsWithInit({
4276
- projectRoot,
4277
- verbose: false
4278
- });
4279
- const pluginsWithInit = sortPluginsByDependencies(discoveredPlugins);
4280
- try {
4281
- initErrors = await executePluginServerInits(pluginsWithInit, viteServer, false);
4282
- } catch (error) {
4283
- getLogger().warn(chalk.yellow(`⚠️ Plugin initialization skipped: ${error.message}`));
4434
+ async function runValidate(scanPath, options) {
4435
+ const result = await validateJayFiles({
4436
+ path: scanPath,
4437
+ verbose: options.verbose,
4438
+ json: options.json
4439
+ });
4440
+ printJayValidationResult(result, options);
4441
+ if (!result.valid) {
4442
+ process.exit(1);
4443
+ }
4444
+ }
4445
+ async function runValidatePlugin(pluginPath, options) {
4446
+ const result = await validatePlugin({
4447
+ pluginPath: pluginPath || process.cwd(),
4448
+ local: options.local,
4449
+ verbose: options.verbose,
4450
+ strict: options.strict,
4451
+ generateTypes: options.generateTypes
4452
+ });
4453
+ printValidationResult(result, options.verbose ?? false);
4454
+ if (!result.valid || options.strict && result.warnings.length > 0) {
4455
+ process.exit(1);
4456
+ }
4457
+ }
4458
+ function printValidationResult(result, verbose) {
4459
+ const logger = getLogger();
4460
+ if (result.valid && result.warnings.length === 0) {
4461
+ logger.important(chalk.green("Plugin validation successful!\n"));
4462
+ if (verbose) {
4463
+ logger.important("Plugin: " + result.pluginName);
4464
+ logger.important(" plugin.yaml valid");
4465
+ logger.important(` ${result.contractsChecked} contracts validated`);
4466
+ if (result.typesGenerated) {
4467
+ logger.important(` ${result.typesGenerated} type definitions generated`);
4468
+ }
4469
+ logger.important(` ${result.componentsChecked} components validated`);
4470
+ if (result.packageJsonChecked) {
4471
+ logger.important(" package.json valid");
4472
+ }
4473
+ logger.important("\nNo errors found.");
4284
4474
  }
4285
- const initPathTs = path2.join(projectRoot, "src", "init.ts");
4286
- const initPathJs = path2.join(projectRoot, "src", "init.js");
4287
- let initModule;
4288
- if (fs2.existsSync(initPathTs) && viteServer) {
4289
- initModule = await viteServer.ssrLoadModule(initPathTs);
4290
- } else if (fs2.existsSync(initPathJs)) {
4291
- initModule = await import(initPathJs);
4475
+ } else if (result.valid && result.warnings.length > 0) {
4476
+ logger.important(chalk.yellow("Plugin validation passed with warnings\n"));
4477
+ logger.important("Warnings:");
4478
+ result.warnings.forEach((warning) => {
4479
+ logger.important(chalk.yellow(` ${warning.message}`));
4480
+ if (warning.location) {
4481
+ logger.important(chalk.gray(` Location: ${warning.location}`));
4482
+ }
4483
+ if (warning.suggestion) {
4484
+ logger.important(chalk.gray(` ${warning.suggestion}`));
4485
+ }
4486
+ logger.important("");
4487
+ });
4488
+ logger.important(chalk.gray("Use --strict to treat warnings as errors."));
4489
+ } else {
4490
+ logger.important(chalk.red("Plugin validation failed\n"));
4491
+ logger.important("Errors:");
4492
+ result.errors.forEach((error) => {
4493
+ logger.important(chalk.red(` ${error.message}`));
4494
+ if (error.location) {
4495
+ logger.important(chalk.gray(` Location: ${error.location}`));
4496
+ }
4497
+ if (error.suggestion) {
4498
+ logger.important(chalk.gray(` ${error.suggestion}`));
4499
+ }
4500
+ logger.important("");
4501
+ });
4502
+ logger.important(chalk.red(`${result.errors.length} errors found.`));
4503
+ }
4504
+ }
4505
+ const ALL_ROLES = ["designer", "developer", "plugin"];
4506
+ async function runAgentKit(options) {
4507
+ const projectRoot = process.cwd();
4508
+ const { initErrors, viteServer } = await runMaterialize(
4509
+ projectRoot,
4510
+ options,
4511
+ "agent-kit/materialized-contracts",
4512
+ true
4513
+ );
4514
+ try {
4515
+ if (!options.list) {
4516
+ await ensureAgentKitDocs(projectRoot, options.force, options.mode);
4517
+ await mergePluginAgentKitGuides(projectRoot, options.mode);
4518
+ if (options.references !== false) {
4519
+ await generatePluginReferences(projectRoot, options, initErrors, viteServer);
4520
+ }
4292
4521
  }
4293
- if (initModule?.init?._serverInit) {
4294
- await initModule.init._serverInit();
4522
+ } finally {
4523
+ if (viteServer) {
4524
+ await viteServer.close();
4295
4525
  }
4296
- await runInitCallbacks();
4297
- } catch (error) {
4298
- getLogger().warn(chalk.yellow(`⚠️ Service initialization failed: ${error.message}`));
4299
- getLogger().warn(chalk.gray(" Static contracts will still be listed."));
4300
4526
  }
4301
- return { services: getServiceRegistry(), initErrors };
4302
4527
  }
4303
- async function runAction(actionRef, options, projectRoot, initializeServices) {
4528
+ async function runMaterialize(projectRoot, options, defaultOutputRelative, keepViteAlive = false) {
4529
+ const outputDir = options.output ?? path$1.join(projectRoot, defaultOutputRelative);
4304
4530
  let viteServer;
4531
+ let initErrors = /* @__PURE__ */ new Map();
4305
4532
  try {
4306
- const slashIndex = actionRef.indexOf("/");
4307
- if (slashIndex === -1) {
4308
- getLogger().error(
4309
- chalk.red("❌ Invalid action reference. Use format: <plugin>/<action>")
4310
- );
4311
- process.exit(1);
4533
+ if (options.list) {
4534
+ const index = await listContracts({
4535
+ projectRoot,
4536
+ dynamicOnly: options.dynamicOnly,
4537
+ pluginFilter: options.plugin
4538
+ });
4539
+ if (options.yaml) {
4540
+ getLogger().important(YAML.stringify(index));
4541
+ } else {
4542
+ printContractList(index);
4543
+ }
4544
+ return { initErrors };
4312
4545
  }
4313
- const actionExport = actionRef.substring(slashIndex + 1);
4314
- const input = options.input ? JSON.parse(options.input) : {};
4315
4546
  if (options.verbose) {
4316
4547
  getLogger().info("Starting Vite for TypeScript support...");
4317
4548
  }
4318
4549
  viteServer = await createViteForCli({ projectRoot });
4319
- await initializeServices(projectRoot, viteServer);
4320
- const { discoverAndRegisterActions, discoverAllPluginActions, ActionRegistry } = await import("@jay-framework/stack-server-runtime");
4321
- const registry = new ActionRegistry();
4322
- await discoverAndRegisterActions({
4323
- projectRoot,
4324
- registry,
4325
- verbose: options.verbose,
4326
- viteServer
4327
- });
4328
- await discoverAllPluginActions({
4550
+ const { services, initErrors: errors } = await initializeServicesForCli(
4329
4551
  projectRoot,
4330
- registry,
4331
- verbose: options.verbose,
4332
4552
  viteServer
4333
- });
4334
- const allNames = registry.getNames();
4335
- const matchedName = allNames.find((name) => name === actionExport) || allNames.find((name) => name.endsWith("." + actionExport)) || allNames.find((name) => name === actionRef);
4336
- if (!matchedName) {
4337
- getLogger().error(chalk.red(`❌ Action "${actionExport}" not found.`));
4338
- if (allNames.length > 0) {
4339
- getLogger().error(` Available actions: ${allNames.join(", ")}`);
4340
- } else {
4341
- getLogger().error(" No actions registered. Does the plugin have actions?");
4342
- }
4343
- process.exit(1);
4553
+ );
4554
+ initErrors = errors;
4555
+ const result = await materializeContracts(
4556
+ {
4557
+ projectRoot,
4558
+ outputDir,
4559
+ force: options.force,
4560
+ dynamicOnly: options.dynamicOnly,
4561
+ pluginFilter: options.plugin,
4562
+ verbose: options.verbose,
4563
+ viteServer
4564
+ },
4565
+ services
4566
+ );
4567
+ if (options.yaml) {
4568
+ getLogger().important(YAML.stringify(result.pluginsIndex));
4569
+ } else {
4570
+ const totalContracts = result.pluginsIndex.plugins.reduce(
4571
+ (sum, p) => sum + p.contracts.length,
4572
+ 0
4573
+ );
4574
+ getLogger().important(chalk.green(`
4575
+ Materialized ${totalContracts} contracts`));
4576
+ getLogger().important(` Static: ${result.staticCount}`);
4577
+ getLogger().important(` Dynamic: ${result.dynamicCount}`);
4578
+ getLogger().important(` Output: ${result.outputDir}`);
4344
4579
  }
4580
+ return { initErrors, viteServer: keepViteAlive ? viteServer : void 0 };
4581
+ } catch (error) {
4582
+ getLogger().error(chalk.red("Failed to materialize contracts:") + " " + error.message);
4345
4583
  if (options.verbose) {
4346
- getLogger().info(`Executing action: ${matchedName}`);
4347
- getLogger().info(`Input: ${JSON.stringify(input)}`);
4584
+ getLogger().error(error.stack);
4348
4585
  }
4349
- const result = await registry.execute(matchedName, input);
4350
- if (result.success) {
4351
- if (options.yaml) {
4352
- getLogger().important(YAML.stringify(result.data));
4586
+ process.exit(1);
4587
+ } finally {
4588
+ if (viteServer && !keepViteAlive) {
4589
+ await viteServer.close();
4590
+ }
4591
+ }
4592
+ return { initErrors };
4593
+ }
4594
+ async function ensureAgentKitDocs(projectRoot, _force, mode) {
4595
+ const agentKitDir = path$1.join(projectRoot, "agent-kit");
4596
+ const thisDir = path$1.dirname(fileURLToPath(import.meta.url));
4597
+ const templateDir = path$1.resolve(thisDir, "..", "agent-kit-template");
4598
+ const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
4599
+ for (const role of roles) {
4600
+ const roleTemplateDir = path$1.join(templateDir, role);
4601
+ const roleOutputDir = path$1.join(agentKitDir, role);
4602
+ let files;
4603
+ try {
4604
+ files = (await fs$1.readdir(roleTemplateDir)).filter((f) => f.endsWith(".md"));
4605
+ } catch {
4606
+ continue;
4607
+ }
4608
+ await fs$1.mkdir(roleOutputDir, { recursive: true });
4609
+ for (const filename of files) {
4610
+ await fs$1.copyFile(
4611
+ path$1.join(roleTemplateDir, filename),
4612
+ path$1.join(roleOutputDir, filename)
4613
+ );
4614
+ getLogger().info(chalk.gray(` Created agent-kit/${role}/${filename}`));
4615
+ }
4616
+ }
4617
+ }
4618
+ async function mergePluginAgentKitGuides(projectRoot, mode) {
4619
+ const plugins = await scanPlugins$1({ projectRoot });
4620
+ const agentKitDir = path$1.join(projectRoot, "agent-kit");
4621
+ const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
4622
+ const copiedPerRole = /* @__PURE__ */ new Map();
4623
+ for (const [, plugin] of plugins) {
4624
+ const pluginAgentKitDir = path$1.join(plugin.pluginPath, "agent-kit");
4625
+ if (!fsSync.existsSync(pluginAgentKitDir))
4626
+ continue;
4627
+ for (const role of roles) {
4628
+ const roleSourceDir = path$1.join(pluginAgentKitDir, role);
4629
+ let files;
4630
+ try {
4631
+ files = (await fs$1.readdir(roleSourceDir)).filter(
4632
+ (f) => f.endsWith(".md") && f !== "INSTRUCTIONS.md"
4633
+ );
4634
+ } catch {
4635
+ continue;
4636
+ }
4637
+ if (files.length === 0)
4638
+ continue;
4639
+ const roleOutputDir = path$1.join(agentKitDir, role);
4640
+ await fs$1.mkdir(roleOutputDir, { recursive: true });
4641
+ for (const filename of files) {
4642
+ const sourcePath = path$1.join(roleSourceDir, filename);
4643
+ await fs$1.copyFile(sourcePath, path$1.join(roleOutputDir, filename));
4644
+ let description = "";
4645
+ try {
4646
+ const content = await fs$1.readFile(sourcePath, "utf-8");
4647
+ const lines = content.split("\n");
4648
+ let pastHeading = false;
4649
+ for (const line of lines) {
4650
+ if (line.startsWith("# ")) {
4651
+ pastHeading = true;
4652
+ continue;
4653
+ }
4654
+ if (pastHeading && line.trim()) {
4655
+ description = line.trim();
4656
+ break;
4657
+ }
4658
+ }
4659
+ } catch {
4660
+ }
4661
+ if (!copiedPerRole.has(role))
4662
+ copiedPerRole.set(role, []);
4663
+ copiedPerRole.get(role).push({ filename, pluginName: plugin.name, description });
4664
+ getLogger().info(
4665
+ chalk.gray(
4666
+ ` Copied agent-kit/${role}/${filename} from plugin "${plugin.name}"`
4667
+ )
4668
+ );
4669
+ }
4670
+ }
4671
+ }
4672
+ for (const [role, entries] of copiedPerRole) {
4673
+ const instructionsPath = path$1.join(agentKitDir, role, "INSTRUCTIONS.md");
4674
+ if (!fsSync.existsSync(instructionsPath))
4675
+ continue;
4676
+ const lines = [
4677
+ "",
4678
+ "## Plugin-Contributed Guides",
4679
+ "",
4680
+ "| File | Plugin | Description |",
4681
+ "| --- | --- | --- |"
4682
+ ];
4683
+ for (const { filename, pluginName, description } of entries) {
4684
+ lines.push(`| [${filename}](${filename}) | ${pluginName} | ${description} |`);
4685
+ }
4686
+ lines.push("");
4687
+ await fs$1.appendFile(instructionsPath, lines.join("\n"));
4688
+ }
4689
+ }
4690
+ async function generatePluginReferences(projectRoot, options, initErrors, viteServer) {
4691
+ const { discoverPluginsWithReferences, executePluginReferences } = await import("@jay-framework/stack-server-runtime");
4692
+ const plugins = await discoverPluginsWithReferences({
4693
+ projectRoot,
4694
+ verbose: options.verbose,
4695
+ pluginFilter: options.plugin
4696
+ });
4697
+ if (plugins.length === 0)
4698
+ return;
4699
+ const logger = getLogger();
4700
+ logger.important("");
4701
+ logger.important(chalk.bold("Generating plugin references..."));
4702
+ for (const plugin of plugins) {
4703
+ const pluginInitError = initErrors.get(plugin.name);
4704
+ if (pluginInitError) {
4705
+ logger.warn(
4706
+ chalk.yellow(
4707
+ ` ${plugin.name}: references skipped — init failed: ${pluginInitError.message}`
4708
+ )
4709
+ );
4710
+ continue;
4711
+ }
4712
+ try {
4713
+ const result = await executePluginReferences(plugin, {
4714
+ projectRoot,
4715
+ force: options.force ?? false,
4716
+ viteServer,
4717
+ verbose: options.verbose
4718
+ });
4719
+ if (result.referencesCreated.length > 0) {
4720
+ logger.important(chalk.green(` ${plugin.name}:`));
4721
+ for (const ref of result.referencesCreated) {
4722
+ logger.important(chalk.gray(` ${ref}`));
4723
+ }
4724
+ if (result.message) {
4725
+ logger.important(chalk.gray(` ${result.message}`));
4726
+ }
4727
+ }
4728
+ } catch (error) {
4729
+ logger.warn(chalk.yellow(` ${plugin.name}: references skipped — ${error.message}`));
4730
+ }
4731
+ }
4732
+ }
4733
+ function printContractList(index) {
4734
+ const logger = getLogger();
4735
+ logger.important("\nAvailable Contracts:\n");
4736
+ for (const plugin of index.plugins) {
4737
+ logger.important(chalk.bold(plugin.name));
4738
+ for (const contract of plugin.contracts) {
4739
+ const typeIcon = contract.type === "static" ? "static" : "dynamic";
4740
+ logger.important(` [${typeIcon}] ${contract.name}`);
4741
+ }
4742
+ logger.important("");
4743
+ }
4744
+ if (index.plugins.length === 0) {
4745
+ logger.important(chalk.gray("No contracts found."));
4746
+ }
4747
+ }
4748
+ async function runAction(actionRef, options, projectRoot, initializeServices) {
4749
+ let viteServer;
4750
+ try {
4751
+ const slashIndex = actionRef.indexOf("/");
4752
+ if (slashIndex === -1) {
4753
+ getLogger().error(
4754
+ chalk.red("❌ Invalid action reference. Use format: <plugin>/<action>")
4755
+ );
4756
+ process.exit(1);
4757
+ }
4758
+ const actionExport = actionRef.substring(slashIndex + 1);
4759
+ const input = options.input ? JSON.parse(options.input) : {};
4760
+ if (options.verbose) {
4761
+ getLogger().info("Starting Vite for TypeScript support...");
4762
+ }
4763
+ viteServer = await createViteForCli({ projectRoot });
4764
+ await initializeServices(projectRoot, viteServer);
4765
+ const { discoverAndRegisterActions, discoverAllPluginActions, ActionRegistry } = await import("@jay-framework/stack-server-runtime");
4766
+ const registry = new ActionRegistry();
4767
+ await discoverAndRegisterActions({
4768
+ projectRoot,
4769
+ registry,
4770
+ verbose: options.verbose,
4771
+ viteServer
4772
+ });
4773
+ await discoverAllPluginActions({
4774
+ projectRoot,
4775
+ registry,
4776
+ verbose: options.verbose,
4777
+ viteServer
4778
+ });
4779
+ const allNames = registry.getNames();
4780
+ const matchedName = allNames.find((name) => name === actionExport) || allNames.find((name) => name.endsWith("." + actionExport)) || allNames.find((name) => name === actionRef);
4781
+ if (!matchedName) {
4782
+ getLogger().error(chalk.red(`❌ Action "${actionExport}" not found.`));
4783
+ if (allNames.length > 0) {
4784
+ getLogger().error(` Available actions: ${allNames.join(", ")}`);
4785
+ } else {
4786
+ getLogger().error(" No actions registered. Does the plugin have actions?");
4787
+ }
4788
+ process.exit(1);
4789
+ }
4790
+ if (options.verbose) {
4791
+ getLogger().info(`Executing action: ${matchedName}`);
4792
+ getLogger().info(`Input: ${JSON.stringify(input)}`);
4793
+ }
4794
+ const result = await registry.execute(matchedName, input);
4795
+ if (result.success) {
4796
+ if (options.yaml) {
4797
+ getLogger().important(YAML.stringify(result.data));
4353
4798
  } else {
4354
4799
  getLogger().important(JSON.stringify(result.data, null, 2));
4355
4800
  }
@@ -4586,45 +5031,17 @@ async function runSetup(pluginFilter, options, projectRoot, initializeServices)
4586
5031
  }
4587
5032
  const program = new Command();
4588
5033
  program.name("jay-stack").description("Jay Stack CLI - Development server and plugin validation").version("0.9.0");
4589
- program.command("dev [path]").description("Start the Jay Stack development server").option("-v, --verbose", "Enable verbose logging output").option("-q, --quiet", "Suppress all non-error output").option("--test-mode", "Enable test endpoints (/_jay/health, /_jay/shutdown)").option("--timeout <seconds>", "Auto-shutdown after N seconds (implies --test-mode)", parseInt).action(async (path2, options) => {
5034
+ program.command("dev").description("Start the Jay Stack development server").option("-p, --path <path>", "Project root (default: cwd)").option("-v, --verbose", "Enable verbose logging output").option("-q, --quiet", "Suppress all non-error output").option("--test-mode", "Enable test endpoints (/_jay/health, /_jay/shutdown)").option("--timeout <seconds>", "Auto-shutdown after N seconds (implies --test-mode)", parseInt).action(async (options) => {
4590
5035
  try {
4591
- const logLevel = options.quiet ? "silent" : options.verbose ? "verbose" : "info";
4592
- setDevLogger(createDevLogger(logLevel));
4593
- const testMode = options.testMode || options.timeout !== void 0;
4594
- await startDevServer({
4595
- projectPath: path2 || process.cwd(),
4596
- testMode,
4597
- timeout: options.timeout,
4598
- logLevel
4599
- });
5036
+ await runDev(options.path, options);
4600
5037
  } catch (error) {
4601
5038
  getLogger().error(chalk.red("Error starting dev server:") + " " + error.message);
4602
5039
  process.exit(1);
4603
5040
  }
4604
5041
  });
4605
- program.command("build [path]").description("Build production artifacts").option("--version <n>", "Build version number", "1").option("--no-minify", "Disable minification (useful for debugging)").option("-v, --verbose", "Enable verbose logging output").action(async (projectPath, options) => {
5042
+ program.command("build").description("Build production artifacts").option("-p, --path <path>", "Project root (default: cwd)").option("--version <n>", "Build version number (default: from package.json)").option("--no-minify", "Disable minification (useful for debugging)").option("-v, --verbose", "Enable verbose logging output").action(async (options) => {
4606
5043
  try {
4607
- const logLevel = options.verbose ? "verbose" : "info";
4608
- setDevLogger(createDevLogger(logLevel));
4609
- const resolvedPath = path$1.resolve(projectPath || process.cwd());
4610
- const jayConfigPath = path$1.join(resolvedPath, ".jay");
4611
- let pagesBase = "./src/pages";
4612
- try {
4613
- const jayConfig = YAML.parse(await fs$1.readFile(jayConfigPath, "utf-8"));
4614
- pagesBase = jayConfig?.devServer?.pagesBase || pagesBase;
4615
- } catch {
4616
- }
4617
- const { buildVersion } = await import("@jay-framework/production-server");
4618
- await buildVersion({
4619
- version: parseInt(options.version, 10),
4620
- projectRoot: resolvedPath,
4621
- pagesRoot: path$1.resolve(resolvedPath, pagesBase),
4622
- buildRoot: path$1.join(resolvedPath, "build"),
4623
- publicBasePath: "/",
4624
- concurrency: 4,
4625
- tsConfigFilePath: path$1.join(resolvedPath, "tsconfig.json"),
4626
- minify: options.minify
4627
- });
5044
+ await runBuild(options.path, options);
4628
5045
  } catch (error) {
4629
5046
  getLogger().error(chalk.red("Build failed:") + " " + error.message);
4630
5047
  if (error.stack)
@@ -4632,17 +5049,9 @@ program.command("build [path]").description("Build production artifacts").option
4632
5049
  process.exit(1);
4633
5050
  }
4634
5051
  });
4635
- program.command("serve [path]").description("Start production server").option("--version <n>", "Build version to serve", "1").option("--port <n>", "Port number", "3000").action(async (projectPath, options) => {
5052
+ program.command("serve").description("Start production server").option("-p, --path <path>", "Project root (default: cwd)").option("--version <n>", "Build version to serve (default: from package.json)").option("--port <n>", "Port number", "3000").option("--role <role>", "Server role: main (default) or renderer", "main").option("--static-base-url <url>", "Base URL for browser-facing assets (default: /)").option("--no-serve-static", "Disable serving static files from frontend/").option("--test-mode", "Enable test endpoints (/_jay/health, /_jay/shutdown)").option("-v, --verbose", "Enable verbose logging output").action(async (options) => {
4636
5053
  try {
4637
- setDevLogger(createDevLogger("info"));
4638
- const resolvedPath = path$1.resolve(projectPath || process.cwd());
4639
- const { startMainServer } = await import("@jay-framework/production-server");
4640
- await startMainServer({
4641
- buildRoot: path$1.join(resolvedPath, "build"),
4642
- version: parseInt(options.version, 10),
4643
- port: parseInt(options.port, 10),
4644
- publicBasePath: "/"
4645
- });
5054
+ await runServe(options.path, options);
4646
5055
  } catch (error) {
4647
5056
  getLogger().error(chalk.red("Server failed:") + " " + error.message);
4648
5057
  if (error.stack)
@@ -4650,17 +5059,31 @@ program.command("serve [path]").description("Start production server").option("-
4650
5059
  process.exit(1);
4651
5060
  }
4652
5061
  });
4653
- program.command("validate [path]").description("Validate all .jay-html and .jay-contract files in the project").option("-v, --verbose", "Show per-file validation status").option("--json", "Output results as JSON").action(async (scanPath, options) => {
5062
+ program.command("rebuild").description("Rebuild instances by contract, route, or URL").option("--contract <name>", "Rebuild by contract name (e.g., product-page)").option("--route <pattern>", "Rebuild by route pattern (e.g., /products/[slug])").option("--url <url>", "Rebuild by URL (e.g., /products/blue-widget)").option("--params <json>", `JSON params to rebuild specific instance (e.g., '{"slug":"x"}')`).option("--version <n>", "Build version (default: from package.json)").option("-p, --path <path>", "Project root (default: cwd)").option("-v, --verbose", "Enable verbose logging output").action(async (options) => {
4654
5063
  try {
4655
- const result = await validateJayFiles({
4656
- path: scanPath,
4657
- verbose: options.verbose,
4658
- json: options.json
4659
- });
4660
- printJayValidationResult(result, options);
4661
- if (!result.valid) {
4662
- process.exit(1);
4663
- }
5064
+ await runRebuild(options.path, options);
5065
+ } catch (error) {
5066
+ getLogger().error(chalk.red("Rebuild failed:") + " " + error.message);
5067
+ if (error.stack)
5068
+ getLogger().error(error.stack);
5069
+ process.exit(1);
5070
+ }
5071
+ });
5072
+ program.command("cleanup").description("Delete orphaned files from previous rebuilds").option("--version <n>", "Build version (default: from package.json)").option("-p, --path <path>", "Project root (default: cwd)").action(async (options) => {
5073
+ try {
5074
+ const { initLogger: initLogger2, resolveProductionContext: resolveProductionContext2 } = await Promise.resolve().then(() => runProduction);
5075
+ initLogger2();
5076
+ const ctx = await resolveProductionContext2(options.path, options.version);
5077
+ const { cleanupOrphanedFiles } = await import("@jay-framework/production-server");
5078
+ await cleanupOrphanedFiles(ctx.buildRoot, ctx.version);
5079
+ } catch (error) {
5080
+ getLogger().error(chalk.red("Cleanup failed:") + " " + error.message);
5081
+ process.exit(1);
5082
+ }
5083
+ });
5084
+ program.command("validate").description("Validate all .jay-html and .jay-contract files").option("-p, --path <path>", "Scan root (default: cwd)").option("-v, --verbose", "Show per-file validation status").option("--json", "Output results as JSON").action(async (options) => {
5085
+ try {
5086
+ await runValidate(options.path, options);
4664
5087
  } catch (error) {
4665
5088
  if (options.json) {
4666
5089
  getLogger().important(
@@ -4672,342 +5095,33 @@ program.command("validate [path]").description("Validate all .jay-html and .jay-
4672
5095
  process.exit(1);
4673
5096
  }
4674
5097
  });
4675
- program.command("validate-plugin [path]").description("Validate a Jay Stack plugin package").option("--local", "Validate local plugins in src/plugins/").option("-v, --verbose", "Show detailed validation output").option("--strict", "Treat warnings as errors (for CI)").option("--generate-types", "Generate .d.ts files for contracts").action(async (pluginPath, options) => {
5098
+ program.command("validate-plugin").description("Validate a Jay Stack plugin package").option("-p, --path <path>", "Plugin root (default: cwd)").option("--local", "Validate local plugins in src/plugins/").option("-v, --verbose", "Show detailed validation output").option("--strict", "Treat warnings as errors (for CI)").option("--generate-types", "Generate .d.ts files for contracts").action(async (options) => {
4676
5099
  try {
4677
- const result = await validatePlugin({
4678
- pluginPath: pluginPath || process.cwd(),
4679
- local: options.local,
4680
- verbose: options.verbose,
4681
- strict: options.strict,
4682
- generateTypes: options.generateTypes
4683
- });
4684
- printValidationResult(result, options.verbose);
4685
- if (!result.valid || options.strict && result.warnings.length > 0) {
4686
- process.exit(1);
4687
- }
5100
+ await runValidatePlugin(options.path, options);
4688
5101
  } catch (error) {
4689
5102
  getLogger().error(chalk.red("Validation error:") + " " + error.message);
4690
5103
  process.exit(1);
4691
5104
  }
4692
5105
  });
4693
- const ALL_ROLES = ["designer", "developer", "plugin"];
4694
- async function ensureAgentKitDocs(projectRoot, _force, mode) {
4695
- const agentKitDir = path$1.join(projectRoot, "agent-kit");
4696
- const thisDir = path$1.dirname(fileURLToPath(import.meta.url));
4697
- const templateDir = path$1.resolve(thisDir, "..", "agent-kit-template");
4698
- const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
4699
- for (const role of roles) {
4700
- const roleTemplateDir = path$1.join(templateDir, role);
4701
- const roleOutputDir = path$1.join(agentKitDir, role);
4702
- let files;
4703
- try {
4704
- files = (await fs$1.readdir(roleTemplateDir)).filter((f) => f.endsWith(".md"));
4705
- } catch {
4706
- continue;
4707
- }
4708
- await fs$1.mkdir(roleOutputDir, { recursive: true });
4709
- for (const filename of files) {
4710
- await fs$1.copyFile(
4711
- path$1.join(roleTemplateDir, filename),
4712
- path$1.join(roleOutputDir, filename)
4713
- );
4714
- getLogger().info(chalk.gray(` Created agent-kit/${role}/${filename}`));
4715
- }
4716
- }
4717
- }
4718
- async function mergePluginAgentKitGuides(projectRoot, mode) {
4719
- const plugins = await scanPlugins$1({ projectRoot });
4720
- const agentKitDir = path$1.join(projectRoot, "agent-kit");
4721
- const roles = mode && ALL_ROLES.includes(mode) ? [mode] : ALL_ROLES;
4722
- const copiedPerRole = /* @__PURE__ */ new Map();
4723
- for (const [, plugin] of plugins) {
4724
- const pluginAgentKitDir = path$1.join(plugin.pluginPath, "agent-kit");
4725
- if (!fsSync.existsSync(pluginAgentKitDir))
4726
- continue;
4727
- for (const role of roles) {
4728
- const roleSourceDir = path$1.join(pluginAgentKitDir, role);
4729
- let files;
4730
- try {
4731
- files = (await fs$1.readdir(roleSourceDir)).filter(
4732
- (f) => f.endsWith(".md") && f !== "INSTRUCTIONS.md"
4733
- );
4734
- } catch {
4735
- continue;
4736
- }
4737
- if (files.length === 0)
4738
- continue;
4739
- const roleOutputDir = path$1.join(agentKitDir, role);
4740
- await fs$1.mkdir(roleOutputDir, { recursive: true });
4741
- for (const filename of files) {
4742
- const sourcePath = path$1.join(roleSourceDir, filename);
4743
- await fs$1.copyFile(sourcePath, path$1.join(roleOutputDir, filename));
4744
- let description = "";
4745
- try {
4746
- const content = await fs$1.readFile(sourcePath, "utf-8");
4747
- const lines = content.split("\n");
4748
- let pastHeading = false;
4749
- for (const line of lines) {
4750
- if (line.startsWith("# ")) {
4751
- pastHeading = true;
4752
- continue;
4753
- }
4754
- if (pastHeading && line.trim()) {
4755
- description = line.trim();
4756
- break;
4757
- }
4758
- }
4759
- } catch {
4760
- }
4761
- if (!copiedPerRole.has(role))
4762
- copiedPerRole.set(role, []);
4763
- copiedPerRole.get(role).push({ filename, pluginName: plugin.name, description });
4764
- getLogger().info(
4765
- chalk.gray(
4766
- ` Copied agent-kit/${role}/${filename} from plugin "${plugin.name}"`
4767
- )
4768
- );
4769
- }
4770
- }
4771
- }
4772
- for (const [role, entries] of copiedPerRole) {
4773
- const instructionsPath = path$1.join(agentKitDir, role, "INSTRUCTIONS.md");
4774
- if (!fsSync.existsSync(instructionsPath))
4775
- continue;
4776
- const lines = [
4777
- "",
4778
- "## Plugin-Contributed Guides",
4779
- "",
4780
- "| File | Plugin | Description |",
4781
- "| --- | --- | --- |"
4782
- ];
4783
- for (const { filename, pluginName, description } of entries) {
4784
- lines.push(`| [${filename}](${filename}) | ${pluginName} | ${description} |`);
4785
- }
4786
- lines.push("");
4787
- await fs$1.appendFile(instructionsPath, lines.join("\n"));
4788
- }
4789
- }
4790
- async function generatePluginReferences(projectRoot, options, initErrors, viteServer) {
4791
- const { discoverPluginsWithReferences, executePluginReferences } = await import("@jay-framework/stack-server-runtime");
4792
- const plugins = await discoverPluginsWithReferences({
4793
- projectRoot,
4794
- verbose: options.verbose,
4795
- pluginFilter: options.plugin
4796
- });
4797
- if (plugins.length === 0)
4798
- return;
4799
- const logger = getLogger();
4800
- logger.important("");
4801
- logger.important(chalk.bold("📚 Generating plugin references..."));
4802
- for (const plugin of plugins) {
4803
- const pluginInitError = initErrors.get(plugin.name);
4804
- if (pluginInitError) {
4805
- logger.warn(
4806
- chalk.yellow(
4807
- ` ⚠️ ${plugin.name}: references skipped — init failed: ${pluginInitError.message}`
4808
- )
4809
- );
4810
- continue;
4811
- }
4812
- try {
4813
- const result = await executePluginReferences(plugin, {
4814
- projectRoot,
4815
- force: options.force ?? false,
4816
- viteServer,
4817
- verbose: options.verbose
4818
- });
4819
- if (result.referencesCreated.length > 0) {
4820
- logger.important(chalk.green(` ✅ ${plugin.name}:`));
4821
- for (const ref of result.referencesCreated) {
4822
- logger.important(chalk.gray(` ${ref}`));
4823
- }
4824
- if (result.message) {
4825
- logger.important(chalk.gray(` ${result.message}`));
4826
- }
4827
- }
4828
- } catch (error) {
4829
- logger.warn(
4830
- chalk.yellow(` ⚠️ ${plugin.name}: references skipped — ${error.message}`)
4831
- );
4832
- }
4833
- }
4834
- }
4835
- async function runMaterialize(projectRoot, options, defaultOutputRelative, keepViteAlive = false) {
4836
- const path2 = await import("node:path");
4837
- const outputDir = options.output ?? path2.join(projectRoot, defaultOutputRelative);
4838
- let viteServer;
4839
- let initErrors = /* @__PURE__ */ new Map();
4840
- try {
4841
- if (options.list) {
4842
- const index = await listContracts({
4843
- projectRoot,
4844
- dynamicOnly: options.dynamicOnly,
4845
- pluginFilter: options.plugin
4846
- });
4847
- if (options.yaml) {
4848
- getLogger().important(YAML.stringify(index));
4849
- } else {
4850
- printContractList(index);
4851
- }
4852
- return { initErrors };
4853
- }
4854
- if (options.verbose) {
4855
- getLogger().info("Starting Vite for TypeScript support...");
4856
- }
4857
- viteServer = await createViteForCli({ projectRoot });
4858
- const { services, initErrors: errors } = await initializeServicesForCli(
4859
- projectRoot,
4860
- viteServer
4861
- );
4862
- initErrors = errors;
4863
- const result = await materializeContracts(
4864
- {
4865
- projectRoot,
4866
- outputDir,
4867
- force: options.force,
4868
- dynamicOnly: options.dynamicOnly,
4869
- pluginFilter: options.plugin,
4870
- verbose: options.verbose,
4871
- viteServer
4872
- },
4873
- services
4874
- );
4875
- if (options.yaml) {
4876
- getLogger().important(YAML.stringify(result.pluginsIndex));
4877
- } else {
4878
- const totalContracts = result.pluginsIndex.plugins.reduce(
4879
- (sum, p) => sum + p.contracts.length,
4880
- 0
4881
- );
4882
- getLogger().important(chalk.green(`
4883
- ✅ Materialized ${totalContracts} contracts`));
4884
- getLogger().important(` Static: ${result.staticCount}`);
4885
- getLogger().important(` Dynamic: ${result.dynamicCount}`);
4886
- getLogger().important(` Output: ${result.outputDir}`);
4887
- }
4888
- return { initErrors, viteServer: keepViteAlive ? viteServer : void 0 };
4889
- } catch (error) {
4890
- getLogger().error(chalk.red("❌ Failed to materialize contracts:") + " " + error.message);
4891
- if (options.verbose) {
4892
- getLogger().error(error.stack);
4893
- }
4894
- process.exit(1);
4895
- } finally {
4896
- if (viteServer && !keepViteAlive) {
4897
- await viteServer.close();
4898
- }
4899
- }
4900
- return { initErrors };
4901
- }
4902
- program.command("setup [plugin]").description(
4903
- "Run plugin setup: create config templates, validate credentials, generate reference data"
4904
- ).option("--force", "Force re-run (overwrite config templates and regenerate references)").option("-v, --verbose", "Show detailed output").action(async (plugin, options) => {
5106
+ program.command("setup [plugin]").description("Run plugin setup: config templates, credential validation, reference data").option("--force", "Force re-run (overwrite config templates and regenerate references)").option("-v, --verbose", "Show detailed output").action(async (plugin, options) => {
4905
5107
  await runSetup(plugin, options, process.cwd(), initializeServicesForCli);
4906
5108
  });
4907
- program.command("agent-kit").description(
4908
- "Prepare the agent kit: materialize contracts, generate references, and write docs to agent-kit/"
4909
- ).option("-o, --output <dir>", "Output directory (default: agent-kit/materialized-contracts)").option("--yaml", "Output contract index as YAML to stdout").option("--list", "List contracts without writing files").option("--plugin <name>", "Filter to specific plugin").option("--dynamic-only", "Only process dynamic contracts").option("--force", "Force re-materialization").option("--no-references", "Skip reference data generation").option(
5109
+ program.command("agent-kit").description("Prepare agent kit: materialize contracts, generate references, write docs").option("-o, --output <dir>", "Output directory (default: agent-kit/materialized-contracts)").option("--yaml", "Output contract index as YAML to stdout").option("--list", "List contracts without writing files").option("--plugin <name>", "Filter to specific plugin").option("--dynamic-only", "Only process dynamic contracts").option("--force", "Force re-materialization").option("--no-references", "Skip reference data generation").option(
4910
5110
  "-m, --mode <role>",
4911
5111
  "Generate guides for a specific role: designer, developer, or plugin (default: all)"
4912
5112
  ).option("-v, --verbose", "Show detailed output").action(async (options) => {
4913
- const projectRoot = process.cwd();
4914
- const { initErrors, viteServer } = await runMaterialize(
4915
- projectRoot,
4916
- options,
4917
- "agent-kit/materialized-contracts",
4918
- /* keepViteAlive */
4919
- true
4920
- );
4921
- try {
4922
- if (!options.list) {
4923
- await ensureAgentKitDocs(projectRoot, options.force, options.mode);
4924
- await mergePluginAgentKitGuides(projectRoot, options.mode);
4925
- if (options.references !== false) {
4926
- await generatePluginReferences(projectRoot, options, initErrors, viteServer);
4927
- }
4928
- }
4929
- } finally {
4930
- if (viteServer) {
4931
- await viteServer.close();
4932
- }
4933
- }
5113
+ await runAgentKit(options);
4934
5114
  });
4935
- program.command("action <plugin/action>").description(
4936
- `Run a plugin action (e.g., jay-stack action wix-stores/searchProducts --input '{"query":""}')`
4937
- ).option("--input <json>", "JSON input for the action (default: {})").option("--yaml", "Output result as YAML instead of JSON").option("-v, --verbose", "Show detailed output").action(async (actionRef, options) => {
5115
+ program.command("action <plugin/action>").description("Run a plugin action (e.g., jay-stack action wix-stores/searchProducts)").option("--input <json>", "JSON input for the action (default: {})").option("--yaml", "Output result as YAML instead of JSON").option("-v, --verbose", "Show detailed output").action(async (actionRef, options) => {
4938
5116
  await runAction(actionRef, options, process.cwd(), initializeServicesForCli);
4939
5117
  });
4940
- program.command("params <plugin/contract>").description(
4941
- "Discover load param values for a contract. Streams results as NDJSON (one JSON object per line) or YAML."
4942
- ).option("--yaml", "Output as YAML instead of NDJSON").option("-v, --verbose", "Show detailed output").action(async (contractRef, options) => {
5118
+ program.command("params <plugin/contract>").description("Discover load param values for a contract (NDJSON or YAML)").option("--yaml", "Output as YAML instead of NDJSON").option("-v, --verbose", "Show detailed output").action(async (contractRef, options) => {
4943
5119
  await runParams(contractRef, options, process.cwd(), initializeServicesForCli);
4944
5120
  });
4945
5121
  program.parse(process.argv);
4946
5122
  if (!process.argv.slice(2).length) {
4947
5123
  program.outputHelp();
4948
5124
  }
4949
- function printValidationResult(result, verbose) {
4950
- const logger = getLogger();
4951
- if (result.valid && result.warnings.length === 0) {
4952
- logger.important(chalk.green("✅ Plugin validation successful!\n"));
4953
- if (verbose) {
4954
- logger.important("Plugin: " + result.pluginName);
4955
- logger.important(" ✅ plugin.yaml valid");
4956
- logger.important(` ✅ ${result.contractsChecked} contracts validated`);
4957
- if (result.typesGenerated) {
4958
- logger.important(` ✅ ${result.typesGenerated} type definitions generated`);
4959
- }
4960
- logger.important(` ✅ ${result.componentsChecked} components validated`);
4961
- if (result.packageJsonChecked) {
4962
- logger.important(" ✅ package.json valid");
4963
- }
4964
- logger.important("\nNo errors found.");
4965
- }
4966
- } else if (result.valid && result.warnings.length > 0) {
4967
- logger.important(chalk.yellow("⚠️ Plugin validation passed with warnings\n"));
4968
- logger.important("Warnings:");
4969
- result.warnings.forEach((warning) => {
4970
- logger.important(chalk.yellow(` ⚠️ ${warning.message}`));
4971
- if (warning.location) {
4972
- logger.important(chalk.gray(` Location: ${warning.location}`));
4973
- }
4974
- if (warning.suggestion) {
4975
- logger.important(chalk.gray(` → ${warning.suggestion}`));
4976
- }
4977
- logger.important("");
4978
- });
4979
- logger.important(chalk.gray("Use --strict to treat warnings as errors."));
4980
- } else {
4981
- logger.important(chalk.red("❌ Plugin validation failed\n"));
4982
- logger.important("Errors:");
4983
- result.errors.forEach((error) => {
4984
- logger.important(chalk.red(` ❌ ${error.message}`));
4985
- if (error.location) {
4986
- logger.important(chalk.gray(` Location: ${error.location}`));
4987
- }
4988
- if (error.suggestion) {
4989
- logger.important(chalk.gray(` → ${error.suggestion}`));
4990
- }
4991
- logger.important("");
4992
- });
4993
- logger.important(chalk.red(`${result.errors.length} errors found.`));
4994
- }
4995
- }
4996
- function printContractList(index) {
4997
- const logger = getLogger();
4998
- logger.important("\nAvailable Contracts:\n");
4999
- for (const plugin of index.plugins) {
5000
- logger.important(chalk.bold(`📦 ${plugin.name}`));
5001
- for (const contract of plugin.contracts) {
5002
- const typeIcon = contract.type === "static" ? "📄" : "⚡";
5003
- logger.important(` ${typeIcon} ${contract.name}`);
5004
- }
5005
- logger.important("");
5006
- }
5007
- if (index.plugins.length === 0) {
5008
- logger.important(chalk.gray("No contracts found."));
5009
- }
5010
- }
5011
5125
  export {
5012
5126
  createEditorHandlers,
5013
5127
  getConfigWithDefaults,