agentmux 0.8.0

Multi-agent coordination runtime with inter-agent messaging across CLI, MCP, tmux, and ACP.
Documentation
pub(super) fn wrap_index(index: usize, delta: isize, len: usize) -> usize {
    if len == 0 {
        return 0;
    }
    let len = len as isize;
    ((index as isize + delta).rem_euclid(len)) as usize
}

pub(super) fn previous_char_boundary(value: &str, cursor_index: usize) -> usize {
    value[..cursor_index]
        .char_indices()
        .next_back()
        .map(|(index, _)| index)
        .unwrap_or(0)
}

pub(super) fn next_char_boundary(value: &str, cursor_index: usize) -> usize {
    value
        .char_indices()
        .find_map(|(index, _)| (index > cursor_index).then_some(index))
        .unwrap_or(value.len())
}

pub(super) fn line_ranges(value: &str) -> Vec<(usize, usize)> {
    let mut ranges = Vec::<(usize, usize)>::new();
    let mut line_start = 0usize;
    for (index, character) in value.char_indices() {
        if character == '\n' {
            ranges.push((line_start, index));
            line_start = index + character.len_utf8();
        }
    }
    ranges.push((line_start, value.len()));
    ranges
}

pub(super) fn line_range_for_cursor(value: &str, cursor_index: usize) -> (usize, usize) {
    let cursor_index = cursor_index.min(value.len());
    let ranges = line_ranges(value);
    for (line_index, (line_start, line_end)) in ranges.iter().enumerate() {
        if cursor_index <= *line_end || line_index + 1 == ranges.len() {
            return (*line_start, *line_end);
        }
    }
    (0, value.len())
}

pub(super) fn line_and_column_for_index(value: &str, cursor_index: usize) -> (usize, usize) {
    let ranges = line_ranges(value);
    for (line_index, (line_start, line_end)) in ranges.iter().enumerate() {
        if cursor_index <= *line_end || line_index + 1 == ranges.len() {
            let column_end = cursor_index.min(*line_end);
            let column = value[*line_start..column_end].chars().count();
            return (line_index, column);
        }
    }
    (0, 0)
}

pub(super) fn cursor_index_for_line_column(
    value: &str,
    line_range: (usize, usize),
    target_column: usize,
) -> usize {
    let (line_start, line_end) = line_range;
    let line_slice = &value[line_start..line_end];
    let line_len = line_slice.chars().count();
    let clamped_column = target_column.min(line_len);
    if clamped_column == line_len {
        return line_end;
    }
    line_start
        + line_slice
            .char_indices()
            .nth(clamped_column)
            .map(|(index, _)| index)
            .unwrap_or(0)
}