maxsimcli 3.11.0 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/assets/CHANGELOG.md +19 -0
  3. package/dist/assets/dashboard/client/assets/index-CxFKStBk.css +32 -0
  4. package/dist/assets/dashboard/client/assets/{index-CZ8WC97G.js → index-wtQDvXzr.js} +64 -64
  5. package/dist/assets/dashboard/client/index.html +2 -2
  6. package/dist/assets/templates/agents/AGENTS.md +82 -0
  7. package/dist/assets/templates/commands/maxsim/settings.md +1 -1
  8. package/dist/assets/templates/skills/code-review/SKILL.md +151 -0
  9. package/dist/assets/templates/skills/memory-management/SKILL.md +174 -0
  10. package/dist/assets/templates/skills/simplify/SKILL.md +137 -0
  11. package/dist/assets/templates/skills/using-maxsim/SKILL.md +115 -0
  12. package/dist/assets/templates/templates/config.json +1 -1
  13. package/dist/assets/templates/workflows/add-tests.md +3 -3
  14. package/dist/assets/templates/workflows/complete-milestone.md +1 -1
  15. package/dist/assets/templates/workflows/execute-phase.md +4 -14
  16. package/dist/assets/templates/workflows/init-existing.md +7 -3
  17. package/dist/assets/templates/workflows/new-milestone.md +4 -0
  18. package/dist/assets/templates/workflows/new-project.md +6 -2
  19. package/dist/assets/templates/workflows/plan-phase.md +2 -2
  20. package/dist/assets/templates/workflows/settings.md +8 -4
  21. package/dist/assets/templates/workflows/verify-work.md +1 -1
  22. package/dist/cli.cjs +265 -161
  23. package/dist/cli.cjs.map +1 -1
  24. package/dist/cli.js +73 -204
  25. package/dist/cli.js.map +1 -1
  26. package/dist/core/commands.d.ts.map +1 -1
  27. package/dist/core/commands.js +4 -1
  28. package/dist/core/commands.js.map +1 -1
  29. package/dist/core/core.d.ts +18 -0
  30. package/dist/core/core.d.ts.map +1 -1
  31. package/dist/core/core.js +43 -13
  32. package/dist/core/core.js.map +1 -1
  33. package/dist/core/dashboard-launcher.d.ts +56 -0
  34. package/dist/core/dashboard-launcher.d.ts.map +1 -0
  35. package/dist/core/dashboard-launcher.js +243 -0
  36. package/dist/core/dashboard-launcher.js.map +1 -0
  37. package/dist/core/index.d.ts +3 -1
  38. package/dist/core/index.d.ts.map +1 -1
  39. package/dist/core/index.js +20 -2
  40. package/dist/core/index.js.map +1 -1
  41. package/dist/core/init.d.ts +0 -1
  42. package/dist/core/init.d.ts.map +1 -1
  43. package/dist/core/init.js +0 -1
  44. package/dist/core/init.js.map +1 -1
  45. package/dist/core/phase.d.ts.map +1 -1
  46. package/dist/core/phase.js +7 -1
  47. package/dist/core/phase.js.map +1 -1
  48. package/dist/core/roadmap.d.ts.map +1 -1
  49. package/dist/core/roadmap.js +1 -0
  50. package/dist/core/roadmap.js.map +1 -1
  51. package/dist/core/state.d.ts.map +1 -1
  52. package/dist/core/state.js +7 -5
  53. package/dist/core/state.js.map +1 -1
  54. package/dist/core/types.d.ts +1 -2
  55. package/dist/core/types.d.ts.map +1 -1
  56. package/dist/core/types.js +1 -2
  57. package/dist/core/types.js.map +1 -1
  58. package/dist/install/adapters.d.ts +15 -0
  59. package/dist/install/adapters.d.ts.map +1 -0
  60. package/dist/install/adapters.js +203 -0
  61. package/dist/install/adapters.js.map +1 -0
  62. package/dist/install/copy.d.ts +15 -0
  63. package/dist/install/copy.d.ts.map +1 -0
  64. package/dist/install/copy.js +191 -0
  65. package/dist/install/copy.js.map +1 -0
  66. package/dist/install/dashboard.d.ts +16 -0
  67. package/dist/install/dashboard.d.ts.map +1 -0
  68. package/dist/install/dashboard.js +273 -0
  69. package/dist/install/dashboard.js.map +1 -0
  70. package/dist/install/hooks.d.ts +32 -0
  71. package/dist/install/hooks.d.ts.map +1 -0
  72. package/dist/install/hooks.js +285 -0
  73. package/dist/install/hooks.js.map +1 -0
  74. package/dist/install/index.d.ts +2 -0
  75. package/dist/install/index.d.ts.map +1 -0
  76. package/dist/install/index.js +598 -0
  77. package/dist/install/index.js.map +1 -0
  78. package/dist/install/manifest.d.ts +20 -0
  79. package/dist/install/manifest.d.ts.map +1 -0
  80. package/dist/install/manifest.js +135 -0
  81. package/dist/install/manifest.js.map +1 -0
  82. package/dist/install/patches.d.ts +11 -0
  83. package/dist/install/patches.d.ts.map +1 -0
  84. package/dist/install/patches.js +136 -0
  85. package/dist/install/patches.js.map +1 -0
  86. package/dist/install/shared.d.ts +50 -0
  87. package/dist/install/shared.d.ts.map +1 -0
  88. package/dist/install/shared.js +142 -0
  89. package/dist/install/shared.js.map +1 -0
  90. package/dist/install/uninstall.d.ts +6 -0
  91. package/dist/install/uninstall.d.ts.map +1 -0
  92. package/dist/install/uninstall.js +280 -0
  93. package/dist/install/uninstall.js.map +1 -0
  94. package/dist/install.cjs +763 -709
  95. package/dist/install.cjs.map +1 -1
  96. package/dist/mcp-server.cjs.map +1 -1
  97. package/package.json +1 -1
  98. package/dist/assets/dashboard/client/assets/index-DzJChB-D.css +0 -32
  99. package/dist/install.d.ts +0 -2
  100. package/dist/install.d.ts.map +0 -1
  101. package/dist/install.js +0 -1841
  102. package/dist/install.js.map +0 -1
