@dmsdc-ai/aigentry-devkit 0.1.10 → 0.1.11

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.
@@ -1337,6 +1337,7 @@ function archiveState(state) {
1337
1337
 
1338
1338
  const TMUX_SESSION = "deliberation";
1339
1339
  const MONITOR_SCRIPT = path.join(HOME, ".local", "lib", "mcp-deliberation", "session-monitor.sh");
1340
+ const MONITOR_SCRIPT_WIN = path.join(HOME, ".local", "lib", "mcp-deliberation", "session-monitor-win.js");
1340
1341
 
1341
1342
  function tmuxWindowName(sessionId) {
1342
1343
  // tmux 윈도우 이름은 짧게 (마지막 부분 제거하고 20자)
@@ -1368,6 +1369,10 @@ function buildMonitorCommand(sessionId, project) {
1368
1369
  return `${shell} ${shellQuote(MONITOR_SCRIPT)} ${shellQuote(sessionId)} ${shellQuote(project)}`;
1369
1370
  }
1370
1371
 
1372
+ function buildMonitorCommandWindows(sessionId, project) {
1373
+ return `node "${MONITOR_SCRIPT_WIN}" "${sessionId}" "${project}"`;
1374
+ }
1375
+
1371
1376
  function hasTmuxSession(name) {
1372
1377
  try {
1373
1378
  execFileSync("tmux", ["has-session", "-t", name], { stdio: "ignore", windowsHide: true });
@@ -1499,28 +1504,40 @@ function openPhysicalTerminal(sessionId) {
1499
1504
  }
1500
1505
 
1501
1506
  if (process.platform === "win32") {
1502
- const attachForWindows = `tmux attach -t "${TMUX_SESSION}"`;
1503
- if ((commandExistsInPath("wt.exe") || commandExistsInPath("wt"))
1504
- && tryExecFile("wt", ["new-tab", "powershell", "-NoExit", "-Command", attachForWindows])) {
1505
- return { opened: true, windowIds: [] };
1507
+ // Windows: monitor is launched directly by spawnMonitorTerminal (no tmux)
1508
+ // Physical terminal opening is handled there, so just return success
1509
+ return { opened: true, windowIds: [] };
1510
+ }
1511
+
1512
+ return { opened: false, windowIds: [] };
1513
+ }
1514
+
1515
+ function spawnMonitorTerminal(sessionId) {
1516
+ // Windows: use Windows Terminal or PowerShell directly (no tmux needed)
1517
+ if (process.platform === "win32") {
1518
+ const project = getProjectSlug();
1519
+ const monitorCmd = buildMonitorCommandWindows(sessionId, project);
1520
+
1521
+ // Try Windows Terminal (wt.exe)
1522
+ if (commandExistsInPath("wt") || commandExistsInPath("wt.exe")) {
1523
+ if (tryExecFile("wt", ["new-tab", "--title", "Deliberation Monitor", "cmd", "/c", monitorCmd])) {
1524
+ return true;
1525
+ }
1506
1526
  }
1507
1527
 
1508
- const shell = ["powershell.exe", "powershell", "pwsh.exe", "pwsh"]
1509
- .find(cmd => commandExistsInPath(cmd));
1528
+ // Fallback: new PowerShell window
1529
+ const shell = ["pwsh.exe", "pwsh", "powershell.exe", "powershell"].find(c => commandExistsInPath(c));
1510
1530
  if (shell) {
1511
- const targetShell = shell.toLowerCase().startsWith("pwsh") ? "pwsh" : "powershell";
1512
- const escaped = attachForWindows.replace(/'/g, "''");
1513
- const script = `Start-Process ${targetShell} -ArgumentList '-NoExit','-Command','${escaped}'`;
1514
- if (tryExecFile(shell, ["-NoProfile", "-Command", script])) {
1515
- return { opened: true, windowIds: [] };
1531
+ const escaped = monitorCmd.replace(/'/g, "''");
1532
+ if (tryExecFile(shell, ["-NoProfile", "-Command", `Start-Process cmd -ArgumentList '/c','${escaped}'`])) {
1533
+ return true;
1516
1534
  }
1517
1535
  }
1518
- }
1519
1536
 
1520
- return { opened: false, windowIds: [] };
1521
- }
1537
+ return false;
1538
+ }
1522
1539
 
1523
- function spawnMonitorTerminal(sessionId) {
1540
+ // macOS/Linux: use tmux (existing logic)
1524
1541
  if (!commandExistsInPath("tmux")) {
1525
1542
  return false;
1526
1543
  }
@@ -1682,22 +1699,24 @@ function closePhysicalTerminal(windowId) {
1682
1699
  }
1683
1700
 
1684
1701
  function closeMonitorTerminal(sessionId, terminalWindowIds = []) {
1685
- const winName = tmuxWindowName(sessionId);
1686
- try {
1687
- execFileSync("tmux", ["kill-window", "-t", `${TMUX_SESSION}:${winName}`], {
1688
- stdio: "ignore",
1689
- windowsHide: true,
1690
- });
1691
- } catch { /* ignore */ }
1692
-
1693
- try {
1694
- if (tmuxWindowCount(TMUX_SESSION) === 0) {
1695
- execFileSync("tmux", ["kill-session", "-t", TMUX_SESSION], {
1702
+ if (process.platform !== "win32") {
1703
+ const winName = tmuxWindowName(sessionId);
1704
+ try {
1705
+ execFileSync("tmux", ["kill-window", "-t", `${TMUX_SESSION}:${winName}`], {
1696
1706
  stdio: "ignore",
1697
1707
  windowsHide: true,
1698
1708
  });
1699
- }
1700
- } catch { /* ignore */ }
1709
+ } catch { /* ignore */ }
1710
+
1711
+ try {
1712
+ if (tmuxWindowCount(TMUX_SESSION) === 0) {
1713
+ execFileSync("tmux", ["kill-session", "-t", TMUX_SESSION], {
1714
+ stdio: "ignore",
1715
+ windowsHide: true,
1716
+ });
1717
+ }
1718
+ } catch { /* ignore */ }
1719
+ }
1701
1720
 
1702
1721
  for (const windowId of terminalWindowIds) {
1703
1722
  closePhysicalTerminal(windowId);
@@ -1987,11 +2006,18 @@ server.tool(
1987
2006
  });
1988
2007
  state.monitor_terminal_window_ids = terminalWindowIds;
1989
2008
  }
2009
+ const isWin = process.platform === "win32";
1990
2010
  const terminalMsg = !tmuxOpened
1991
- ? `\n⚠️ tmux를 찾을 수 없어 모니터 터미널 미생성`
2011
+ ? isWin
2012
+ ? `\n⚠️ Windows Terminal을 찾을 수 없어 모니터 터미널 미생성`
2013
+ : `\n⚠️ tmux를 찾을 수 없어 모니터 터미널 미생성`
1992
2014
  : physicalOpened
1993
- ? `\n🖥️ 모니터 터미널 오픈됨: tmux attach -t ${TMUX_SESSION}`
1994
- : `\n⚠️ tmux 윈도우는 생성됐지만 외부 터미널 자동 오픈 실패. 수동 실행: tmux attach -t ${TMUX_SESSION}`;
2015
+ ? isWin
2016
+ ? `\n🖥️ 모니터 터미널 오픈됨 (Windows Terminal)`
2017
+ : `\n🖥️ 모니터 터미널 오픈됨: tmux attach -t ${TMUX_SESSION}`
2018
+ : isWin
2019
+ ? `\n⚠️ 모니터 터미널 자동 오픈 실패`
2020
+ : `\n⚠️ tmux 윈도우는 생성됐지만 외부 터미널 자동 오픈 실패. 수동 실행: tmux attach -t ${TMUX_SESSION}`;
1995
2021
  const manualNotDetected = hasManualSpeakers
1996
2022
  ? speakerOrder.filter(s => !candidateSnapshot.candidates.some(c => c.speaker === s))
1997
2023
  : [];
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+
4
+ // Usage: node session-monitor-win.js <sessionId> <project>
5
+ const fs = require("fs");
6
+ const path = require("path");
7
+
8
+ const sessionId = process.argv[2];
9
+ const project = process.argv[3] || "default";
10
+
11
+ if (!sessionId) {
12
+ console.error("Usage: node session-monitor-win.js <sessionId> <project>");
13
+ process.exit(1);
14
+ }
15
+
16
+ const HOME = process.env.HOME || process.env.USERPROFILE || "";
17
+ const stateDir = path.join(HOME, ".local", "state", "mcp-deliberation", project);
18
+ const stateFile = path.join(stateDir, `${sessionId}.json`);
19
+
20
+ const BOLD = "\x1b[1m";
21
+ const CYAN = "\x1b[36m";
22
+ const GREEN = "\x1b[32m";
23
+ const YELLOW = "\x1b[33m";
24
+ const DIM = "\x1b[2m";
25
+ const NC = "\x1b[0m";
26
+
27
+ let prevData = "";
28
+
29
+ function render() {
30
+ let raw;
31
+ try {
32
+ raw = fs.readFileSync(stateFile, "utf-8");
33
+ } catch {
34
+ return; // file not ready yet
35
+ }
36
+ if (raw === prevData) return;
37
+ prevData = raw;
38
+
39
+ let state;
40
+ try {
41
+ state = JSON.parse(raw);
42
+ } catch {
43
+ return;
44
+ }
45
+
46
+ console.clear();
47
+ console.log(`${BOLD}${CYAN}╔═══════════════════════════════════════╗${NC}`);
48
+ console.log(`${BOLD}${CYAN}║ Deliberation Monitor ║${NC}`);
49
+ console.log(`${BOLD}${CYAN}╚═══════════════════════════════════════╝${NC}`);
50
+ console.log();
51
+ console.log(`${BOLD}Topic:${NC} ${state.topic || "(none)"}`);
52
+ console.log(`${BOLD}Round:${NC} ${state.current_round || "?"}/${state.max_rounds || "?"}`);
53
+ console.log(`${BOLD}Speaker:${NC} ${YELLOW}${state.current_speaker || "(waiting)"}${NC}`);
54
+ console.log(`${BOLD}Speakers:${NC} ${(state.speakers || []).join(", ")}`);
55
+ console.log();
56
+ console.log(`${DIM}${"─".repeat(50)}${NC}`);
57
+ console.log();
58
+
59
+ const log = Array.isArray(state.log) ? state.log : [];
60
+ const recent = log.slice(-10);
61
+ for (const entry of recent) {
62
+ const speaker = entry.speaker || "unknown";
63
+ const content = String(entry.content || "").slice(0, 300);
64
+ const round = entry.round != null ? ` (R${entry.round})` : "";
65
+ console.log(`${GREEN}[${speaker}]${NC}${DIM}${round}${NC}`);
66
+ console.log(content);
67
+ console.log();
68
+ }
69
+
70
+ if (recent.length === 0) {
71
+ console.log(`${DIM}(No messages yet. Waiting for first turn...)${NC}`);
72
+ }
73
+
74
+ console.log(`${DIM}${"─".repeat(50)}${NC}`);
75
+ console.log(`${DIM}Auto-refresh every 2s | Session: ${sessionId} | Ctrl+C to close${NC}`);
76
+ }
77
+
78
+ // Initial render
79
+ render();
80
+
81
+ // Poll every 2 seconds
82
+ const interval = setInterval(render, 2000);
83
+
84
+ // Graceful shutdown
85
+ process.on("SIGINT", () => {
86
+ clearInterval(interval);
87
+ console.log(`\n${DIM}Monitor closed.${NC}`);
88
+ process.exit(0);
89
+ });
90
+
91
+ process.on("SIGTERM", () => {
92
+ clearInterval(interval);
93
+ process.exit(0);
94
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-devkit",
3
- "version": "0.1.10",
3
+ "version": "0.1.11",
4
4
  "description": "Cross-platform installer and tooling bundle for aigentry-devkit",
5
5
  "license": "MIT",
6
6
  "repository": {