claude-tmux 1.0.1 → 1.0.3
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 +22 -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,15 @@ 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
|
-
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
function isDone(output) {
|
|
39
|
+
// Claude shows "✻ [verb] for [duration]" when task completes
|
|
40
|
+
// e.g., "✻ Baked for 1m 35s", "✻ Cogitated for 2m 10s"
|
|
41
|
+
return /✻\s+\w+\s+for\s+\d+[ms]/.test(output);
|
|
38
42
|
}
|
|
39
43
|
function filterUIChrome(output) {
|
|
40
44
|
const lines = output.split('\n');
|
|
@@ -54,55 +58,33 @@ function filterUIChrome(output) {
|
|
|
54
58
|
});
|
|
55
59
|
return filtered.join('\n').trim();
|
|
56
60
|
}
|
|
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
61
|
async function waitForIdle(session) {
|
|
71
62
|
const timeout = 600000; // 10 minutes
|
|
72
|
-
const lines =
|
|
63
|
+
const lines = 100; // Capture more lines to catch done signal
|
|
73
64
|
const startTime = Date.now();
|
|
74
65
|
let lastOutput = "";
|
|
75
|
-
let
|
|
76
|
-
let iterations = 0;
|
|
66
|
+
let idleCount = 0;
|
|
77
67
|
while (Date.now() - startTime < timeout) {
|
|
78
68
|
await sleep(2000);
|
|
79
|
-
iterations++;
|
|
80
69
|
try {
|
|
81
70
|
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)) {
|
|
71
|
+
// If busy, reset idle count and keep waiting
|
|
72
|
+
if (isBusy(output)) {
|
|
87
73
|
lastOutput = output;
|
|
88
|
-
|
|
74
|
+
idleCount = 0;
|
|
89
75
|
continue;
|
|
90
76
|
}
|
|
91
|
-
//
|
|
92
|
-
if (
|
|
77
|
+
// Check for positive done signal
|
|
78
|
+
if (isDone(output)) {
|
|
93
79
|
return filterUIChrome(output);
|
|
94
80
|
}
|
|
95
|
-
//
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
stableCount = 0;
|
|
104
|
-
lastOutput = output;
|
|
81
|
+
// Not busy but no done signal - use settling delay
|
|
82
|
+
// Require 2 consecutive idle checks (4 seconds) before returning
|
|
83
|
+
idleCount++;
|
|
84
|
+
if (idleCount >= 2) {
|
|
85
|
+
return filterUIChrome(output);
|
|
105
86
|
}
|
|
87
|
+
lastOutput = output;
|
|
106
88
|
}
|
|
107
89
|
catch (e) {
|
|
108
90
|
return `Error: ${e.message}`;
|