package/dist/cli.cjs CHANGED
@@ -31,10 +31,9 @@ let node_fs = require("node:fs");
31
31
  node_fs = __toESM(node_fs);
32
32
  let node_path = require("node:path");
33
33
  node_path = __toESM(node_path);
34
+ let node_child_process = require("node:child_process");
34
35
  let node_os = require("node:os");
35
36
  node_os = __toESM(node_os);
36
- let node_child_process = require("node:child_process");
37
- let node_module = require("node:module");
38
37
  let node_buffer = require("node:buffer");
39
38
  let child_process = require("child_process");
40
39
  let node_events = require("node:events");
@@ -42,6 +41,7 @@ let node_process = require("node:process");
42
41
  node_process = __toESM(node_process);
43
42
  let node_tty = require("node:tty");
44
43
  node_tty = __toESM(node_tty);
44
+ let node_module = require("node:module");
45
45
 
46
46
  //#region src/core/types.ts
47
47
  const PLANNING_CONFIG_DEFAULTS = {
@@ -53,9 +53,8 @@ const PLANNING_CONFIG_DEFAULTS = {
53
53
  milestone_branch_template: "maxsim/{milestone}-{slug}",
54
54
  workflow: {
55
55
  research: true,
56
- plan_check: true,
57
- verifier: true,
58
- nyquist_validation: false
56
+ plan_checker: true,
57
+ verifier: true
59
58
  },
60
59
  parallelization: true,
61
60
  brave_search: false
@@ -594,7 +593,7 @@ var require_has_flag = /* @__PURE__ */ __commonJSMin(((exports, module) => {
594
593
  //#endregion
595
594
  //#region ../../node_modules/supports-color/index.js
596
595
  var require_supports_color = /* @__PURE__ */ __commonJSMin(((exports, module) => {
597
- const os$4 = require("os");
596
+ const os$5 = require("os");
598
597
  const tty$2 = require("tty");
599
598
  const hasFlag = require_has_flag();
600
599
  const { env } = process;
@@ -621,7 +620,7 @@ var require_supports_color = /* @__PURE__ */ __commonJSMin(((exports, module) =>
621
620
  const min = forceColor || 0;
622
621
  if (env.TERM === "dumb") return min;
623
622
  if (process.platform === "win32") {
624
- const osRelease = os$4.release().split(".");
623
+ const osRelease = os$5.release().split(".");
625
624
  if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) return Number(osRelease[2]) >= 14931 ? 3 : 2;
626
625
  return 1;
627
626
  }
@@ -4711,21 +4710,47 @@ const MODEL_PROFILES = {
4711
4710
  tokenburner: "opus"
4712
4711
  }
4713
4712
  };
4713
+ /** Thrown by output() to signal successful command completion. */
4714
+ var CliOutput = class {
4715
+ result;
4716
+ raw;
4717
+ rawValue;
4718
+ constructor(result, raw, rawValue) {
4719
+ this.result = result;
4720
+ this.raw = raw ?? false;
4721
+ this.rawValue = rawValue;
4722
+ }
4723
+ };
4724
+ /** Thrown by error() to signal a command error. */
4725
+ var CliError = class {
4726
+ message;
4727
+ constructor(message) {
4728
+ this.message = message;
4729
+ }
4730
+ };
4714
4731
  function output(result, raw, rawValue) {
4715
- if (raw && rawValue !== void 0) process.stdout.write(String(rawValue));
4732
+ throw new CliOutput(result, raw, rawValue);
4733
+ }
4734
+ function error(message) {
4735
+ throw new CliError(message);
4736
+ }
4737
+ /** Re-throw CliOutput/CliError signals so catch blocks don't intercept them */
4738
+ function rethrowCliSignals(e) {
4739
+ if (e instanceof CliOutput || e instanceof CliError) throw e;
4740
+ }
4741
+ /**
4742
+ * Handle a CliOutput by writing to stdout. Extracted so cli.ts can use it.
4743
+ */
4744
+ function writeOutput(out) {
4745
+ if (out.raw && out.rawValue !== void 0) process.stdout.write(String(out.rawValue));
4716
4746
  else {
4717
- const json = JSON.stringify(result, null, 2);
4747
+ const json = JSON.stringify(out.result, null, 2);
4718
4748
  if (json.length > 5e4) {
4719
4749
  const tmpPath = node_path.default.join(node_os.default.tmpdir(), `maxsim-${Date.now()}.json`);
4720
4750
  node_fs.default.writeFileSync(tmpPath, json, "utf-8");
4721
4751
  process.stdout.write("@file:" + tmpPath);
4722
4752
  } else process.stdout.write(json);
4723
4753
  }
4724
- process.exit(0);
4725
- }
4726
- function error(message) {
4727
- process.stderr.write("Error: " + message + "\n");
4728
- process.exit(1);
4729
4754
  }
4730
4755
  /** Today's date as YYYY-MM-DD. */
4731
4756
  function todayISO() {
@@ -4833,6 +4858,9 @@ function loadConfig(cwd) {
4833
4858
  field: "research"
4834
4859
  }) ?? defaults.research,
4835
4860
  plan_checker: get("plan_checker", {
4861
+ section: "workflow",
4862
+ field: "plan_checker"
4863
+ }) ?? get("plan_checker", {
4836
4864
  section: "workflow",
4837
4865
  field: "plan_check"
4838
4866
  }) ?? defaults.plan_checker,
@@ -12079,7 +12107,7 @@ function cmdStateLoad(cwd, raw) {
12079
12107
  };
12080
12108
  if (raw) {
12081
12109
  const c = config;
12082
- const lines = [
12110
+ output(result, true, [
12083
12111
  `model_profile=${c.model_profile}`,
12084
12112
  `commit_docs=${c.commit_docs}`,
12085
12113
  `branching_strategy=${c.branching_strategy}`,
@@ -12092,9 +12120,7 @@ function cmdStateLoad(cwd, raw) {
12092
12120
  `config_exists=${configExists}`,
12093
12121
  `roadmap_exists=${roadmapExists}`,
12094
12122
  `state_exists=${stateExists}`
12095
- ];
12096
- process.stdout.write(lines.join("\n"));
12097
- process.exit(0);
12123
+ ].join("\n"));
12098
12124
  }
12099
12125
  output(result);
12100
12126
  }
@@ -12120,7 +12146,8 @@ function cmdStateGet(cwd, section, raw) {
12120
12146
  return;
12121
12147
  }
12122
12148
  output({ error: `Section or field "${section}" not found` }, raw, "");
12123
- } catch {
12149
+ } catch (e) {
12150
+ rethrowCliSignals(e);
12124
12151
  error("STATE.md not found");
12125
12152
  }
12126
12153
  }
@@ -12142,7 +12169,8 @@ function cmdStatePatch(cwd, patches, raw) {
12142
12169
  }
12143
12170
  if (results.updated.length > 0) node_fs.default.writeFileSync(statePath$3, content, "utf-8");
12144
12171
  output(results, raw, results.updated.length > 0 ? "true" : "false");
12145
- } catch {
12172
+ } catch (e) {
12173
+ rethrowCliSignals(e);
12146
12174
  error("STATE.md not found");
12147
12175
  }
12148
12176
  }
@@ -12161,7 +12189,8 @@ function cmdStateUpdate(cwd, field, value) {
12161
12189
  updated: false,
12162
12190
  reason: `Field "${field}" not found in STATE.md`
12163
12191
  });
12164
- } catch {
12192
+ } catch (e) {
12193
+ rethrowCliSignals(e);
12165
12194
  output({
12166
12195
  updated: false,
12167
12196
  reason: "STATE.md not found"
@@ -12555,6 +12584,7 @@ function cmdRoadmapGetPhase(cwd, phaseNum, raw) {
12555
12584
  section
12556
12585
  }, raw, section);
12557
12586
  } catch (e) {
12587
+ rethrowCliSignals(e);
12558
12588
  error("Failed to read ROADMAP.md: " + e.message);
12559
12589
  }
12560
12590
  }
@@ -13327,7 +13357,8 @@ function cmdVerifyPathExists(cwd, targetPath, raw) {
13327
13357
  exists: true,
13328
13358
  type: stats.isDirectory() ? "directory" : stats.isFile() ? "file" : "other"
13329
13359
  }, raw, "true");
13330
- } catch {
13360
+ } catch (e) {
13361
+ rethrowCliSignals(e);
13331
13362
  output({
13332
13363
  exists: false,
13333
13364
  type: null
@@ -13408,6 +13439,7 @@ function cmdHistoryDigest(cwd, raw) {
13408
13439
  };
13409
13440
  output(outputDigest, raw);
13410
13441
  } catch (e) {
13442
+ rethrowCliSignals(e);
13411
13443
  error("Failed to generate history digest: " + e.message);
13412
13444
  }
13413
13445
  }
@@ -13580,6 +13612,7 @@ async function cmdWebsearch(query, options, raw) {
13580
13612
  results
13581
13613
  }, raw, results.map((r) => `${r.title}\n${r.url}\n${r.description}`).join("\n\n"));
