Skip to content

Commit 84cf5c4

Browse files
authored
Merge pull request #74 from dmoliveira/loopmux-release-0-1-26
Add initial poll interval and bump to 0.1.26
2 parents 7e25c5b + 1cf29d4 commit 84cf5c4

File tree

4 files changed

+69
-9
lines changed

4 files changed

+69
-9
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
[package]
33
name = "loopmux"
4-
version = "0.1.25"
4+
version = "0.1.26"
55
edition = "2024"
66
description = "Loop prompts into tmux panes with triggers and delays."
77
license = "MIT"

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ target: "ai:8.1"
8787
iterations: 50
8888
tail: 3
8989
poll: 5
90+
initial_poll: 5
9091
default_action:
9192
prompt: "Do the next iteration."
9293
rules:
@@ -116,7 +117,7 @@ runs:
116117
117118
Notes:
118119
- Imported files can contribute extra `runs`/`events` profiles.
119-
- Each profile uses the same run-config schema as normal YAML runs (`target`, `rules`, `poll`, `tail`, or `exec.command`).
120+
- Each profile uses the same run-config schema as normal YAML runs (`target`, `rules`, `poll`, `initial_poll`, `tail`, or `exec.command`).
120121
- Startup validates all selected profiles before launch and prints clear per-profile errors.
121122
- Migration guide: `docs/specs/config-first-migration.md`.
122123

