lean-spec 0.2.3 → 0.2.4-dev.20251118091714

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.
@@ -6974,7 +6974,7 @@ function detectPackageManager(baseDir = process.cwd()) {
6974
6974
 
6975
6975
  // src/commands/ui.ts
6976
6976
  function uiCommand() {
6977
- return new Command("ui").description("Start local web UI for spec management").option("-s, --specs <dir>", "Specs directory (auto-detected if not specified)").option("-p, --port <port>", "Port to run on", "3000").option("--no-open", "Don't open browser automatically").option("--dry-run", "Show what would run without executing").action(async (options) => {
6977
+ return new Command("ui").description("Start local web UI for spec management").option("-s, --specs <dir>", "Specs directory (auto-detected if not specified)").option("-p, --port <port>", "Port to run on", "3000").option("--no-open", "Don't open browser automatically").option("--dev", "Run in development mode (only works in LeanSpec monorepo)").option("--dry-run", "Show what would run without executing").action(async (options) => {
6978
6978
  try {
6979
6979
  await startUi(options);
6980
6980
  } catch (error) {
@@ -7002,21 +7002,38 @@ async function startUi(options) {
7002
7002
  console.log(chalk18.dim("\nRun `lean-spec init` to initialize LeanSpec in this directory."));
7003
7003
  throw new Error(`Specs directory not found: ${specsDir}`);
7004
7004
  }
7005
- const localWebDir = join(cwd, "packages/web");
7006
- const isMonorepo = existsSync(localWebDir);
7007
- if (isMonorepo) {
7008
- return runLocalWeb(localWebDir, specsDir, options);
7009
- } else {
7010
- return runPublishedUI(cwd, specsDir, options);
7005
+ if (options.dev) {
7006
+ const isLeanSpecMonorepo = checkIsLeanSpecMonorepo(cwd);
7007
+ if (!isLeanSpecMonorepo) {
7008
+ console.error(chalk18.red(`\u2717 Development mode only works in the LeanSpec monorepo`));
7009
+ console.log(chalk18.dim("Remove --dev flag to use production mode"));
7010
+ throw new Error("Not in LeanSpec monorepo");
7011
+ }
7012
+ const localUiDir = join(cwd, "packages/ui");
7013
+ return runLocalWeb(localUiDir, specsDir, options);
7011
7014
  }
7015
+ return runPublishedUI(cwd, specsDir, options);
7012
7016
  }
7013
- async function runLocalWeb(webDir, specsDir, options) {
7014
- console.log(chalk18.dim("\u2192 Detected LeanSpec monorepo, using local web package\n"));
7015
- const repoRoot = resolve(webDir, "..", "..");
7017
+ function checkIsLeanSpecMonorepo(cwd) {
7018
+ const localUiDir = join(cwd, "packages/ui");
7019
+ const uiPackageJson = join(localUiDir, "package.json");
7020
+ if (!existsSync(uiPackageJson)) {
7021
+ return false;
7022
+ }
7023
+ try {
7024
+ const packageJson2 = JSON.parse(readFileSync(uiPackageJson, "utf-8"));
7025
+ return packageJson2.name === "@leanspec/ui";
7026
+ } catch {
7027
+ return false;
7028
+ }
7029
+ }
7030
+ async function runLocalWeb(uiDir, specsDir, options) {
7031
+ console.log(chalk18.dim("\u2192 Detected LeanSpec monorepo, using local ui package\n"));
7032
+ const repoRoot = resolve(uiDir, "..", "..");
7016
7033
  const packageManager = detectPackageManager(repoRoot);
7017
7034
  if (options.dryRun) {
7018
7035
  console.log(chalk18.cyan("Would run:"));
7019
- console.log(chalk18.dim(` cd ${webDir}`));
7036
+ console.log(chalk18.dim(` cd ${uiDir}`));
7020
7037
  console.log(chalk18.dim(` SPECS_MODE=filesystem SPECS_DIR=${specsDir} PORT=${options.port} ${packageManager} run dev`));
7021
7038
  if (options.open) {
7022
7039
  console.log(chalk18.dim(` open http://localhost:${options.port}`));
@@ -7031,9 +7048,10 @@ async function runLocalWeb(webDir, specsDir, options) {
7031
7048
  PORT: options.port
7032
7049
  };
7033
7050
  const child = spawn(packageManager, ["run", "dev"], {
7034
- cwd: webDir,
7051
+ cwd: uiDir,
7035
7052
  stdio: "inherit",
7036
- env
7053
+ env,
7054
+ detached: true
7037
7055
  });
7038
7056
  const readyTimeout = setTimeout(async () => {
7039
7057
  spinner.succeed("Web UI running");
@@ -7053,14 +7071,28 @@ async function runLocalWeb(webDir, specsDir, options) {
7053
7071
  }
7054
7072
  }
7055
7073
  }, 3e3);
7056
- const sigintHandler = () => {
7074
+ const shutdown = (signal) => {
7057
7075
  clearTimeout(readyTimeout);
7058
7076
  spinner.stop();
7059
- child.kill("SIGTERM");
7077
+ try {
7078
+ if (child && child.pid) {
7079
+ try {
7080
+ process.kill(-child.pid, signal ?? "SIGTERM");
7081
+ } catch (err) {
7082
+ child.kill(signal ?? "SIGTERM");
7083
+ }
7084
+ }
7085
+ } catch (err) {
7086
+ }
7060
7087
  console.log(chalk18.dim("\n\u2713 Web UI stopped"));
7061
7088
  process.exit(0);
7062
7089
  };
7063
- process.once("SIGINT", sigintHandler);
7090
+ process.once("SIGINT", () => shutdown("SIGINT"));
7091
+ process.once("SIGTERM", () => shutdown("SIGTERM"));
7092
+ process.once("SIGHUP", () => shutdown("SIGHUP"));
7093
+ if (process.stdin && !process.stdin.destroyed) {
7094
+ process.stdin.once("end", () => shutdown("SIGTERM"));
7095
+ }
7064
7096
  child.on("exit", (code) => {
7065
7097
  clearTimeout(readyTimeout);
7066
7098
  spinner.stop();
@@ -7070,6 +7102,7 @@ async function runLocalWeb(webDir, specsDir, options) {
7070
7102
  Process exited with code ${code}`));
7071
7103
  process.exit(code);
7072
7104
  }
7105
+ process.exit(0);
7073
7106
  });
7074
7107
  }
7075
7108
  async function runPublishedUI(cwd, specsDir, options) {
@@ -7083,16 +7116,32 @@ async function runPublishedUI(cwd, specsDir, options) {
7083
7116
  }
7084
7117
  const child = spawn(command, args, {
7085
7118
  stdio: "inherit",
7086
- env: process.env
7119
+ env: process.env,
7120
+ detached: true
7087
7121
  });
7088
- const shutdown = () => {
7089
- child.kill("SIGINT");
7122
+ const shutdownPublished = (signal) => {
7123
+ try {
7124
+ if (child && child.pid) {
7125
+ try {
7126
+ process.kill(-child.pid, signal ?? "SIGINT");
7127
+ } catch (err) {
7128
+ child.kill(signal ?? "SIGINT");
7129
+ }
7130
+ }
7131
+ } catch (err) {
7132
+ }
7090
7133
  console.log(chalk18.dim("\n\u2713 Web UI stopped"));
7091
7134
  process.exit(0);
7092
7135
  };
7093
- process.once("SIGINT", shutdown);
7136
+ process.once("SIGINT", () => shutdownPublished("SIGINT"));
7137
+ process.once("SIGTERM", () => shutdownPublished("SIGTERM"));
7138
+ process.once("SIGHUP", () => shutdownPublished("SIGHUP"));
7139
+ if (process.stdin && !process.stdin.destroyed) {
7140
+ process.stdin.once("end", () => shutdownPublished("SIGTERM"));
7141
+ }
7094
7142
  child.on("exit", (code) => {
7095
7143
  if (code === 0 || code === null) {
7144
+ process.exit(0);
7096
7145
  return;
7097
7146
  }
7098
7147
  console.error(chalk18.red(`
@@ -7112,7 +7161,7 @@ function buildUiRunner(packageManager, specsDir, port, openBrowser) {
7112
7161
  uiArgs.push("--no-open");
7113
7162
  }
7114
7163
  if (packageManager === "pnpm") {
7115
- const args2 = ["dlx", "--prefer-offline", ...uiArgs];
7164
+ const args2 = ["dlx", ...uiArgs];
7116
7165
  return { command: "pnpm", args: args2, preview: `pnpm ${args2.join(" ")}` };
7117
7166
  }
7118
7167
  if (packageManager === "yarn") {
@@ -8406,5 +8455,5 @@ if (import.meta.url === `file://${process.argv[1]}`) {
8406
8455
  }
8407
8456
 
8408
8457
  export { analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, createMcpServer, depsCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand };
8409
- //# sourceMappingURL=chunk-WCNEWMW3.js.map
8410
- //# sourceMappingURL=chunk-WCNEWMW3.js.map
8458
+ //# sourceMappingURL=chunk-EICKCYUL.js.map
8459
+ //# sourceMappingURL=chunk-EICKCYUL.js.map