13582
13614
  } catch (err) {
13615
+ rethrowCliSignals(err);
13583
13616
  output({
13584
13617
  available: false,
13585
13618
  error: err.message
@@ -14597,6 +14630,7 @@ function cmdPhasesList(cwd, options, raw) {
14597
14630
  count: dirs.length
14598
14631
  }, raw, dirs.join("\n"));
14599
14632
  } catch (e) {
14633
+ rethrowCliSignals(e);
14600
14634
  error("Failed to list phases: " + e.message);
14601
14635
  }
14602
14636
  }
@@ -14637,6 +14671,7 @@ function cmdPhaseNextDecimal(cwd, basePhase, raw) {
14637
14671
  existing: existingDecimals
14638
14672
  }, raw, nextDecimal);
14639
14673
  } catch (e) {
14674
+ rethrowCliSignals(e);
14640
14675
  error("Failed to calculate next decimal phase: " + e.message);
14641
14676
  }
14642
14677
  }
@@ -14674,7 +14709,8 @@ function cmdFindPhase(cwd, phase, raw) {
14674
14709
  summaries
14675
14710
  };
14676
14711
  output(result, raw, result.directory);
14677
- } catch {
14712
+ } catch (e) {
14713
+ rethrowCliSignals(e);
14678
14714
  output(notFound, raw, "");
14679
14715
  }
