openspecui 0.9.0 → 0.9.3

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/cli.mjs +152 -76
  2. package/package.json +2 -1
package/dist/cli.mjs CHANGED
@@ -2647,14 +2647,14 @@ function usage(yargs, shim$2) {
2647
2647
  if (shim$2.process.stdColumns) return Math.min(maxWidth$1, shim$2.process.stdColumns);
2648
2648
  else return maxWidth$1;
2649
2649
  }
2650
- let version = null;
2650
+ let version$1 = null;
2651
2651
  self.version = (ver) => {
2652
- version = ver;
2652
+ version$1 = ver;
2653
2653
  };
2654
2654
  self.showVersion = (level) => {
2655
2655
  const logger = yargs.getInternalMethods().getLoggerInstance();
2656
2656
  if (!level) level = "error";
2657
- (typeof level === "function" ? level : logger[level])(version);
2657
+ (typeof level === "function" ? level : logger[level])(version$1);
2658
2658
  };
2659
2659
  self.reset = function reset(localLookup) {
2660
2660
  failMessage = null;
@@ -4502,10 +4502,13 @@ function isYargsInstance(y) {
4502
4502
  const Yargs = YargsFactory(esm_default);
4503
4503
  var yargs_default = Yargs;
4504
4504
 
4505
+ //#endregion
4506
+ //#region package.json
4507
+ var version = "0.9.3";
4508
+
4505
4509
  //#endregion
4506
4510
  //#region src/export.ts
4507
4511
  const __dirname$1 = dirname$1(fileURLToPath$1(import.meta.url));
4508
- const WEB_PACKAGE_VERSION = "0.9.0";
4509
4512
  /**
4510
4513
  * Generate a complete data snapshot of the OpenSpec project
4511
4514
  * (Kept for backwards compatibility and testing)
@@ -4585,7 +4588,7 @@ async function generateSnapshot(projectDir) {
4585
4588
  return {
4586
4589
  meta: {
4587
4590
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
4588
- version: WEB_PACKAGE_VERSION,
4591
+ version,
4589
4592
  projectDir
4590
4593
  },
4591
4594
  dashboard: {
@@ -4601,62 +4604,111 @@ async function generateSnapshot(projectDir) {
4601
4604
  };
4602
4605
  }
4603
4606
  /**
4604
- * Detect the package manager being used
4607
+ * Check if running in local monorepo development mode
4608
+ * Returns the path to web package root if available, null otherwise
4609
+ */
4610
+ function findLocalWebPackage() {
4611
+ if (existsSync(join$1(__dirname$1, "..", "..", "web", "package.json"))) return join$1(__dirname$1, "..", "..", "web");
4612
+ return null;
4613
+ }
4614
+ /**
4615
+ * Run a command and wait for it to complete
4616
+ */
4617
+ function runCommand(cmd, args, cwd) {
4618
+ return new Promise((resolvePromise, reject) => {
4619
+ const child = spawn(cmd, args, {
4620
+ stdio: "inherit",
4621
+ cwd,
4622
+ shell: true
4623
+ });
4624
+ child.on("close", (code) => {
4625
+ if (code === 0) resolvePromise();
4626
+ else reject(/* @__PURE__ */ new Error(`Command failed with exit code ${code}`));
4627
+ });
4628
+ child.on("error", (err) => reject(err));
4629
+ });
4630
+ }
4631
+ /**
4632
+ * Detect the package manager used in the current project
4605
4633
  */
4606
4634
  function detectPackageManager() {
4607
- const userAgent = process.env.npm_config_user_agent || "";
4608
- if (userAgent.includes("pnpm")) return "pnpm";
4609
- if (userAgent.includes("yarn")) return "yarn";
4610
- if (userAgent.includes("bun")) return "bun";
4635
+ if (process.env.DENO_VERSION) return "deno";
4636
+ const userAgent = process.env.npm_config_user_agent;
4637
+ if (userAgent) {
4638
+ if (userAgent.startsWith("bun")) return "bun";
4639
+ if (userAgent.startsWith("pnpm")) return "pnpm";
4640
+ if (userAgent.startsWith("yarn")) return "yarn";
4641
+ if (userAgent.startsWith("npm")) return "npm";
4642
+ if (userAgent.startsWith("deno")) return "deno";
4643
+ }
4644
+ if (existsSync("deno.lock")) return "deno";
4645
+ if (existsSync("bun.lockb") || existsSync("bun.lock")) return "bun";
4646
+ if (existsSync("pnpm-lock.yaml")) return "pnpm";
4647
+ if (existsSync("yarn.lock")) return "yarn";
4611
4648
  return "npm";
4612
4649
  }
4613
4650
  /**
4614
- * Get the command to execute a package binary
4651
+ * Get the exec command for running a package binary
4652
+ * Uses appropriate flags to ensure the correct version of @openspecui/web is installed
4615
4653
  */
4616
4654
  function getExecCommand(pm) {
4617
- const packageSpec = WEB_PACKAGE_VERSION.startsWith("__") ? "@openspecui/web" : `@openspecui/web@${WEB_PACKAGE_VERSION}`;
4655
+ const webPkgSpec = `@openspecui/web@${version}`;
4618
4656
  switch (pm) {
4657
+ case "bun": return {
4658
+ cmd: "bunx",
4659
+ args: [
4660
+ "-p",
4661
+ webPkgSpec,
4662
+ "openspecui-ssg"
4663
+ ]
4664
+ };
4619
4665
  case "pnpm": return {
4620
4666
  cmd: "pnpm",
4621
- args: ["dlx", packageSpec]
4667
+ args: ["dlx", webPkgSpec]
4622
4668
  };
4623
4669
  case "yarn": return {
4624
4670
  cmd: "yarn",
4625
- args: ["dlx", packageSpec]
4671
+ args: ["dlx", webPkgSpec]
4626
4672
  };
4627
- case "bun": return {
4628
- cmd: "bunx",
4629
- args: [packageSpec]
4673
+ case "deno": return {
4674
+ cmd: "deno",
4675
+ args: [
4676
+ "run",
4677
+ "-A",
4678
+ `npm:${webPkgSpec}/openspecui-ssg`
4679
+ ]
4630
4680
  };
4631
- case "npm":
4632
4681
  default: return {
4633
4682
  cmd: "npx",
4634
- args: [packageSpec]
4683
+ args: [
4684
+ "-p",
4685
+ webPkgSpec,
4686
+ "openspecui-ssg"
4687
+ ]
4635
4688
  };
4636
4689
  }
4637
4690
  }
4638
4691
  /**
4639
- * Check if running in local monorepo development mode
4640
- * Returns the path to local SSG CLI if available, null otherwise
4692
+ * Export as JSON only (data.json)
4641
4693
  */
4642
- function findLocalSSGCli() {
4643
- const localSsgTs = join$1(__dirname$1, "..", "..", "web", "src", "ssg", "cli.ts");
4644
- if (existsSync(localSsgTs)) return localSsgTs;
4645
- return null;
4694
+ async function exportJson(options) {
4695
+ const { projectDir, outputDir, clean } = options;
4696
+ if (clean && existsSync(outputDir)) rmSync(outputDir, { recursive: true });
4697
+ mkdirSync(outputDir, { recursive: true });
4698
+ console.log("Generating data snapshot...");
4699
+ const snapshot = await generateSnapshot(projectDir);
4700
+ const dataJsonPath = join$1(outputDir, "data.json");
4701
+ writeFileSync(dataJsonPath, JSON.stringify(snapshot, null, 2));
4702
+ console.log(`\nExported to ${dataJsonPath}`);
4703
+ console.log(` Specs: ${snapshot.specs.length}`);
4704
+ console.log(` Changes: ${snapshot.changes.length}`);
4705
+ console.log(` Archives: ${snapshot.archives.length}`);
4646
4706
  }
4647
4707
  /**
4648
- * Export the OpenSpec UI as a static website with SSG (pre-rendered HTML)
4649
- *
4650
- * This function:
4651
- * 1. Generates a data snapshot from the openspec/ directory
4652
- * 2. Writes data.json to the output directory
4653
- * 3. Delegates to @openspecui/web's SSG CLI for rendering
4654
- *
4655
- * In development (monorepo), it uses the local web package.
4656
- * In production, it uses npx/pnpm dlx to fetch the published package.
4708
+ * Export as static HTML site
4657
4709
  */
4658
- async function exportStaticSite(options) {
4659
- const { projectDir, outputDir, basePath, clean, open, previewPort, previewHost } = options;
4710
+ async function exportHtml(options) {
4711
+ const { projectDir, outputDir, basePath = "/", clean, open, previewPort, previewHost } = options;
4660
4712
  if (clean && existsSync(outputDir)) rmSync(outputDir, { recursive: true });
4661
4713
  mkdirSync(outputDir, { recursive: true });
4662
4714
  console.log("Generating data snapshot...");
@@ -4664,46 +4716,63 @@ async function exportStaticSite(options) {
4664
4716
  const dataJsonPath = join$1(outputDir, "data.json");
4665
4717
  writeFileSync(dataJsonPath, JSON.stringify(snapshot, null, 2));
4666
4718
  console.log(`Data snapshot written to ${dataJsonPath}`);
4667
- const ssgOnlyArgs = [
4668
- "--data",
4669
- dataJsonPath,
4670
- "--output",
4671
- outputDir
4672
- ];
4673
- if (basePath !== void 0) ssgOnlyArgs.push("--base-path", basePath);
4674
- if (open) {
4675
- ssgOnlyArgs.push("--open");
4676
- if (previewPort !== void 0) ssgOnlyArgs.push("--preview-port", String(previewPort));
4677
- if (previewHost !== void 0) ssgOnlyArgs.push("--host", previewHost);
4678
- }
4679
- const localCli = findLocalSSGCli();
4680
- let cmd;
4681
- let args;
4682
- if (localCli) {
4683
- cmd = "npx";
4684
- args = [
4719
+ const localWebPkg = findLocalWebPackage();
4720
+ if (localWebPkg) {
4721
+ console.log("\n[Local dev mode] Running SSG from local web package...");
4722
+ await runCommand("pnpm", [
4685
4723
  "tsx",
4686
- localCli,
4687
- ...ssgOnlyArgs
4688
- ];
4724
+ join$1(localWebPkg, "src", "ssg", "cli.ts"),
4725
+ "--data",
4726
+ dataJsonPath,
4727
+ "--output",
4728
+ outputDir,
4729
+ "--base-path",
4730
+ basePath
4731
+ ], localWebPkg);
4689
4732
  } else {
4690
- const execCmd = getExecCommand(detectPackageManager());
4691
- cmd = execCmd.cmd;
4692
- args = [...execCmd.args, ...ssgOnlyArgs];
4733
+ console.log("\n[Production mode] Running SSG via @openspecui/web...");
4734
+ const pm = detectPackageManager();
4735
+ const execCmd = getExecCommand(pm);
4736
+ try {
4737
+ await runCommand(execCmd.cmd, [
4738
+ ...execCmd.args,
4739
+ "--data",
4740
+ dataJsonPath,
4741
+ "--output",
4742
+ outputDir,
4743
+ "--base-path",
4744
+ basePath
4745
+ ], process.cwd());
4746
+ } catch (err) {
4747
+ console.error("\nSSG failed. Make sure @openspecui/web is installed:");
4748
+ console.error(` ${pm} add @openspecui/web`);
4749
+ throw err;
4750
+ }
4751
+ }
4752
+ console.log(`\nExport complete: ${outputDir}`);
4753
+ if (open) {
4754
+ console.log("\nStarting preview server...");
4755
+ const previewArgs = [
4756
+ "vite",
4757
+ "preview",
4758
+ "--outDir",
4759
+ resolve$1(outputDir)
4760
+ ];
4761
+ if (previewPort) previewArgs.push("--port", String(previewPort));
4762
+ if (previewHost) previewArgs.push("--host", previewHost);
4763
+ previewArgs.push("--open");
4764
+ await runCommand(detectPackageManager(), previewArgs, outputDir);
4693
4765
  }
4694
- return new Promise((resolvePromise, reject) => {
4695
- const child = spawn(cmd, args, {
4696
- stdio: "inherit",
4697
- cwd: projectDir
4698
- });
4699
- child.on("close", (code) => {
4700
- if (code === 0) resolvePromise();
4701
- else reject(/* @__PURE__ */ new Error(`SSG export failed with exit code ${code}`));
4702
- });
4703
- child.on("error", (err) => {
4704
- reject(/* @__PURE__ */ new Error(`Failed to start SSG CLI: ${err.message}`));
4705
- });
4706
- });
4766
+ }
4767
+ /**
4768
+ * Export the OpenSpec project
4769
+ *
4770
+ * @param options Export options
4771
+ * @param options.format 'html' (default) - full static site, 'json' - data only
4772
+ */
4773
+ async function exportStaticSite(options) {
4774
+ if ((options.format || "html") === "json") await exportJson(options);
4775
+ else await exportHtml(options);
4707
4776
  }
4708
4777
 
4709
4778
  //#endregion
@@ -4778,12 +4847,18 @@ async function main() {
4778
4847
  console.error("❌ Failed to start server:", error);
4779
4848
  process.exit(1);
4780
4849
  }
4781
- }).command("export", "Export OpenSpec UI as a static website (alias for @openspecui/web ssg)", (yargs) => {
4850
+ }).command("export", "Export OpenSpec project as static website or JSON data", (yargs) => {
4782
4851
  return yargs.option("output", {
4783
4852
  alias: "o",
4784
- describe: "Output directory for static export",
4853
+ describe: "Output directory for export",
4785
4854
  type: "string",
4786
4855
  demandOption: true
4856
+ }).option("format", {
4857
+ alias: "f",
4858
+ describe: "Export format",
4859
+ type: "string",
4860
+ choices: ["html", "json"],
4861
+ default: "html"
4787
4862
  }).option("dir", {
4788
4863
  alias: "d",
4789
4864
  describe: "Project directory containing openspec/",
@@ -4813,6 +4888,7 @@ async function main() {
4813
4888
  await exportStaticSite({
4814
4889
  projectDir,
4815
4890
  outputDir,
4891
+ format: argv.format,
4816
4892
  basePath: argv["base-path"],
4817
4893
  clean: argv.clean,
4818
4894
  open: argv.open,
@@ -4824,7 +4900,7 @@ async function main() {
4824
4900
  console.error("❌ Export failed:", error);
4825
4901
  process.exit(1);
4826
4902
  }
4827
- }).example("$0", "Start server in current directory").example("$0 ./my-project", "Start server with specific project").example("$0 -p 8080", "Start server on custom port").example("$0 export -o ./dist", "Export to ./dist directory").example("$0 export --output ./public", "Export to ./public directory").example("$0 export -o ./dist --base-path=/docs/", "Export for subdirectory deployment").example("$0 export -o ./dist --clean", "Clean output directory before export").version(getVersion()).alias("v", "version").help().alias("h", "help").parse();
4903
+ }).example("$0", "Start server in current directory").example("$0 ./my-project", "Start server with specific project").example("$0 -p 8080", "Start server on custom port").example("$0 export -o ./dist", "Export HTML to ./dist directory").example("$0 export -o ./dist -f json", "Export JSON data only").example("$0 export -o ./dist --base-path=/docs/", "Export for subdirectory deployment").example("$0 export -o ./dist --clean", "Clean output directory before export").version(getVersion()).alias("v", "version").help().alias("h", "help").parse();
4828
4904
  }
4829
4905
  main();
4830
4906
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openspecui",
3
- "version": "0.9.0",
3
+ "version": "0.9.3",
4
4
  "description": "OpenSpec UI - Visual interface for spec-driven development",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -31,6 +31,7 @@
31
31
  "typescript": "^5.7.2",
32
32
  "vitest": "^2.1.8",
33
33
  "yargs": "^18.0.0",
34
+ "@openspecui/web": "0.9.3",
34
35
  "@openspecui/server": "0.9.0"
35
36
  },
36
37
  "scripts": {