@@ -193,6 +194,7 @@ logging:
193194
exec:
194195
command: "gw-watch-comp"
195196
poll: 10
197+
initial_poll: 5
196198
iterations: 3
197199
name: "gw-watch"
198200
```

src/main.rs

Lines changed: 64 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ enum Command {
5252
#[derive(Debug, Parser)]
5353
#[command(
5454
after_help = concat!(
55-
"Examples:\n loopmux run -t ai:5.0 -n 5 --prompt \"Do the next iteration.\" --trigger \"Concluded|What is next\" --once\n loopmux run -t ai:5.0 -n 5 --prompt \"Do the next iteration.\" --trigger \"Concluded|What is next\" --exclude \"PROD\"\n loopmux run --config loop.yaml --duration 2h\n loopmux run --tui\n loopmux run --exec \"gw-watch-comp\" --poll 10 --iterations 3\n\nDefaults:\n tail=1 (last non-blank line)\n poll=5s\n trigger-confirm-seconds=5\n history-limit=50\n log-preview-lines=3\n trigger-edge=on\n recheck-before-send=on\n\nDuration units: s, m, h, d, w, mon (30d), y (365d)\n\n",
55+
"Examples:\n loopmux run -t ai:5.0 -n 5 --prompt \"Do the next iteration.\" --trigger \"Concluded|What is next\" --once\n loopmux run -t ai:5.0 -n 5 --prompt \"Do the next iteration.\" --trigger \"Concluded|What is next\" --exclude \"PROD\"\n loopmux run --config loop.yaml --duration 2h\n loopmux run --tui\n loopmux run --exec \"gw-watch-comp\" --poll 10 --iterations 3\n\nDefaults:\n tail=1 (last non-blank line)\n poll=5s\n initial-poll=5s\n trigger-confirm-seconds=5\n history-limit=50\n log-preview-lines=3\n trigger-edge=on\n recheck-before-send=on\n\nDuration units: s, m, h, d, w, mon (30d), y (365d)\n\n",
5656
"Version: ",
5757
env!("CARGO_PKG_VERSION"),
5858
"\n"
@@ -122,6 +122,9 @@ struct RunArgs {
122122
/// Poll interval in seconds when waiting for changes.
123123
#[arg(long)]
124124
poll: Option<u64>,
125+
/// Initial wait in seconds before the second scan (default 5).
126+
#[arg(long)]
127+
initial_poll: Option<u64>,
125128
/// Seconds a trigger must remain matched before send (default 5).
126129
#[arg(long)]
127130
trigger_confirm_seconds: Option<u64>,
@@ -149,6 +152,7 @@ struct RunArgs {
149152
}
150153

151154
const DEFAULT_HISTORY_LIMIT: usize = 50;
155+
const DEFAULT_INITIAL_POLL_SECONDS: u64 = 5;
152156
const DEFAULT_TRIGGER_CONFIRM_SECONDS: u64 = 5;
153157

154158
#[derive(Debug, Serialize, Deserialize, Default)]
@@ -172,6 +176,7 @@ struct HistoryEntry {
172176
head: Option<usize>,
173177
once: bool,
174178
poll: Option<u64>,
179+
initial_poll: Option<u64>,
175180
trigger_confirm_seconds: Option<u64>,
176181
log_preview_lines: Option<usize>,
177182
trigger_edge: Option<bool>,
@@ -315,6 +320,7 @@ struct Config {
315320
iterations: Option<u32>,
316321
infinite: Option<bool>,
317322
poll: Option<u64>,
323+
initial_poll: Option<u64>,
318324
trigger_confirm_seconds: Option<u64>,
319325
log_preview_lines: Option<usize>,
320326
trigger_edge: Option<bool>,
@@ -1560,6 +1566,9 @@ fn hydrate_run_args_from_history(mut args: RunArgs) -> Result<RunArgs> {
15601566
if args.poll.is_none() {
15611567
args.poll = entry.poll;
15621568
}
1569+
if args.initial_poll.is_none() {
1570+
args.initial_poll = entry.initial_poll;
1571+
}
15631572
if args.trigger_confirm_seconds.is_none() {
15641573
args.trigger_confirm_seconds = entry.trigger_confirm_seconds;
15651574
}
@@ -3228,7 +3237,7 @@ fn history_signature(args: &RunArgs) -> Option<String> {
32283237
return None;
32293238
}
32303239
Some(format!(
3231-
"target={target}|prompt={prompt}|trigger={trigger}|trigger_expr={trigger_expr}|trigger_exact_line={}|exclude={}|pre={}|post={}|iterations={}|tail={}|head={}|once={}|poll={}|trigger_confirm_seconds={}|log_preview_lines={}|trigger_edge={}|recheck_before_send={}|fanout={}|duration={}",
3240+
"target={target}|prompt={prompt}|trigger={trigger}|trigger_expr={trigger_expr}|trigger_exact_line={}|exclude={}|pre={}|post={}|iterations={}|tail={}|head={}|once={}|poll={}|initial_poll={}|trigger_confirm_seconds={}|log_preview_lines={}|trigger_edge={}|recheck_before_send={}|fanout={}|duration={}",
32323241
args.trigger_exact_line,
32333242
args.exclude.as_deref().unwrap_or(""),
32343243
args.pre.as_deref().unwrap_or(""),
@@ -3238,6 +3247,7 @@ fn history_signature(args: &RunArgs) -> Option<String> {
32383247
args.head.map(|v| v.to_string()).unwrap_or_default(),
32393248
args.once,
32403249
args.poll.map(|v| v.to_string()).unwrap_or_default(),
3250+
args.initial_poll.map(|v| v.to_string()).unwrap_or_default(),
32413251
args.trigger_confirm_seconds
32423252
.map(|v| v.to_string())
32433253
.unwrap_or_default(),
@@ -3281,6 +3291,7 @@ fn store_run_history(args: &RunArgs) -> Result<()> {
32813291
head: args.head,
32823292
once: args.once,
32833293
poll: args.poll,
3294+
initial_poll: args.initial_poll,
32843295
trigger_confirm_seconds: args.trigger_confirm_seconds,
32853296
log_preview_lines: args.log_preview_lines,
32863297
trigger_edge: Some(!args.no_trigger_edge),
@@ -3297,7 +3308,7 @@ fn store_run_history(args: &RunArgs) -> Result<()> {
32973308

32983309
fn history_entry_signature(entry: &HistoryEntry) -> Option<String> {
32993310
Some(format!(
3300-
"target={}|prompt={}|trigger={}|trigger_expr={}|trigger_exact_line={}|exclude={}|pre={}|post={}|iterations={}|tail={}|head={}|once={}|poll={}|trigger_confirm_seconds={}|log_preview_lines={}|trigger_edge={}|recheck_before_send={}|fanout={}|duration={}",
3311+
"target={}|prompt={}|trigger={}|trigger_expr={}|trigger_exact_line={}|exclude={}|pre={}|post={}|iterations={}|tail={}|head={}|once={}|poll={}|initial_poll={}|trigger_confirm_seconds={}|log_preview_lines={}|trigger_edge={}|recheck_before_send={}|fanout={}|duration={}",
33013312
entry.target,
33023313
entry.prompt,
33033314
entry.trigger,
@@ -3311,6 +3322,10 @@ fn history_entry_signature(entry: &HistoryEntry) -> Option<String> {
33113322
entry.head.map(|v| v.to_string()).unwrap_or_default(),
33123323
entry.once,
33133324
entry.poll.map(|v| v.to_string()).unwrap_or_default(),
3325+
entry
3326+
.initial_poll
3327+
.map(|v| v.to_string())
3328+
.unwrap_or_default(),
33143329
entry
33153330
.trigger_confirm_seconds
33163331
.map(|v| v.to_string())
@@ -3442,6 +3457,7 @@ fn run_loop(config: ResolvedConfig, identity: RunIdentity) -> Result<()> {
34423457
let run_started = std::time::Instant::now();
34433458
let mut held_total = std::time::Duration::from_secs(0);
34443459
let mut hold_started: Option<std::time::Instant> = None;
3460+
let mut first_wait_cycle = true;
34453461
fleet_registry.update(&config.target_label, loop_state, send_count, config.poll)?;
34463462

34473463
while config.infinite || send_count < max_sends {
@@ -3770,14 +3786,20 @@ fn run_loop(config: ResolvedConfig, identity: RunIdentity) -> Result<()> {
37703786
let _ = std::io::stdout().flush();
37713787
}
37723788

3789+
let wait_seconds = if first_wait_cycle {
3790+
config.initial_poll
3791+
} else {
3792+
config.poll
3793+
};
37733794
sleep_with_heartbeat(
37743795
&fleet_registry,
37753796
&config.target_label,
37763797
loop_state,
37773798
send_count,
3778-
config.poll,
3799+
wait_seconds,
37793800
config.poll,
37803801
)?;
3802+
first_wait_cycle = false;
37813803
continue;
37823804
}
37833805

@@ -4289,8 +4311,13 @@ fn run_loop(config: ResolvedConfig, identity: RunIdentity) -> Result<()> {
42894311
}
42904312

42914313
if ui_mode == UiMode::Tui {
4314+
let wait_seconds = if first_wait_cycle {
4315+
config.initial_poll
4316+
} else {
4317+
config.poll
4318+
};
42924319
let sleep_until =
4293-
std::time::Instant::now() + std::time::Duration::from_secs(config.poll);
4320+
std::time::Instant::now() + std::time::Duration::from_secs(wait_seconds);
42944321
let mut should_exit_loop = false;
42954322
while std::time::Instant::now() < sleep_until {
42964323
if let Some(tui_state) = tui.as_mut() {
@@ -4402,15 +4429,22 @@ fn run_loop(config: ResolvedConfig, identity: RunIdentity) -> Result<()> {
44024429
if force_rescan {
44034430
continue;
44044431
}
4432+
first_wait_cycle = false;
44054433
} else {
4434+
let wait_seconds = if first_wait_cycle {
4435+
config.initial_poll
4436+
} else {
4437+
config.poll
4438+
};
44064439
sleep_with_heartbeat(
44074440
&fleet_registry,
44084441
&config.target_label,
44094442
loop_state,
44104443
send_count,
4411-
config.poll,
4444+
wait_seconds,
44124445
config.poll,
44134446
)?;
4447+
first_wait_cycle = false;
44144448
}
44154449
}
44164450

@@ -5071,6 +5105,7 @@ fn resolve_run_config(args: &RunArgs) -> Result<Config> {
50715105
iterations: args.iterations,
50725106
infinite: None,
50735107
poll: args.poll,
5108+
initial_poll: args.initial_poll,
50745109
trigger_confirm_seconds: args.trigger_confirm_seconds,
50755110
log_preview_lines: args.log_preview_lines,
50765111
trigger_edge: Some(!args.no_trigger_edge),
@@ -5152,6 +5187,7 @@ fn resolve_run_config(args: &RunArgs) -> Result<Config> {
51525187
iterations: args.iterations,
51535188
infinite: None,
51545189
poll: args.poll,
5190+
initial_poll: args.initial_poll,
51555191
trigger_confirm_seconds: args.trigger_confirm_seconds,
51565192
log_preview_lines: args.log_preview_lines,
51575193
trigger_edge: Some(!args.no_trigger_edge),
@@ -5241,6 +5277,7 @@ struct ResolvedConfig {
52415277
infinite: bool,
52425278
has_prompt: bool,
52435279
poll: u64,
5280+
initial_poll: u64,
52445281
trigger_confirm_seconds: u64,
52455282
log_preview_lines: usize,
52465283
trigger_edge: bool,
@@ -6099,6 +6136,10 @@ fn resolve_config(
60996136
}
61006137

61016138
let poll = config.poll.unwrap_or(5).max(1);
6139+
let initial_poll = config
6140+
.initial_poll
6141+
.unwrap_or(DEFAULT_INITIAL_POLL_SECONDS)
6142+
.max(1);
61026143
let trigger_confirm_seconds = config
61036144
.trigger_confirm_seconds
61046145
.unwrap_or(DEFAULT_TRIGGER_CONFIRM_SECONDS);
@@ -6136,6 +6177,7 @@ fn resolve_config(
61366177
infinite,
61376178
has_prompt,
61386179
poll,
6180+
initial_poll,
61396181
trigger_confirm_seconds,
61406182
log_preview_lines,
61416183
trigger_edge,
@@ -6203,6 +6245,7 @@ fn print_validation(config: &ResolvedConfig) {
62036245
}
62046246
}
62056247
println!("- poll: {}s", config.poll);
6248+
println!("- initial_poll: {}s", config.initial_poll);
62066249
println!(
62076250
"- trigger_confirm_seconds: {}s",
62086251
config.trigger_confirm_seconds
@@ -7363,6 +7406,7 @@ runs:
73637406
single_line: false,
73647407
tui: false,
73657408
poll: None,
7409+
initial_poll: None,
73667410
trigger_confirm_seconds: None,
73677411
log_preview_lines: None,
73687412
no_trigger_edge: false,
@@ -7399,6 +7443,7 @@ runs:
73997443
single_line: false,
74007444
tui: false,
74017445
poll: Some(5),
7446+
initial_poll: None,
74027447
trigger_confirm_seconds: None,
74037448
log_preview_lines: None,
74047449
no_trigger_edge: false,
@@ -7442,6 +7487,7 @@ runs:
74427487
single_line: false,
74437488
tui: false,
74447489
poll: None,
7490+
initial_poll: None,
74457491
trigger_confirm_seconds: None,
74467492
log_preview_lines: None,
74477493
no_trigger_edge: false,
@@ -7480,6 +7526,7 @@ runs:
74807526
single_line: false,
74817527
tui: false,
74827528
poll: None,
7529+
initial_poll: None,
74837530
trigger_confirm_seconds: None,
74847531
log_preview_lines: None,
74857532
no_trigger_edge: false,
@@ -7536,6 +7583,7 @@ runs:
75367583
single_line: false,
75377584
tui: false,
75387585
poll: None,
7586+
initial_poll: None,
75397587
trigger_confirm_seconds: None,
75407588
log_preview_lines: None,
75417589
no_trigger_edge: false,
@@ -7577,6 +7625,7 @@ runs:
75777625
single_line: false,
75787626
tui: false,
75797627
poll: None,
7628+
initial_poll: None,
75807629
trigger_confirm_seconds: None,
75817630
log_preview_lines: None,
75827631
no_trigger_edge: false,
@@ -7618,6 +7667,7 @@ runs:
76187667
single_line: false,
76197668
tui: false,
76207669
poll: None,
7670+
initial_poll: None,
76217671
trigger_confirm_seconds: None,
76227672
log_preview_lines: None,
76237673
no_trigger_edge: false,
@@ -7659,6 +7709,7 @@ runs:
76597709
single_line: false,
76607710
tui: false,
76617711
poll: None,
7712+
initial_poll: None,
76627713
trigger_confirm_seconds: None,
76637714
log_preview_lines: None,
76647715
no_trigger_edge: false,
@@ -7689,6 +7740,7 @@ runs:
76897740
iterations: Some(1),
76907741
infinite: None,
76917742
poll: Some(1),
7743+
initial_poll: None,
76927744
trigger_confirm_seconds: Some(0),
76937745
log_preview_lines: Some(1),
76947746
trigger_edge: Some(true),
@@ -7741,6 +7793,7 @@ runs:
77417793
iterations: Some(3),
77427794
infinite: None,
77437795
poll: Some(7),
7796+
initial_poll: None,
77447797
trigger_confirm_seconds: None,
77457798
log_preview_lines: None,
77467799
trigger_edge: None,
@@ -8009,6 +8062,7 @@ runs:
80098062
single_line: false,
80108063
tui: false,
80118064
poll: 5,
8065+
initial_poll: 5,
80128066
log_preview_lines: 3,
80138067
trigger_edge: true,
80148068
recheck_before_send: true,
@@ -8070,6 +8124,7 @@ runs:
80708124
single_line: false,
80718125
tui: false,
80728126
poll: 5,
8127+
initial_poll: 5,
80738128
log_preview_lines: 3,
80748129
trigger_edge: true,
80758130
recheck_before_send: true,
@@ -8131,6 +8186,7 @@ runs:
81318186
single_line: false,
81328187
tui: true,
81338188
poll: 10,
8189+
initial_poll: 5,
81348190
log_preview_lines: 3,
81358191
trigger_edge: true,
81368192
recheck_before_send: true,
@@ -8191,6 +8247,7 @@ runs:
81918247
single_line: true,
81928248
tui: false,
81938249
poll: 10,
8250+
initial_poll: 5,
81948251
log_preview_lines: 3,
81958252
trigger_edge: true,
81968253
recheck_before_send: true,
@@ -8640,6 +8697,7 @@ fn default_template() -> String {
86408697
let template = r#"target: "ai:5.0"
86418698
iterations: 10
86428699
poll: 5
8700+
initial_poll: 5
86438701
trigger_confirm_seconds: 5
86448702
log_preview_lines: 3
86458703
trigger_edge: true

0 commit comments

Comments
 (0)