14680
14716
  }
@@ -14756,6 +14792,7 @@ function cmdPhaseAdd(cwd, description, raw) {
14756
14792
  directory: result.directory
14757
14793
  }, raw, result.padded);
14758
14794
  } catch (e) {
14795
+ rethrowCliSignals(e);
14759
14796
  error(e.message);
14760
14797
  }
14761
14798
  }
@@ -14771,6 +14808,7 @@ function cmdPhaseInsert(cwd, afterPhase, description, raw) {
14771
14808
  directory: result.directory
14772
14809
  }, raw, result.phase_number);
14773
14810
  } catch (e) {
14811
+ rethrowCliSignals(e);
14774
14812
  error(e.message);
14775
14813
  }
14776
14814
  }
@@ -14954,6 +14992,7 @@ function cmdPhaseComplete(cwd, phaseNum, raw) {
14954
14992
  state_updated: result.state_updated
14955
14993
  }, raw);
14956
14994
  } catch (e) {
14995
+ rethrowCliSignals(e);
14957
14996
  error(e.message);
14958
14997
  }
14959
14998
  }
@@ -15179,6 +15218,155 @@ function cmdTemplateFill(cwd, templateType, options, raw) {
15179
15218
  }, raw, relPath);
15180
15219
  }
15181
15220
 
