muuuuse 4.0.0 → 5.0.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "muuuuse",
3
- "version": "4.0.0",
4
- "description": "🔌Muuuuse arms regular terminals in isolated pairs and can continue relay output into any other armed seat.",
3
+ "version": "5.0.0",
4
+ "description": "🔌Muuuuse arms terminals with flexible routing to any targets - pure message relay graph.",
5
5
  "type": "commonjs",
6
6
  "bin": {
7
7
  "muuuuse": "bin/muuse.js"
package/src/agents.js CHANGED
@@ -69,6 +69,44 @@ function detectAgent(processes) {
69
69
  return buildDetectedAgent("gemini", process);
70
70
  }
71
71
  }
72
+
73
+ // Fallback: if no agent process found, check for Claude Code running in a separate terminal
74
+ // (Claude Code's session updates even if it's not a child process of this shell)
75
+ if (processes.length > 0) {
76
+ // Use the bash/shell process as reference for timing
77
+ const shellProcess = processes[0];
78
+ const now = Date.now();
79
+ const recentThreshold = now - 2 * 60 * 1000; // Last 2 minutes
80
+
81
+ const CLAUDE_ROOT = path.join(os.homedir(), ".claude", "projects");
82
+ if (fs.existsSync(CLAUDE_ROOT)) {
83
+ try {
84
+ const sessionFiles = walkFiles(CLAUDE_ROOT, (f) => f.endsWith(".jsonl"))
85
+ .map(filePath => {
86
+ try {
87
+ const stat = fs.statSync(filePath);
88
+ return { filePath, mtimeMs: stat.mtimeMs };
89
+ } catch {
90
+ return null;
91
+ }
92
+ })
93
+ .filter(e => e && e.mtimeMs > recentThreshold)
94
+ .sort((a, b) => b.mtimeMs - a.mtimeMs)[0];
95
+
96
+ if (sessionFiles) {
97
+ return {
98
+ type: "claude",
99
+ pid: process.pid || shellProcess.pid,
100
+ args: "claude-code-terminal-session",
101
+ cwd: shellProcess.cwd || null,
102
+ elapsedSeconds: Math.round((Date.now() - shellProcess.startedAtMs) / 1000),
103
+ processStartedAtMs: Date.now() - (shellProcess.elapsedSeconds ?? 0) * 1000,
104
+ };
105
+ }
106
+ } catch {}
107
+ }
108
+ }
109
+
72
110
  return null;
73
111
  }
74
112
 
package/src/cli.js CHANGED
@@ -105,16 +105,7 @@ function renderSeatStatus(seat) {
105
105
  }
106
106
 
107
107
  function renderLinkTargets(seat) {
108
- const targets = [];
109
- if (seat.partnerSeatId) {
110
- targets.push({
111
- targetSeatId: seat.partnerSeatId,
112
- flowMode: seat.flowMode || "off",
113
- });
114
- }
115
- for (const target of Array.isArray(seat.continueTargets) ? seat.continueTargets : []) {
116
- targets.push(target);
117
- }
108
+ const targets = Array.isArray(seat.continueTargets) ? seat.continueTargets : [];
118
109
  if (targets.length === 0) {
119
110
  return "";
120
111
  }
@@ -165,9 +156,7 @@ function parseSeatOptions(command, args) {
165
156
  }
166
157
 
167
158
  function parseLinkTargets(args, seatId, defaultFlowMode) {
168
- const partnerSeatId = seatId ? getPartnerSeatId(seatId) : null;
169
- const continueTargets = [];
170
- let flowMode = defaultFlowMode;
159
+ const targets = [];
171
160
  let consumed = 0;
172
161
 
173
162
  while (consumed < args.length) {
@@ -181,19 +170,15 @@ function parseLinkTargets(args, seatId, defaultFlowMode) {
181
170
  break;
182
171
  }
183
172
 
184
- if (targetSeatId === partnerSeatId) {
185
- flowMode = targetFlowMode;
186
- } else {
187
- upsertTarget(continueTargets, {
188
- targetSeatId,
189
- flowMode: targetFlowMode,
190
- });
191
- }
173
+ upsertTarget(targets, {
174
+ targetSeatId,
175
+ flowMode: targetFlowMode,
176
+ });
192
177
 
193
178
  consumed += 3;
194
179
  }
195
180
 
196
- return { consumed, continueTargets, flowMode };
181
+ return { consumed, continueTargets: targets, flowMode: defaultFlowMode };
197
182
  }
198
183
 
199
184
  function parseFlowModeToken(flowToken, modeToken) {
package/src/runtime.js CHANGED
@@ -222,7 +222,12 @@ function findJoinableSessionName(currentPath = process.cwd(), seatId = 2) {
222
222
  const stopRequestedAtMs = Date.parse(stopRequest?.requestedAt || "");
223
223
  const createdAtMs = Date.parse(controller?.createdAt || anchorMeta?.startedAt || anchorStatus?.updatedAt || "");
224
224
 
225
- if (!anchorLive || seatLive) {
225
+ // Accept session if: controller exists AND (anchor is live OR anchor has written meta/status files).
226
+ // This handles the startup race: anchor might not have a live PID yet, but if it wrote files, it's initialized.
227
+ const controllerExists = controller !== null;
228
+ const anchorInitialized = anchorMeta !== null || anchorStatus !== null;
229
+ const anchorReady = anchorLive || anchorInitialized;
230
+ if (!controllerExists || !anchorReady || seatLive) {
226
231
  return null;
227
232
  }
228
233
 
@@ -1523,18 +1528,13 @@ class ArmedSeat {
1523
1528
  this.identity.privateKey
1524
1529
  );
1525
1530
 
1526
- // Only write to partner events if local flow mode accepts this phase.
1527
- // Commentary is gated here; continue targets filter independently.
1528
- if (shouldAcceptInboundEntry(this.flowMode, signedEntry)) {
1529
- appendJsonl(this.paths.eventsPath, signedEntry);
1530
- this.log(`[${this.seatId}] ${previewText(payload)}`);
1531
- }
1532
-
1533
- this.forwardContinuation(signedEntry);
1531
+ appendJsonl(this.paths.eventsPath, signedEntry);
1532
+ this.routeToTargets(signedEntry);
1534
1533
  this.rememberEmittedAnswer(answerKey);
1534
+ this.log(`[${this.seatId}] ${previewText(payload)}`);
1535
1535
  }
1536
1536
 
1537
- forwardContinuation(signedEntry) {
1537
+ routeToTargets(signedEntry) {
1538
1538
  if (this.continueTargets.length === 0) {
1539
1539
  return;
1540
1540
  }
@@ -1546,7 +1546,7 @@ class ArmedSeat {
1546
1546
 
1547
1547
  const target = this.findContinuationTarget(targetEntry.targetSeatId);
1548
1548
  if (!target) {
1549
- this.log(`[${this.seatId}] continue ${targetEntry.targetSeatId} unavailable`);
1549
+ this.log(`[${this.seatId}] target ${targetEntry.targetSeatId} unavailable`);
1550
1550
  continue;
1551
1551
  }
1552
1552
 
@@ -1795,6 +1795,13 @@ function stopAllSessions() {
1795
1795
  signalPid(seat.wrapperPid, "SIGTERM");
1796
1796
  }
1797
1797
  }
1798
+
1799
+ // Clean up session directory so stale data doesn't bleed into next session.
1800
+ try {
1801
+ fs.rmSync(sessionPaths.dir, { recursive: true, force: true });
1802
+ } catch {
1803
+ // Best-effort cleanup.
1804
+ }
1798
1805
  }
1799
1806
 
1800
1807
  return {