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.
- package/dist/assets/CHANGELOG.md +14 -0
- package/dist/assets/dashboard/server.js +80 -35
- package/package.json +1 -1
package/dist/assets/CHANGELOG.md
CHANGED
|
@@ -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:
|
|
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)
|
|
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 (
|
|
42010
|
-
|
|
42011
|
-
|
|
42012
|
-
|
|
42013
|
-
|
|
42014
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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())
|
|
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())
|
|
42167
|
-
|
|
42168
|
-
|
|
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
|
-
|
|
42182
|
-
|
|
42183
|
-
|
|
42184
|
-
|
|
42185
|
-
|
|
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
|
-
|
|
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