15221
+ //#endregion
15222
+ //#region src/core/dashboard-launcher.ts
15223
+ /**
15224
+ * Dashboard Launcher — Shared dashboard lifecycle utilities
15225
+ *
15226
+ * Used by both cli.ts (tool-router) and install.ts (npx entry point).
15227
+ */
15228
+ const DEFAULT_PORT = 3333;
15229
+ const PORT_RANGE_END = 3343;
15230
+ const HEALTH_TIMEOUT_MS = 1500;
15231
+ /**
15232
+ * Check if a dashboard health endpoint is responding on the given port.
15233
+ */
15234
+ async function checkHealth(port, timeoutMs = HEALTH_TIMEOUT_MS) {
15235
+ try {
15236
+ const controller = new AbortController();
15237
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
15238
+ const res = await fetch(`http://localhost:${port}/api/health`, { signal: controller.signal });
15239
+ clearTimeout(timer);
15240
+ if (res.ok) return (await res.json()).status === "ok";
15241
+ return false;
15242
+ } catch {
15243
+ return false;
15244
+ }
15245
+ }
15246
+ /**
15247
+ * Scan the port range for a running dashboard instance.
15248
+ * Returns the port number if found, null otherwise.
15249
+ */
15250
+ async function findRunningDashboard(timeoutMs = HEALTH_TIMEOUT_MS) {
15251
+ for (let port = DEFAULT_PORT; port <= PORT_RANGE_END; port++) if (await checkHealth(port, timeoutMs)) return port;
15252
+ return null;
15253
+ }
15254
+ /**
15255
+ * Kill processes listening on the given port. Cross-platform.
15256
+ */
15257
+ function killProcessOnPort(port) {
15258
+ if (process.platform === "win32") try {
15259
+ const lines = (0, node_child_process.execSync)(`netstat -ano | findstr :${port} | findstr LISTENING`, { encoding: "utf-8" }).trim().split("\n");
15260
+ const pids = /* @__PURE__ */ new Set();
15261
+ for (const line of lines) {
15262
+ const parts = line.trim().split(/\s+/);
15263
+ const pid = parts[parts.length - 1];
15264
+ if (pid && pid !== "0") pids.add(pid);
15265
+ }
15266
+ for (const pid of pids) try {
15267
+ (0, node_child_process.execSync)(`taskkill /PID ${pid} /F`, { stdio: "ignore" });
15268
+ } catch {}
15269
+ } catch {}
15270
+ else try {
15271
+ (0, node_child_process.execSync)(`lsof -i :${port} -t | xargs kill -SIGTERM 2>/dev/null`, { stdio: "ignore" });
15272
+ } catch {}
15273
+ }
15274
+ /**
15275
+ * Resolve the dashboard server entry point path.
15276
+ * Tries: local project install, global install, @maxsim/dashboard package, monorepo walk.
15277
+ */
15278
+ function resolveDashboardServer() {
15279
+ const localDashboard = node_path.default.join(process.cwd(), ".claude", "dashboard", "server.js");
15280
+ if (node_fs.default.existsSync(localDashboard)) return localDashboard;
15281
+ const globalDashboard = node_path.default.join(node_os.default.homedir(), ".claude", "dashboard", "server.js");
15282
+ if (node_fs.default.existsSync(globalDashboard)) return globalDashboard;
15283
+ try {
15284
+ const pkgPath = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href).resolve("@maxsim/dashboard/package.json");
15285
+ const pkgDir = node_path.default.dirname(pkgPath);
15286
+ const serverJs = node_path.default.join(pkgDir, "server.js");
15287
+ if (node_fs.default.existsSync(serverJs)) return serverJs;
15288
+ const serverTs = node_path.default.join(pkgDir, "server.ts");
15289
+ if (node_fs.default.existsSync(serverTs)) return serverTs;
15290
+ } catch {}
15291
+ try {
15292
+ let dir = node_path.default.dirname(new URL(require("url").pathToFileURL(__filename).href).pathname);
15293
+ if (process.platform === "win32" && dir.startsWith("/")) dir = dir.slice(1);
15294
+ for (let i = 0; i < 5; i++) {
15295
+ const candidate = node_path.default.join(dir, "packages", "dashboard", "server.ts");
15296
+ if (node_fs.default.existsSync(candidate)) return candidate;
15297
+ const candidateJs = node_path.default.join(dir, "packages", "dashboard", "server.js");
15298
+ if (node_fs.default.existsSync(candidateJs)) return candidateJs;
15299
+ dir = node_path.default.dirname(dir);
15300
+ }
15301
+ } catch {}
15302
+ return null;
15303
+ }
15304
+ /**
15305
+ * Ensure node-pty is installed in the dashboard directory.
15306
+ * Returns true if node-pty is available after this call.
15307
+ */
15308
+ function ensureNodePty(serverDir) {
15309
+ const ptyModulePath = node_path.default.join(serverDir, "node_modules", "node-pty");
15310
+ if (node_fs.default.existsSync(ptyModulePath)) return true;
15311
+ const dashPkgPath = node_path.default.join(serverDir, "package.json");
15312
+ if (!node_fs.default.existsSync(dashPkgPath)) node_fs.default.writeFileSync(dashPkgPath, "{\"private\":true}\n");
15313
+ try {
15314
+ (0, node_child_process.execSync)("npm install node-pty --save-optional --no-audit --no-fund --loglevel=error", {
15315
+ cwd: serverDir,
15316
+ stdio: "inherit",
15317
+ timeout: 12e4
15318
+ });
15319
+ return true;
15320
+ } catch {
15321
+ return false;
15322
+ }
15323
+ }
15324
+ /**
15325
+ * Read dashboard.json config from the parent directory of the dashboard dir.
15326
+ */
15327
+ function readDashboardConfig(serverPath) {
15328
+ const dashboardDir = node_path.default.dirname(serverPath);
15329
+ const dashboardConfigPath = node_path.default.join(node_path.default.dirname(dashboardDir), "dashboard.json");
15330
+ let projectCwd = process.cwd();
15331
+ let networkMode = false;
15332
+ if (node_fs.default.existsSync(dashboardConfigPath)) try {
15333
+ const config = JSON.parse(node_fs.default.readFileSync(dashboardConfigPath, "utf8"));
15334
+ if (config.projectCwd) projectCwd = config.projectCwd;
15335
+ networkMode = config.networkMode ?? false;
15336
+ } catch {}
15337
+ return {
15338
+ projectCwd,
15339
+ networkMode
15340
+ };
15341
+ }
15342
+ /**
15343
+ * Spawn the dashboard server as a detached background process.
15344
+ * Returns the child process PID, or null if spawn failed.
15345
+ */
15346
+ function spawnDashboard(options) {
15347
+ const { serverPath, projectCwd, networkMode = false, nodeEnv = "production" } = options;
15348
+ const serverDir = node_path.default.dirname(serverPath);
15349
+ const isTsFile = serverPath.endsWith(".ts");
15350
+ const child = (0, node_child_process.spawn)("node", isTsFile ? [
15351
+ "--import",
15352
+ "tsx",
15353
+ serverPath
15354
+ ] : [serverPath], {
15355
+ cwd: serverDir,
15356
+ detached: true,
15357
+ stdio: "ignore",
15358
+ env: {
15359
+ ...process.env,
15360
+ MAXSIM_PROJECT_CWD: projectCwd,
15361
+ MAXSIM_NETWORK_MODE: networkMode ? "1" : "0",
15362
+ NODE_ENV: isTsFile ? "development" : nodeEnv
15363
+ },
15364
+ ...process.platform === "win32" ? { shell: true } : {}
15365
+ });
15366
+ child.unref();
15367
+ return child.pid ?? null;
15368
+ }
15369
+
15182
15370
  //#endregion
