maxsimcli 2.5.2 → 2.5.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.
@@ -1,3 +1,17 @@
1
+ ## [2.5.4](https://github.com/maystudios/maxsim/compare/v2.5.3...v2.5.4) (2026-02-25)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **dashboard:** cross-platform compatibility fixes ([9101d90](https://github.com/maystudios/maxsim/commit/9101d90de233ea563227d9477721ac1fb1f39115))
7
+
8
+ ## [2.5.3](https://github.com/maystudios/maxsim/compare/v2.5.2...v2.5.3) (2026-02-25)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **dashboard:** make state section append robust and add server logging ([dd19326](https://github.com/maystudios/maxsim/commit/dd193265ac694c1fe522a4b20da0e55245ca0b73))
14
+
1
15
  ## [2.5.2](https://github.com/maystudios/maxsim/compare/v2.5.1...v2.5.2) (2026-02-25)
2
16
 
3
17
 
@@ -41425,12 +41425,20 @@ var SessionStore = class {
41425
41425
  //#endregion
41426
41426
  //#region src/terminal/pty-manager.ts
41427
41427
  let pty = null;
41428
+ let ptyLoadError = null;
41428
41429
  try {
41429
41430
  pty = require("node-pty");
41430
- } catch {}
41431
+ } catch (err) {
41432
+ ptyLoadError = err instanceof Error ? err.message : String(err);
41433
+ }
41431
41434
  const DISCONNECT_TIMEOUT_MS = 6e4;
41432
41435
  const STATUS_INTERVAL_MS = 1e3;
41433
41436
  const ACTIVE_THRESHOLD_MS = 2e3;
