claude-tmux 1.0.1 → 1.0.2
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/index.js +8 -40
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
3
3
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
4
|
import { z } from "zod";
|
|
5
5
|
import { execSync } from "child_process";
|
|
6
|
-
import { writeFileSync
|
|
6
|
+
import { writeFileSync } from "fs";
|
|
7
7
|
const SESSION_PREFIX = "claude-";
|
|
8
8
|
function runTmux(args) {
|
|
9
9
|
return execSync(`tmux ${args}`, { encoding: "utf-8", timeout: 10000 }).trim();
|
|
@@ -22,7 +22,7 @@ function sessionName(name) {
|
|
|
22
22
|
function sleep(ms) {
|
|
23
23
|
return new Promise(resolve => setTimeout(resolve, ms));
|
|
24
24
|
}
|
|
25
|
-
function
|
|
25
|
+
function isBusy(output) {
|
|
26
26
|
// Claude shows these patterns when actively working
|
|
27
27
|
const busyPatterns = [
|
|
28
28
|
'ctrl+c to interrupt',
|
|
@@ -30,11 +30,10 @@ function isClaudeIdle(output) {
|
|
|
30
30
|
];
|
|
31
31
|
for (const pattern of busyPatterns) {
|
|
32
32
|
if (output.includes(pattern)) {
|
|
33
|
-
return
|
|
33
|
+
return true;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
return true;
|
|
36
|
+
return false;
|
|
38
37
|
}
|
|
39
38
|
function filterUIChrome(output) {
|
|
40
39
|
const lines = output.split('\n');
|
|
@@ -54,55 +53,24 @@ function filterUIChrome(output) {
|
|
|
54
53
|
});
|
|
55
54
|
return filtered.join('\n').trim();
|
|
56
55
|
}
|
|
57
|
-
function isClearlBusy(output) {
|
|
58
|
-
// These patterns definitively mean Claude is still working
|
|
59
|
-
const definitelyBusy = [
|
|
60
|
-
'ctrl+c to interrupt',
|
|
61
|
-
'esc to interrupt',
|
|
62
|
-
];
|
|
63
|
-
for (const pattern of definitelyBusy) {
|
|
64
|
-
if (output.includes(pattern)) {
|
|
65
|
-
return true;
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
56
|
async function waitForIdle(session) {
|
|
71
57
|
const timeout = 600000; // 10 minutes
|
|
72
58
|
const lines = 50;
|
|
73
59
|
const startTime = Date.now();
|
|
74
60
|
let lastOutput = "";
|
|
75
61
|
let stableCount = 0;
|
|
76
|
-
let iterations = 0;
|
|
77
62
|
while (Date.now() - startTime < timeout) {
|
|
78
63
|
await sleep(2000);
|
|
79
|
-
iterations++;
|
|
80
64
|
try {
|
|
81
65
|
const output = runTmux(`capture-pane -t "${session}" -p -S -${lines}`);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
appendFileSync('/tmp/claude-tmux-debug.log', `[${new Date().toISOString()}] iteration=${iterations} busy=${busy} outputLen=${output.length}\n`);
|
|
85
|
-
// If clearly busy, never return early
|
|
86
|
-
if (isClearlBusy(output)) {
|
|
66
|
+
// If busy, keep waiting
|
|
67
|
+
if (isBusy(output)) {
|
|
87
68
|
lastOutput = output;
|
|
88
69
|
stableCount = 0;
|
|
89
70
|
continue;
|
|
90
71
|
}
|
|
91
|
-
//
|
|
92
|
-
|
|
93
|
-
return filterUIChrome(output);
|
|
94
|
-
}
|
|
95
|
-
// Fallback: if output is stable for 6 seconds and not clearly busy, return
|
|
96
|
-
if (output === lastOutput) {
|
|
97
|
-
stableCount++;
|
|
98
|
-
if (stableCount >= 3) {
|
|
99
|
-
return filterUIChrome(output);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
stableCount = 0;
|
|
104
|
-
lastOutput = output;
|
|
105
|
-
}
|
|
72
|
+
// Not busy - return
|
|
73
|
+
return filterUIChrome(output);
|
|
106
74
|
}
|
|
107
75
|
catch (e) {
|
|
108
76
|
return `Error: ${e.message}`;
|