15183
15371
  //#region src/core/init.ts
15184
15372
  /**
@@ -15258,7 +15446,6 @@ function cmdInitPlanPhase(cwd, phase, raw) {
15258
15446
  checker_model: resolveModelInternal(cwd, "maxsim-plan-checker"),
15259
15447
  research_enabled: config.research,
15260
15448
  plan_checker_enabled: config.plan_checker,
15261
- nyquist_validation_enabled: false,
15262
15449
  commit_docs: config.commit_docs,
15263
15450
  phase_found: !!phaseInfo,
15264
15451
  phase_dir: phaseInfo?.directory ?? null,
@@ -15946,30 +16133,42 @@ const COMMANDS = {
15946
16133
  }
15947
16134
  };
15948
16135
  async function main() {
15949
- const args = process.argv.slice(2);
15950
- let cwd = process.cwd();
15951
- const cwdEqArg = args.find((arg) => arg.startsWith("--cwd="));
15952
- const cwdIdx = args.indexOf("--cwd");
15953
- if (cwdEqArg) {
15954
- const value = cwdEqArg.slice(6).trim();
15955
- if (!value) error("Missing value for --cwd");
15956
- args.splice(args.indexOf(cwdEqArg), 1);
15957
- cwd = node_path.resolve(value);
15958
- } else if (cwdIdx !== -1) {
15959
- const value = args[cwdIdx + 1];
15960
- if (!value || value.startsWith("--")) error("Missing value for --cwd");
15961
- args.splice(cwdIdx, 2);
15962
- cwd = node_path.resolve(value);
15963
- }
15964
- if (!node_fs.existsSync(cwd) || !node_fs.statSync(cwd).isDirectory()) error(`Invalid --cwd: ${cwd}`);
15965
- const rawIndex = args.indexOf("--raw");
15966
- const raw = rawIndex !== -1;
15967
- if (rawIndex !== -1) args.splice(rawIndex, 1);
15968
- const command = args[0];
15969
- if (!command) error(`Usage: maxsim-tools <command> [args] [--raw] [--cwd <path>]\nCommands: ${Object.keys(COMMANDS).join(", ")}`);
15970
- const handler = COMMANDS[command];
15971
- if (!handler) error(`Unknown command: ${command}`);
15972
- await handler(args, cwd, raw);
16136
+ try {
16137
+ const args = process.argv.slice(2);
16138
+ let cwd = process.cwd();
16139
+ const cwdEqArg = args.find((arg) => arg.startsWith("--cwd="));
16140
+ const cwdIdx = args.indexOf("--cwd");
16141
+ if (cwdEqArg) {
16142
+ const value = cwdEqArg.slice(6).trim();
16143
+ if (!value) error("Missing value for --cwd");
16144
+ args.splice(args.indexOf(cwdEqArg), 1);
16145
+ cwd = node_path.resolve(value);
16146
+ } else if (cwdIdx !== -1) {
16147
+ const value = args[cwdIdx + 1];
16148
+ if (!value || value.startsWith("--")) error("Missing value for --cwd");
16149
+ args.splice(cwdIdx, 2);
16150
+ cwd = node_path.resolve(value);
16151
+ }
16152
+ if (!node_fs.existsSync(cwd) || !node_fs.statSync(cwd).isDirectory()) error(`Invalid --cwd: ${cwd}`);
16153
+ const rawIndex = args.indexOf("--raw");
16154
+ const raw = rawIndex !== -1;
16155
+ if (rawIndex !== -1) args.splice(rawIndex, 1);
16156
+ const command = args[0];
16157
+ if (!command) error(`Usage: maxsim-tools <command> [args] [--raw] [--cwd <path>]\nCommands: ${Object.keys(COMMANDS).join(", ")}`);
16158
+ const handler = COMMANDS[command];
16159
+ if (!handler) error(`Unknown command: ${command}`);
16160
+ await handler(args, cwd, raw);
16161
+ } catch (thrown) {
16162
+ if (thrown instanceof CliOutput) {
16163
+ writeOutput(thrown);
16164
+ process.exit(0);
16165
+ }
16166
+ if (thrown instanceof CliError) {
16167
+ process.stderr.write("Error: " + thrown.message + "\n");
16168
+ process.exit(1);
16169
+ }
16170
+ throw thrown;
16171
+ }
15973
16172
  }
15974
16173
  /**
15975
16174
  * Dashboard launch command.
@@ -15979,40 +16178,20 @@ async function main() {
15979
16178
  * Supports --stop to kill a running instance.
15980
16179
  */
