@hasna/conversations 0.2.30 → 0.2.31
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/bin/index.js +42 -27
- package/bin/mcp.js +33 -17
- package/package.json +2 -2
package/bin/index.js
CHANGED
|
@@ -14928,7 +14928,7 @@ var init_presence = __esm(() => {
|
|
|
14928
14928
|
var require_package = __commonJS((exports, module) => {
|
|
14929
14929
|
module.exports = {
|
|
14930
14930
|
name: "@hasna/conversations",
|
|
14931
|
-
version: "0.2.
|
|
14931
|
+
version: "0.2.31",
|
|
14932
14932
|
description: "Real-time CLI messaging for AI agents",
|
|
14933
14933
|
type: "module",
|
|
14934
14934
|
bin: {
|
|
@@ -15715,8 +15715,23 @@ import { execSync } from "child_process";
|
|
|
15715
15715
|
function sleep(ms) {
|
|
15716
15716
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
15717
15717
|
}
|
|
15718
|
+
function countLines(message) {
|
|
15719
|
+
const matches = message.match(/\r?\n/g);
|
|
15720
|
+
return (matches?.length ?? 0) + 1;
|
|
15721
|
+
}
|
|
15722
|
+
function getDefaultDelayMs(message) {
|
|
15723
|
+
const byLength = message.length * 1.5;
|
|
15724
|
+
const byLines = countLines(message) * 10;
|
|
15725
|
+
return Math.max(25, Math.min(1500, Math.round(byLength + byLines)));
|
|
15726
|
+
}
|
|
15727
|
+
function getVerifyPauseMs(message) {
|
|
15728
|
+
return message.length <= 120 ? 50 : 100;
|
|
15729
|
+
}
|
|
15730
|
+
function getRetryBackoffMs(attempt) {
|
|
15731
|
+
return Math.min(500, 100 * attempt);
|
|
15732
|
+
}
|
|
15718
15733
|
async function tmuxSend(target, message, opts = {}) {
|
|
15719
|
-
const delay = opts.delayMs ??
|
|
15734
|
+
const delay = opts.delayMs ?? getDefaultDelayMs(message);
|
|
15720
15735
|
const maxRetries = opts.retries ?? 3;
|
|
15721
15736
|
const verify = opts.verify !== false;
|
|
15722
15737
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
@@ -15725,22 +15740,23 @@ async function tmuxSend(target, message, opts = {}) {
|
|
|
15725
15740
|
execSync(`tmux send-keys -t ${JSON.stringify(target)} Enter`);
|
|
15726
15741
|
if (!verify)
|
|
15727
15742
|
return { success: true, attempts: attempt };
|
|
15728
|
-
await sleep(
|
|
15743
|
+
await sleep(getVerifyPauseMs(message));
|
|
15729
15744
|
const pane = execSync(`tmux capture-pane -t ${JSON.stringify(target)} -p`).toString();
|
|
15730
15745
|
const lastLines = pane.split(`
|
|
15731
|
-
`).slice(-
|
|
15746
|
+
`).slice(-6).join(`
|
|
15732
15747
|
`);
|
|
15733
|
-
|
|
15748
|
+
const marker = message.slice(0, Math.min(32, message.length));
|
|
15749
|
+
if (!lastLines.includes(marker)) {
|
|
15734
15750
|
return { success: true, attempts: attempt };
|
|
15735
15751
|
}
|
|
15736
15752
|
if (attempt < maxRetries)
|
|
15737
|
-
await sleep(
|
|
15753
|
+
await sleep(getRetryBackoffMs(attempt));
|
|
15738
15754
|
}
|
|
15739
15755
|
return { success: false, attempts: maxRetries };
|
|
15740
15756
|
}
|
|
15741
15757
|
function registerTmuxCommands(program2) {
|
|
15742
15758
|
const tmux = program2.command("tmux").description("Dispatch messages to tmux windows (Claude Code sessions)");
|
|
15743
|
-
tmux.command("send").description("Send a message to a tmux window with paste+wait+Enter+verify").requiredOption("--target <target>", "Tmux target: session:window or session:window.pane").requiredOption("--message <text>", "Message text to send").option("--delay <ms>", "Wait time (ms) after paste before hitting Enter (default:
|
|
15759
|
+
tmux.command("send").description("Send a message to a tmux window with paste+wait+Enter+verify").requiredOption("--target <target>", "Tmux target: session:window or session:window.pane").requiredOption("--message <text>", "Message text to send").option("--delay <ms>", "Wait time (ms) after paste before hitting Enter (default: adaptive 25-1500ms)", parseInt).option("--retries <n>", "Max retry attempts (default: 3)", parseInt).option("--no-verify", "Skip verification after sending").option("--json", "Output result as JSON").action(async (opts) => {
|
|
15744
15760
|
const target = opts.target.trim();
|
|
15745
15761
|
const message = opts.message;
|
|
15746
15762
|
if (!target) {
|
|
@@ -15775,10 +15791,10 @@ function registerTmuxCommands(program2) {
|
|
|
15775
15791
|
process.exit(1);
|
|
15776
15792
|
}
|
|
15777
15793
|
});
|
|
15778
|
-
tmux.command("broadcast").description("Send the same message to multiple tmux windows").requiredOption("--targets <list>", "Comma-separated list of tmux targets").requiredOption("--message <text>", "Message text to send").option("--delay <ms>", "Wait time (ms) after paste before Enter (default:
|
|
15794
|
+
tmux.command("broadcast").description("Send the same message to multiple tmux windows").requiredOption("--targets <list>", "Comma-separated list of tmux targets").requiredOption("--message <text>", "Message text to send").option("--delay <ms>", "Wait time (ms) after paste before Enter (default: adaptive 25-1500ms)", parseInt).option("--stagger <ms>", "Delay (ms) between each target (default: 500)", parseInt).option("--retries <n>", "Max retry attempts per target (default: 3)", parseInt).option("--no-verify", "Skip verification after sending").option("--json", "Output results as JSON").action(async (opts) => {
|
|
15779
15795
|
const targets = opts.targets.split(",").map((t) => t.trim()).filter(Boolean);
|
|
15780
15796
|
const message = opts.message;
|
|
15781
|
-
const stagger = Number.isFinite(opts.stagger) && opts.stagger
|
|
15797
|
+
const stagger = Number.isFinite(opts.stagger) && opts.stagger >= 0 ? opts.stagger : 500;
|
|
15782
15798
|
if (targets.length === 0) {
|
|
15783
15799
|
console.error(chalk9.red("--targets must be a non-empty comma-separated list."));
|
|
15784
15800
|
process.exit(1);
|
|
@@ -15787,18 +15803,17 @@ function registerTmuxCommands(program2) {
|
|
|
15787
15803
|
console.error(chalk9.red("--message cannot be empty."));
|
|
15788
15804
|
process.exit(1);
|
|
15789
15805
|
}
|
|
15790
|
-
const results =
|
|
15791
|
-
|
|
15792
|
-
const target = targets[i];
|
|
15806
|
+
const results = new Array(targets.length);
|
|
15807
|
+
await Promise.all(targets.map(async (target, i) => {
|
|
15793
15808
|
if (i > 0 && stagger > 0)
|
|
15794
|
-
await sleep(stagger);
|
|
15809
|
+
await sleep(stagger * i);
|
|
15795
15810
|
try {
|
|
15796
15811
|
const result = await tmuxSend(target, message, {
|
|
15797
15812
|
delayMs: Number.isFinite(opts.delay) ? opts.delay : undefined,
|
|
15798
15813
|
retries: Number.isFinite(opts.retries) ? opts.retries : undefined,
|
|
15799
15814
|
verify: opts.verify !== false
|
|
15800
15815
|
});
|
|
15801
|
-
results
|
|
15816
|
+
results[i] = { target, ...result };
|
|
15802
15817
|
if (!opts.json) {
|
|
15803
15818
|
if (result.success) {
|
|
15804
15819
|
console.log(chalk9.green(` \u2713 ${target}`) + chalk9.dim(` (attempt ${result.attempts})`));
|
|
@@ -15808,12 +15823,12 @@ function registerTmuxCommands(program2) {
|
|
|
15808
15823
|
}
|
|
15809
15824
|
} catch (err) {
|
|
15810
15825
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
15811
|
-
results
|
|
15826
|
+
results[i] = { target, success: false, attempts: 0, error: errMsg };
|
|
15812
15827
|
if (!opts.json) {
|
|
15813
15828
|
console.log(chalk9.red(` \u2717 ${target}: ${errMsg}`));
|
|
15814
15829
|
}
|
|
15815
15830
|
}
|
|
15816
|
-
}
|
|
15831
|
+
}));
|
|
15817
15832
|
const succeeded = results.filter((r) => r.success).length;
|
|
15818
15833
|
const failed = results.length - succeeded;
|
|
15819
15834
|
if (opts.json) {
|
|
@@ -46960,7 +46975,7 @@ function registerTmuxTools(server) {
|
|
|
46960
46975
|
inputSchema: {
|
|
46961
46976
|
target: exports_external2.string().describe("Tmux target: session:window or session:window.pane (e.g. platform-alumia:1)"),
|
|
46962
46977
|
message: exports_external2.string().describe("Message text to send"),
|
|
46963
|
-
delay_ms: exports_external2.coerce.number().optional().describe("Wait time (ms) after paste before hitting Enter. Default:
|
|
46978
|
+
delay_ms: exports_external2.coerce.number().optional().describe("Wait time (ms) after paste before hitting Enter. Default: adaptive 25-1500ms"),
|
|
46964
46979
|
retries: exports_external2.coerce.number().optional().describe("Max retry attempts (default: 3)"),
|
|
46965
46980
|
verify: exports_external2.coerce.boolean().optional().describe("Verify message was submitted after Enter (default: true)")
|
|
46966
46981
|
}
|
|
@@ -46992,8 +47007,8 @@ function registerTmuxTools(server) {
|
|
|
46992
47007
|
inputSchema: {
|
|
46993
47008
|
targets: exports_external2.array(exports_external2.string()).describe("List of tmux targets (session:window or session:window.pane)"),
|
|
46994
47009
|
message: exports_external2.string().describe("Message text to send to all targets"),
|
|
46995
|
-
delay_ms: exports_external2.coerce.number().optional().describe("Wait time (ms) after paste before Enter. Default:
|
|
46996
|
-
stagger_ms: exports_external2.coerce.number().optional().describe("Delay (ms) between sending to each target (default:
|
|
47010
|
+
delay_ms: exports_external2.coerce.number().optional().describe("Wait time (ms) after paste before Enter. Default: adaptive 25-1500ms"),
|
|
47011
|
+
stagger_ms: exports_external2.coerce.number().optional().describe("Delay (ms) between sending to each target (default: 500)"),
|
|
46997
47012
|
retries: exports_external2.coerce.number().optional().describe("Max retry attempts per target (default: 3)"),
|
|
46998
47013
|
verify: exports_external2.coerce.boolean().optional().describe("Verify each message was submitted (default: true)")
|
|
46999
47014
|
}
|
|
@@ -47005,24 +47020,24 @@ function registerTmuxTools(server) {
|
|
|
47005
47020
|
if (!message || !message.trim()) {
|
|
47006
47021
|
return { content: [{ type: "text", text: "message cannot be empty" }], isError: true };
|
|
47007
47022
|
}
|
|
47008
|
-
const stagger = typeof stagger_ms === "number" && stagger_ms
|
|
47009
|
-
const results =
|
|
47010
|
-
|
|
47011
|
-
const target =
|
|
47023
|
+
const stagger = typeof stagger_ms === "number" && stagger_ms >= 0 ? stagger_ms : 500;
|
|
47024
|
+
const results = new Array(targets.length);
|
|
47025
|
+
await Promise.all(targets.map(async (rawTarget, i) => {
|
|
47026
|
+
const target = String(rawTarget).trim();
|
|
47012
47027
|
if (i > 0 && stagger > 0)
|
|
47013
|
-
await sleep2(stagger);
|
|
47028
|
+
await sleep2(stagger * i);
|
|
47014
47029
|
try {
|
|
47015
47030
|
const result = await tmuxSend(target, message, {
|
|
47016
47031
|
delayMs: typeof delay_ms === "number" && delay_ms > 0 ? delay_ms : undefined,
|
|
47017
47032
|
retries: typeof retries === "number" && retries > 0 ? retries : undefined,
|
|
47018
47033
|
verify: verify !== false
|
|
47019
47034
|
});
|
|
47020
|
-
results
|
|
47035
|
+
results[i] = { target, ...result };
|
|
47021
47036
|
} catch (err) {
|
|
47022
47037
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
47023
|
-
results
|
|
47038
|
+
results[i] = { target, success: false, attempts: 0, error: errMsg };
|
|
47024
47039
|
}
|
|
47025
|
-
}
|
|
47040
|
+
}));
|
|
47026
47041
|
const succeeded = results.filter((r) => r.success).length;
|
|
47027
47042
|
const failed = results.length - succeeded;
|
|
47028
47043
|
return {
|
package/bin/mcp.js
CHANGED
|
@@ -44113,8 +44113,23 @@ import { execSync } from "child_process";
|
|
|
44113
44113
|
function sleep(ms) {
|
|
44114
44114
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
44115
44115
|
}
|
|
44116
|
+
function countLines(message) {
|
|
44117
|
+
const matches = message.match(/\r?\n/g);
|
|
44118
|
+
return (matches?.length ?? 0) + 1;
|
|
44119
|
+
}
|
|
44120
|
+
function getDefaultDelayMs(message) {
|
|
44121
|
+
const byLength = message.length * 1.5;
|
|
44122
|
+
const byLines = countLines(message) * 10;
|
|
44123
|
+
return Math.max(25, Math.min(1500, Math.round(byLength + byLines)));
|
|
44124
|
+
}
|
|
44125
|
+
function getVerifyPauseMs(message) {
|
|
44126
|
+
return message.length <= 120 ? 50 : 100;
|
|
44127
|
+
}
|
|
44128
|
+
function getRetryBackoffMs(attempt) {
|
|
44129
|
+
return Math.min(500, 100 * attempt);
|
|
44130
|
+
}
|
|
44116
44131
|
async function tmuxSend(target, message, opts = {}) {
|
|
44117
|
-
const delay = opts.delayMs ??
|
|
44132
|
+
const delay = opts.delayMs ?? getDefaultDelayMs(message);
|
|
44118
44133
|
const maxRetries = opts.retries ?? 3;
|
|
44119
44134
|
const verify = opts.verify !== false;
|
|
44120
44135
|
for (let attempt = 1;attempt <= maxRetries; attempt++) {
|
|
@@ -44123,16 +44138,17 @@ async function tmuxSend(target, message, opts = {}) {
|
|
|
44123
44138
|
execSync(`tmux send-keys -t ${JSON.stringify(target)} Enter`);
|
|
44124
44139
|
if (!verify)
|
|
44125
44140
|
return { success: true, attempts: attempt };
|
|
44126
|
-
await sleep(
|
|
44141
|
+
await sleep(getVerifyPauseMs(message));
|
|
44127
44142
|
const pane = execSync(`tmux capture-pane -t ${JSON.stringify(target)} -p`).toString();
|
|
44128
44143
|
const lastLines = pane.split(`
|
|
44129
|
-
`).slice(-
|
|
44144
|
+
`).slice(-6).join(`
|
|
44130
44145
|
`);
|
|
44131
|
-
|
|
44146
|
+
const marker = message.slice(0, Math.min(32, message.length));
|
|
44147
|
+
if (!lastLines.includes(marker)) {
|
|
44132
44148
|
return { success: true, attempts: attempt };
|
|
44133
44149
|
}
|
|
44134
44150
|
if (attempt < maxRetries)
|
|
44135
|
-
await sleep(
|
|
44151
|
+
await sleep(getRetryBackoffMs(attempt));
|
|
44136
44152
|
}
|
|
44137
44153
|
return { success: false, attempts: maxRetries };
|
|
44138
44154
|
}
|
|
@@ -44147,7 +44163,7 @@ function registerTmuxTools(server) {
|
|
|
44147
44163
|
inputSchema: {
|
|
44148
44164
|
target: exports_external.string().describe("Tmux target: session:window or session:window.pane (e.g. platform-alumia:1)"),
|
|
44149
44165
|
message: exports_external.string().describe("Message text to send"),
|
|
44150
|
-
delay_ms: exports_external.coerce.number().optional().describe("Wait time (ms) after paste before hitting Enter. Default:
|
|
44166
|
+
delay_ms: exports_external.coerce.number().optional().describe("Wait time (ms) after paste before hitting Enter. Default: adaptive 25-1500ms"),
|
|
44151
44167
|
retries: exports_external.coerce.number().optional().describe("Max retry attempts (default: 3)"),
|
|
44152
44168
|
verify: exports_external.coerce.boolean().optional().describe("Verify message was submitted after Enter (default: true)")
|
|
44153
44169
|
}
|
|
@@ -44179,8 +44195,8 @@ function registerTmuxTools(server) {
|
|
|
44179
44195
|
inputSchema: {
|
|
44180
44196
|
targets: exports_external.array(exports_external.string()).describe("List of tmux targets (session:window or session:window.pane)"),
|
|
44181
44197
|
message: exports_external.string().describe("Message text to send to all targets"),
|
|
44182
|
-
delay_ms: exports_external.coerce.number().optional().describe("Wait time (ms) after paste before Enter. Default:
|
|
44183
|
-
stagger_ms: exports_external.coerce.number().optional().describe("Delay (ms) between sending to each target (default:
|
|
44198
|
+
delay_ms: exports_external.coerce.number().optional().describe("Wait time (ms) after paste before Enter. Default: adaptive 25-1500ms"),
|
|
44199
|
+
stagger_ms: exports_external.coerce.number().optional().describe("Delay (ms) between sending to each target (default: 500)"),
|
|
44184
44200
|
retries: exports_external.coerce.number().optional().describe("Max retry attempts per target (default: 3)"),
|
|
44185
44201
|
verify: exports_external.coerce.boolean().optional().describe("Verify each message was submitted (default: true)")
|
|
44186
44202
|
}
|
|
@@ -44192,24 +44208,24 @@ function registerTmuxTools(server) {
|
|
|
44192
44208
|
if (!message || !message.trim()) {
|
|
44193
44209
|
return { content: [{ type: "text", text: "message cannot be empty" }], isError: true };
|
|
44194
44210
|
}
|
|
44195
|
-
const stagger = typeof stagger_ms === "number" && stagger_ms
|
|
44196
|
-
const results =
|
|
44197
|
-
|
|
44198
|
-
const target =
|
|
44211
|
+
const stagger = typeof stagger_ms === "number" && stagger_ms >= 0 ? stagger_ms : 500;
|
|
44212
|
+
const results = new Array(targets.length);
|
|
44213
|
+
await Promise.all(targets.map(async (rawTarget, i) => {
|
|
44214
|
+
const target = String(rawTarget).trim();
|
|
44199
44215
|
if (i > 0 && stagger > 0)
|
|
44200
|
-
await sleep2(stagger);
|
|
44216
|
+
await sleep2(stagger * i);
|
|
44201
44217
|
try {
|
|
44202
44218
|
const result = await tmuxSend(target, message, {
|
|
44203
44219
|
delayMs: typeof delay_ms === "number" && delay_ms > 0 ? delay_ms : undefined,
|
|
44204
44220
|
retries: typeof retries === "number" && retries > 0 ? retries : undefined,
|
|
44205
44221
|
verify: verify !== false
|
|
44206
44222
|
});
|
|
44207
|
-
results
|
|
44223
|
+
results[i] = { target, ...result };
|
|
44208
44224
|
} catch (err) {
|
|
44209
44225
|
const errMsg = err instanceof Error ? err.message : String(err);
|
|
44210
|
-
results
|
|
44226
|
+
results[i] = { target, success: false, attempts: 0, error: errMsg };
|
|
44211
44227
|
}
|
|
44212
|
-
}
|
|
44228
|
+
}));
|
|
44213
44229
|
const succeeded = results.filter((r) => r.success).length;
|
|
44214
44230
|
const failed = results.length - succeeded;
|
|
44215
44231
|
return {
|
|
@@ -44224,7 +44240,7 @@ function registerTmuxTools(server) {
|
|
|
44224
44240
|
// package.json
|
|
44225
44241
|
var package_default = {
|
|
44226
44242
|
name: "@hasna/conversations",
|
|
44227
|
-
version: "0.2.
|
|
44243
|
+
version: "0.2.31",
|
|
44228
44244
|
description: "Real-time CLI messaging for AI agents",
|
|
44229
44245
|
type: "module",
|
|
44230
44246
|
bin: {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hasna/conversations",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.31",
|
|
4
4
|
"description": "Real-time CLI messaging for AI agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -77,4 +77,4 @@
|
|
|
77
77
|
"url": "https://github.com/hasna/conversations/issues"
|
|
78
78
|
},
|
|
79
79
|
"homepage": "https://github.com/hasna/conversations#readme"
|
|
80
|
-
}
|
|
80
|
+
}
|