41437
+ function ptyLog(level, ...args) {
41438
+ const ts = (/* @__PURE__ */ new Date()).toISOString();
41439
+ const msg = args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ");
41440
+ console.error(`[${ts}] [${level}] [pty-manager] ${msg}`);
41441
+ }
41434
41442
  var PtyManager = class PtyManager {
41435
41443
  static instance = null;
41436
41444
  session = null;
@@ -41443,16 +41451,21 @@ var PtyManager = class PtyManager {
41443
41451
  }
41444
41452
  spawn(opts) {
41445
41453
  if (!pty) {
41454
+ ptyLog("ERROR", `node-pty not available: ${ptyLoadError}`);
41446
41455
  this.broadcastToClients({
41447
41456
  type: "output",
41448
- data: "\r\n\x1B[31mTerminal unavailable: node-pty is not installed.\r\nRun: npm install node-pty\x1B[0m\r\n"
41457
+ data: `\r\n\x1b[31mTerminal unavailable: node-pty is not installed.\r\nError: ${ptyLoadError}\r\nRun: npm install node-pty\x1b[0m\r\n`
41449
41458
  });
41450
41459
  return;
41451
41460
  }
41452
- if (this.session) this.kill();
41461
+ if (this.session) {
41462
+ ptyLog("INFO", "Killing existing session before spawn");
41463
+ this.kill();
41464
+ }
41453
41465
  const shell = process.platform === "win32" ? "claude.cmd" : "claude";
41454
41466
  const args = [];
41455
41467
  if (opts.skipPermissions) args.push("--dangerously-skip-permissions");
41468
+ ptyLog("INFO", `Spawning: shell=${shell}, args=${JSON.stringify(args)}, cwd=${opts.cwd}, cols=${opts.cols ?? 120}, rows=${opts.rows ?? 30}`);
41456
41469
  const proc = pty.spawn(shell, args, {
41457
41470
  name: "xterm-256color",
41458
41471
  cols: opts.cols ?? 120,
@@ -41460,6 +41473,7 @@ var PtyManager = class PtyManager {
41460
41473
  cwd: opts.cwd,
41461
41474
  env: process.env
41462
41475
  });
41476
+ ptyLog("INFO", `Process spawned with pid=${proc.pid}`);
41463
41477
  const store = new SessionStore();
41464
41478
  this.session = {
41465
41479
  process: proc,
@@ -41480,6 +41494,7 @@ var PtyManager = class PtyManager {
41480
41494
  });
41481
41495
  });
41482
41496
  proc.onExit(({ exitCode }) => {
41497
+ ptyLog("INFO", `Process exited with code=${exitCode}`);
41483
41498
  this.broadcastToClients({
41484
41499
  type: "exit",
41485
41500
  code: exitCode
@@ -41577,7 +41592,17 @@ var PtyManager = class PtyManager {
41577
41592
 
41578
41593
  //#endregion
41579
41594
  //#region src/server.ts
41595
+ const logDir = node_path.join(__dirname, "logs");
41596
+ node_fs.mkdirSync(logDir, { recursive: true });
41597
+ const logFile = node_path.join(logDir, `dashboard-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.log`);
41598
+ const logStream = node_fs.createWriteStream(logFile, { flags: "a" });
41599
+ function log(level, tag, ...args) {
41600
+ const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [${level}] [${tag}] ${args.map((a) => typeof a === "string" ? a : JSON.stringify(a)).join(" ")}\n`;
41601
+ logStream.write(line);
41602
+ if (level === "ERROR") console.error(`[${tag}]`, ...args);
41603
+ }
41580
41604
  const projectCwd = process.env.MAXSIM_PROJECT_CWD || process.cwd();
41605
+ log("INFO", "server", `Starting dashboard server, projectCwd=${projectCwd}`);
41581
41606
  const clientDir = node_path.join(__dirname, "client");
41582
41607
  function isWithinPlanning(cwd, targetPath) {
41583
41608
  const planningDir = node_path.resolve(cwd, ".planning");
@@ -41677,7 +41702,7 @@ function setupWatcher(cwd, wss) {
41677
41702
  function parseRoadmap(cwd) {
41678
41703
  const roadmapPath = node_path.join(cwd, ".planning", "ROADMAP.md");
41679
41704
  if (!node_fs.existsSync(roadmapPath)) return null;
41680
- const content = node_fs.readFileSync(roadmapPath, "utf-8");
41705
+ const content = node_fs.readFileSync(roadmapPath, "utf-8").replace(/\r\n/g, "\n");
41681
41706
  const phasesDir = node_path.join(cwd, ".planning", "phases");
41682
41707
  const phasePattern = (0, import_dist.getPhasePattern)();
41683
41708
  const phases = [];
@@ -41759,7 +41784,7 @@ function parseRoadmap(cwd) {
41759
41784
  function parseState(cwd) {
41760
41785
  const statePath = node_path.join(cwd, ".planning", "STATE.md");
41761
41786
  if (!node_fs.existsSync(statePath)) return null;
41762
- const content = node_fs.readFileSync(statePath, "utf-8");
41787
+ const content = node_fs.readFileSync(statePath, "utf-8").replace(/\r\n/g, "\n");
41763
41788
  const position = (0, import_dist.stateExtractField)(content, "Current Position") || (0, import_dist.stateExtractField)(content, "Phase");
41764
41789
  const lastActivity = (0, import_dist.stateExtractField)(content, "Last activity") || (0, import_dist.stateExtractField)(content, "Last Activity");
41765
41790
  const currentPhase = (0, import_dist.stateExtractField)(content, "Current Phase") || (0, import_dist.stateExtractField)(content, "Phase");
@@ -41840,7 +41865,7 @@ function parsePhaseDetail(cwd, phaseId) {
41840
41865
  const plans = [];
41841
41866
  for (const planFileName of planFileNames) {
41842
41867
  const planPath = node_path.join(phaseDir, planFileName);
41843
- const content = node_fs.readFileSync(planPath, "utf-8");
41868
+ const content = node_fs.readFileSync(planPath, "utf-8").replace(/\r\n/g, "\n");
41844
41869
  const frontmatter = (0, import_dist.extractFrontmatter)(content);
41845
41870
  const tasks = [];
41846
41871
  const taskRegex = /<task\s+type="([^"]*)"[^>]*>\s*<name>([^<]+)<\/name>([\s\S]*?)<\/task>/g;
@@ -41946,7 +41971,7 @@ app.patch("/api/roadmap", (req, res) => {
41946
41971
  if (!node_fs.existsSync(roadmapPath)) return res.status(404).json({ error: "ROADMAP.md not found" });
41947
41972
  const { phaseNumber, checked } = req.body;
41948
41973
  if (!phaseNumber || checked === void 0) return res.status(400).json({ error: "phaseNumber and checked are required" });
41949
- let content = node_fs.readFileSync(roadmapPath, "utf-8");
41974
+ let content = node_fs.readFileSync(roadmapPath, "utf-8").replace(/\r\n/g, "\n");
41950
41975
  const escapedNum = phaseNumber.replace(".", "\\.");
41951
41976
  const pattern = new RegExp(`(-\\s*\\[)(x| )(\\]\\s*.*Phase\\s+${escapedNum})`, "i");
41952
41977
  if (!content.match(pattern)) return res.status(404).json({ error: `Phase ${phaseNumber} checkbox not found in ROADMAP.md` });
@@ -41970,7 +41995,7 @@ app.patch("/api/state", (req, res) => {
41970
41995
  if (!isWithinPlanning(projectCwd, ".planning/STATE.md")) return res.status(400).json({ error: "Invalid path" });
41971
41996
  const { field, value } = req.body;
41972
41997
  if (!field || value === void 0) return res.status(400).json({ error: "field and value are required" });
41973
- const updated = (0, import_dist.stateReplaceField)(node_fs.readFileSync(statePath, "utf-8"), field, value);
41998
+ const updated = (0, import_dist.stateReplaceField)(node_fs.readFileSync(statePath, "utf-8").replace(/\r\n/g, "\n"), field, value);
41974
41999
  if (!updated) return res.status(404).json({ error: `Field "${field}" not found in STATE.md` });
41975
42000
  suppressPath(statePath);
41976
42001
  node_fs.writeFileSync(statePath, updated, "utf-8");
@@ -42003,17 +42028,15 @@ None yet.
42003
42028
  `;
42004
42029
  node_fs.writeFileSync(statePath, template, "utf-8");
42005
42030
  }
42006
- function appendToStateSection(statePath, sectionPattern, entry) {
42007
- let content = node_fs.readFileSync(statePath, "utf-8");
42031
+ function appendToStateSection(statePath, sectionPattern, entry, fallbackSection) {
42032
+ let content = node_fs.readFileSync(statePath, "utf-8").replace(/\r\n/g, "\n");
42008
42033
  const match = content.match(sectionPattern);
42009
- if (!match) return {
42010
- success: false,
42011
- reason: "Section not found in STATE.md"
42012
- };
42013
- let sectionBody = match[2];
42014
- sectionBody = sectionBody.replace(/None yet\.?\s*\n?/gi, "").replace(/No decisions yet\.?\s*\n?/gi, "").replace(/None\.?\s*\n?/gi, "");
42015
- sectionBody = sectionBody.trimEnd() + "\n" + entry + "\n";
42016
- content = content.replace(sectionPattern, (_m, header) => `${header}${sectionBody}`);
42034
+ if (match) {
42035
+ let sectionBody = match[2];
42036
+ sectionBody = sectionBody.replace(/None yet\.?\s*\n?/gi, "").replace(/No decisions yet\.?\s*\n?/gi, "").replace(/None\.?\s*\n?/gi, "");
42037
+ sectionBody = sectionBody.trimEnd() + "\n" + entry + "\n";
42038
+ content = content.replace(sectionPattern, (_m, header) => `${header}${sectionBody}`);
42039
+ } else content = content.trimEnd() + "\n\n" + fallbackSection + "\n" + entry + "\n";
42017
42040
  suppressPath(statePath);
42018
42041
  node_fs.writeFileSync(statePath, content, "utf-8");
42019
42042
  return { success: true };
@@ -42024,8 +42047,7 @@ app.post("/api/state/decision", (req, res) => {
42024
42047
  const { phase, text } = req.body;
42025
42048
  if (!text?.trim()) return res.status(400).json({ error: "text is required" });
42026
42049
  const entry = `- [Phase ${phase?.trim() || "?"}]: ${text.trim()}`;
42027
- const result = appendToStateSection(statePath, /(###?\s*(?:Decisions|Decisions Made|Accumulated.*Decisions)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i, entry);
42028
- if (!result.success) return res.status(404).json({ error: result.reason });
42050
+ appendToStateSection(statePath, /(###?\s*(?:Decisions|Decisions Made|Accumulated.*Decisions)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i, entry, "### Decisions");
42029
42051
  return res.json({
42030
42052
  added: true,
42031
42053
  decision: entry
@@ -42036,8 +42058,7 @@ app.post("/api/state/blocker", (req, res) => {
42036
42058
  ensureStateMd(statePath);
42037
42059
  const { text } = req.body;
42038
42060
  if (!text?.trim()) return res.status(400).json({ error: "text is required" });
42039
- const result = appendToStateSection(statePath, /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i, `- ${text.trim()}`);
42040
- if (!result.success) return res.status(404).json({ error: result.reason });
42061
+ appendToStateSection(statePath, /(###?\s*(?:Blockers|Blockers\/Concerns|Concerns)\s*\n)([\s\S]*?)(?=\n###?|\n##[^#]|$)/i, `- ${text.trim()}`, "### Blockers/Concerns");
42041
42062
  return res.json({
42042
42063
  added: true,
42043
42064
  blocker: text.trim()
@@ -42160,13 +42181,18 @@ async function main() {
42160
42181
  const wss = createWSS();
42161
42182
  const terminalWss = new import_websocket_server.default({ noServer: true });
42162
42183
  const ptyManager = PtyManager.getInstance();
42163
- if (!ptyManager.isAvailable()) console.error("[server] node-pty not available — terminal features disabled");
42184
+ if (!ptyManager.isAvailable()) log("WARN", "server", "node-pty not available — terminal features disabled");
42185
+ else log("INFO", "server", "node-pty available — terminal features enabled");
42164
42186
  terminalWss.on("connection", (ws) => {
42187
+ log("INFO", "terminal-ws", "Client connected");
42165
42188
  ptyManager.addClient(ws);
42166
- if (!ptyManager.isAvailable()) ws.send(JSON.stringify({
42167
- type: "unavailable",
42168
- reason: "node-pty is not installed — terminal features disabled"
42169
- }));
42189
+ if (!ptyManager.isAvailable()) {
42190
+ ws.send(JSON.stringify({
42191
+ type: "unavailable",
42192
+ reason: "node-pty is not installed — terminal features disabled"
42193
+ }));
42194
+ log("WARN", "terminal-ws", "Sent unavailable to client — node-pty missing");
42195
+ }
42170
42196
  ws.on("message", (raw) => {
42171
42197
  try {
42172
42198
  const msg = JSON.parse(typeof raw === "string" ? raw : raw.toString());
@@ -42175,27 +42201,44 @@ async function main() {
42175
42201
  ptyManager.write(msg.data);
42176
42202
  break;
42177
42203
  case "resize":
42204
+ log("INFO", "terminal-ws", `Resize: ${msg.cols}x${msg.rows}`);
42178
42205
  ptyManager.resize(msg.cols, msg.rows);
42179
42206
  break;
42180
42207
  case "spawn":
42181
- ptyManager.spawn({
42182
- skipPermissions: !!msg.skipPermissions,
42183
- cwd: projectCwd,
42184
- cols: msg.cols,
42185
- rows: msg.rows
42186
- });
42208
+ log("INFO", "terminal-ws", `Spawn requested: skipPermissions=${!!msg.skipPermissions}, cwd=${projectCwd}`);
42209
+ try {
42210
+ ptyManager.spawn({
42211
+ skipPermissions: !!msg.skipPermissions,
42212
+ cwd: projectCwd,
42213
+ cols: msg.cols,
42214
+ rows: msg.rows
42215
+ });
42216
+ log("INFO", "terminal-ws", `Spawn succeeded, pid=${ptyManager.getStatus()?.pid}`);
42217
+ } catch (err) {
42218
+ const errMsg = err instanceof Error ? err.message : String(err);
42219
+ log("ERROR", "terminal-ws", `Spawn failed: ${errMsg}`);
42220
+ ws.send(JSON.stringify({
42221
+ type: "output",
42222
+ data: `\r\n\x1b[31mFailed to start terminal: ${errMsg}\x1b[0m\r\n`
42223
+ }));
42224
+ }
42187
42225
  break;
42188
42226
  case "kill":
42227
+ log("INFO", "terminal-ws", "Kill requested");
42189
42228
  ptyManager.kill();
42190
42229
  break;
42230
+ default: log("WARN", "terminal-ws", `Unknown message type: ${msg.type}`);
42191
42231
  }
42192
- } catch {}
42232
+ } catch (err) {
42233
+ log("ERROR", "terminal-ws", `Message handling error: ${err instanceof Error ? err.message : String(err)}`);
42234
+ }
42193
42235
  });
42194
42236
  ws.on("close", () => {
42237
+ log("INFO", "terminal-ws", "Client disconnected");
42195
42238
  ptyManager.removeClient(ws);
42196
42239
  });
42197
42240
  ws.on("error", (err) => {
42198
- console.error("[terminal-ws] Client error:", err.message);
42241
+ log("ERROR", "terminal-ws", `Client error: ${err.message}`);
42199
42242
  });
42200
42243
  });
42201
42244
  const server = (0, node_http.createServer)(app);
@@ -42218,7 +42261,9 @@ async function main() {
42218
42261
  const port = await esm_default(3333);
42219
42262
  const url = `http://localhost:${port}`;
42220
42263
  server.listen(port, () => {
42264
+ log("INFO", "server", `Dashboard ready at ${url}, log file: ${logFile}`);
42221
42265
  console.error(`Dashboard ready at ${url}`);
42266
+ console.error(`Logs: ${logFile}`);
42222
42267
  open$1(url).catch(() => {});
42223
42268
  });
42224
42269
  function shutdown() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "maxsimcli",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "private": false,
5
5
  "description": "A meta-prompting, context engineering and spec-driven development system for Claude Code, OpenCode, Gemini and Codex by MayStudios.",
6
6
  "bin": {