15981
16180
  async function handleDashboard(args) {
15982
- const DEFAULT_PORT = 3333;
15983
- const PORT_RANGE_END = 3343;
15984
- const HEALTH_TIMEOUT_MS = 1500;
15985
16181
  const networkMode = args.includes("--network");
15986
16182
  if (args.includes("--stop")) {
15987
- for (let port = DEFAULT_PORT; port <= PORT_RANGE_END; port++) if (await checkHealth(port, HEALTH_TIMEOUT_MS)) {
15988
- console.log(`Dashboard found on port ${port} — sending shutdown...`);
15989
- console.log(`Dashboard at http://localhost:${port} is running.`);
15990
- console.log(`To stop it, close the browser tab or kill the process on port ${port}.`);
15991
- try {
15992
- if (process.platform === "win32") {
15993
- const lines = (0, node_child_process.execSync)(`netstat -ano | findstr :${port} | findstr LISTENING`, { encoding: "utf-8" }).trim().split("\n");
15994
- const pids = /* @__PURE__ */ new Set();
15995
- for (const line of lines) {
15996
- const parts = line.trim().split(/\s+/);
15997
- const pid = parts[parts.length - 1];
15998
- if (pid && pid !== "0") pids.add(pid);
15999
- }
16000
- for (const pid of pids) try {
16001
- (0, node_child_process.execSync)(`taskkill /PID ${pid} /F`, { stdio: "ignore" });
16002
- console.log(`Killed process ${pid}`);
16003
- } catch {}
16004
- } else (0, node_child_process.execSync)(`lsof -i :${port} -t | xargs kill -SIGTERM 2>/dev/null`, { stdio: "ignore" });
16005
- console.log("Dashboard stopped.");
16006
- } catch {
16007
- console.log("Could not automatically stop the dashboard. Kill the process manually.");
16008
- }
16183
+ for (let port = DEFAULT_PORT; port <= PORT_RANGE_END; port++) if (await checkHealth(port)) {
16184
+ console.log(`Dashboard found on port ${port} — stopping...`);
16185
+ killProcessOnPort(port);
16186
+ console.log("Dashboard stopped.");
16009
16187
  return;
16010
16188
  }
16011
16189
  console.log("No running dashboard found.");
16012
16190
  return;
16013
16191
  }
16014
- for (let port = DEFAULT_PORT; port <= PORT_RANGE_END; port++) if (await checkHealth(port, HEALTH_TIMEOUT_MS)) {
16015
- console.log(`Dashboard already running at http://localhost:${port}`);
16192
+ const runningPort = await findRunningDashboard();
16193
+ if (runningPort) {
16194
+ console.log(`Dashboard already running at http://localhost:${runningPort}`);
16016
16195
  return;
16017
16196
  }
16018
16197
  const serverPath = resolveDashboardServer();
@@ -16021,100 +16200,25 @@ async function handleDashboard(args) {
16021
16200
  console.error("Ensure @maxsim/dashboard is installed and built.");
16022
16201
  process.exit(1);
16023
16202
  }
16024
- const isTsFile = serverPath.endsWith(".ts");
16025
- const runner = "node";
16026
- const runnerArgs = isTsFile ? [
16027
- "--import",
16028
- "tsx",
16029
- serverPath
16030
- ] : [serverPath];
16031
16203
  const serverDir = node_path.dirname(serverPath);
16032
- let projectCwd = process.cwd();
16033
- const dashboardConfigPath = node_path.join(node_path.dirname(serverDir), "dashboard.json");
16034
- if (node_fs.existsSync(dashboardConfigPath)) try {
16035
- const config = JSON.parse(node_fs.readFileSync(dashboardConfigPath, "utf8"));
16036
- if (config.projectCwd) projectCwd = config.projectCwd;
16037
- } catch {}
16038
- const ptyModulePath = node_path.join(serverDir, "node_modules", "node-pty");
16039
- if (!node_fs.existsSync(ptyModulePath)) {
16040
- console.log("Installing node-pty for terminal support...");
16041
- try {
16042
- (0, node_child_process.execSync)("npm install node-pty --save-optional --no-audit --no-fund --loglevel=error", {
16043
- cwd: serverDir,
16044
- stdio: "inherit",
16045
- timeout: 12e4
16046
- });
16047
- } catch {
16048
- console.warn("node-pty installation failed — terminal will be unavailable.");
16049
- }
16050
- }
16204
+ const dashConfig = readDashboardConfig(serverPath);
16205
+ console.log("Installing node-pty for terminal support...");
16206
+ if (!ensureNodePty(serverDir)) console.warn("node-pty installation failed — terminal will be unavailable.");
16051
16207
  console.log("Dashboard starting...");
16052
- const child = (0, node_child_process.spawn)(runner, runnerArgs, {
16053
- cwd: serverDir,
16054
- detached: true,
16055
- stdio: "ignore",
16056
- env: {
16057
- ...process.env,
16058
- MAXSIM_PROJECT_CWD: projectCwd,
16059
- NODE_ENV: isTsFile ? "development" : "production",
16060
- ...networkMode ? { MAXSIM_NETWORK_MODE: "1" } : {}
16061
- },
16062
- ...process.platform === "win32" ? { shell: true } : {}
16208
+ const pid = spawnDashboard({
16209
+ serverPath,
16210
+ projectCwd: dashConfig.projectCwd,
16211
+ networkMode
16063
16212
  });
16064
- child.unref();
16065
16213
  await new Promise((resolve) => setTimeout(resolve, 3e3));
16066
- for (let port = DEFAULT_PORT; port <= PORT_RANGE_END; port++) if (await checkHealth(port, HEALTH_TIMEOUT_MS)) {
16067
- console.log(`Dashboard ready at http://localhost:${port}`);
16214
+ const readyPort = await findRunningDashboard();
16215
+ if (readyPort) {
16216
+ console.log(`Dashboard ready at http://localhost:${readyPort}`);
16068
16217
  return;
16069
16218
  }
16070
- console.log(`Dashboard spawned (PID ${child.pid}). It may take a moment to start.`);
16219
+ console.log(`Dashboard spawned (PID ${pid}). It may take a moment to start.`);
16071
16220
  console.log(`Check http://localhost:${DEFAULT_PORT} once ready.`);
16072
16221
  }
16073
- /**
16074
- * Check if a dashboard health endpoint is responding on the given port.
16075
- */
16076
- async function checkHealth(port, timeoutMs) {
16077
- try {
16078
- const controller = new AbortController();
16079
- const timer = setTimeout(() => controller.abort(), timeoutMs);
16080
- const res = await fetch(`http://localhost:${port}/api/health`, { signal: controller.signal });
16081
- clearTimeout(timer);
16082
- if (res.ok) return (await res.json()).status === "ok";
16083
- return false;
16084
- } catch {
16085
- return false;
16086
- }
16087
- }
16088
- /**
16089
- * Resolve the dashboard server entry point path.
16090
- * Tries: built server.js first, then source server.ts for dev mode.
16091
- */
16092
- function resolveDashboardServer() {
16093
- const localDashboard = node_path.join(process.cwd(), ".claude", "dashboard", "server.js");
16094
- if (node_fs.existsSync(localDashboard)) return localDashboard;
16095
- const globalDashboard = node_path.join(node_os.homedir(), ".claude", "dashboard", "server.js");
16096
- if (node_fs.existsSync(globalDashboard)) return globalDashboard;
16097
- try {
16098
- const pkgPath = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href).resolve("@maxsim/dashboard/package.json");
16099
- const pkgDir = node_path.dirname(pkgPath);
16100
- const serverJs = node_path.join(pkgDir, "server.js");
16101
- if (node_fs.existsSync(serverJs)) return serverJs;
16102
- const serverTs = node_path.join(pkgDir, "server.ts");
16103
- if (node_fs.existsSync(serverTs)) return serverTs;
16104
- } catch {}
16105
- try {
16106
- let dir = node_path.dirname(new URL(require("url").pathToFileURL(__filename).href).pathname);
16107
- if (process.platform === "win32" && dir.startsWith("/")) dir = dir.slice(1);
16108
- for (let i = 0; i < 5; i++) {
16109
- const candidate = node_path.join(dir, "packages", "dashboard", "server.ts");
16110
- if (node_fs.existsSync(candidate)) return candidate;
16111
- const candidateJs = node_path.join(dir, "packages", "dashboard", "server.js");
16112
- if (node_fs.existsSync(candidateJs)) return candidateJs;
16113
- dir = node_path.dirname(dir);
16114
- }
16115
- } catch {}
16116
- return null;
16117
- }
16118
16222
  main();
16119
16223
 
16120
16224
  //#endregion