remem-ai 0.6.78

Local-first coding agent memory for Claude Code and OpenAI Codex
Documentation
use brush_parser::ast::{CommandPrefixOrSuffixItem, SimpleCommand};

use super::function_args::expand_function_body;
use super::static_execution::{
    direct_command_name, static_builtin_command_name, static_env_split_tokens, static_eval_payload,
    static_export_function_change, static_shell_command_payload, static_shell_exits,
    static_shell_is_bash, static_shell_stdin, static_source_stdin_arguments, static_token_measure,
    static_unset_function_names,
};
use super::stdin_payload::EffectiveStdin;
use super::{unwrap, CommandCollector, PositionalContext};

impl CommandCollector {
    pub(super) fn collect_static_tokens(
        &mut self,
        tokens: Vec<String>,
        command: &SimpleCommand,
    ) -> Result<(), String> {
        if self.command_has_fallible_setup(command) {
            return self.collect_alternative_shell_states(
                vec![true, false],
                true,
                |collector, setup_succeeded| {
                    if setup_succeeded {
                        collector
                            .collect_static_tokens_after_redirect_setup(tokens.clone(), command)?;
                        Ok(())
                    } else {
                        collector.last_positional_status = Some(false);
                        collector.last_positional_success = None;
                        collector.last_positional_failure = collector.positional_context.clone();
                        Ok(())
                    }
                },
            );
        }
        self.collect_static_tokens_after_redirect_setup(tokens, command)
    }

    fn collect_static_tokens_after_redirect_setup(
        &mut self,
        tokens: Vec<String>,
        command: &SimpleCommand,
    ) -> Result<(), String> {
        if self.collect_static_alias_call(&tokens, command)? {
            return Ok(());
        }
        self.collect_static_tokens_after_alias(tokens, command)
    }

    pub(super) fn collect_static_tokens_after_alias(
        &mut self,
        tokens: Vec<String>,
        command: &SimpleCommand,
    ) -> Result<(), String> {
        if self.collect_static_function_call(&tokens, command)? {
            return Ok(());
        }
        self.collect_static_tokens_after_function(tokens, command)
    }

    pub(super) fn collect_static_tokens_after_function(
        &mut self,
        mut tokens: Vec<String>,
        command: &SimpleCommand,
    ) -> Result<(), String> {
        while let Some(expanded) = static_env_split_tokens(&tokens) {
            let before = static_token_measure(&tokens);
            let after = static_token_measure(&expanded);
            if after >= before {
                return Err("env -S static argv expansion did not make progress".to_string());
            }
            tokens = expanded;
        }
        if let Some(names) = static_unset_function_names(&tokens) {
            for name in names {
                if self.execution_is_definite {
                    self.functions.remove(name);
                    self.exported_functions.remove(name);
                } else if let Some(definitions) = self.functions.get_mut(name) {
                    for definition in definitions {
                        definition.is_definite = false;
                    }
                }
            }
        }
        if let Some((exported, names)) = static_export_function_change(&tokens) {
            for name in names {
                if exported && self.functions.contains_key(name) {
                    self.exported_functions.insert(name.to_string());
                } else if !exported && self.execution_is_definite {
                    self.exported_functions.remove(name);
                }
            }
        }
        self.apply_static_shell_state(&tokens);
        self.apply_static_positional_state(&tokens, false);
        if let Some(payload) = static_eval_payload(&tokens) {
            self.collect_source(&payload)?;
        }
        if let Some(shell_command) = static_shell_command_payload(&tokens) {
            let inherited_stdin = match self.effective_stdin_payload(command)? {
                EffectiveStdin::Replaced(payload) => payload,
                EffectiveStdin::Untouched => None,
            };
            self.with_child_shell_scope(static_shell_is_bash(&tokens), |collector| {
                collector.with_inherited_stdin(inherited_stdin, |collector| {
                    collector.with_positional_context(
                        Some(PositionalContext {
                            zero_argument: shell_command.zero_argument,
                            arguments: shell_command.arguments,
                            possible_arguments: Vec::new(),
                        }),
                        |collector| {
                            collector.collect_source(&shell_command.payload)?;
                            collector.collect_exit_traps()
                        },
                    )
                })
            })?;
        }
        if let Some(shell_stdin) = static_shell_stdin(&tokens) {
            let payload = match self.effective_stdin_payload(command)? {
                EffectiveStdin::Replaced(payload) => payload,
                EffectiveStdin::Untouched => self.inherited_stdin.clone(),
            };
            if let Some(payload) = payload {
                self.with_child_shell_scope(static_shell_is_bash(&tokens), |collector| {
                    collector.with_positional_context(
                        Some(PositionalContext {
                            zero_argument: Some(shell_stdin.zero_argument),
                            arguments: shell_stdin.arguments,
                            possible_arguments: Vec::new(),
                        }),
                        |collector| collector.collect_source(&payload),
                    )
                })?;
            }
        } else if let Some(arguments) = static_source_stdin_arguments(&tokens) {
            self.collect_source_stdin_payload(command, arguments)?;
        }
        self.record_static_command_outcomes(&tokens);
        let shell_exits = static_shell_exits(&tokens);
        self.segments.push(tokens);
        if self.execution_is_definite && shell_exits {
            self.execution_terminated = true;
        }
        Ok(())
    }

    fn record_static_command_outcomes(&mut self, tokens: &[String]) {
        if self.positional_context.is_none() {
            return;
        }
        if self.last_positional_success.is_some() || self.last_positional_failure.is_some() {
            return;
        }
        let status = static_builtin_command_name(tokens).and_then(|name| match name {
            ":" | "true" => Some(true),
            "false" => Some(false),
            _ => None,
        });
        let context = self.positional_context.clone();
        match status {
            Some(true) => self.last_positional_success = context,
            Some(false) => self.last_positional_failure = context,
            None => {
                self.last_positional_success = context.clone();
                self.last_positional_failure = context;
            }
        }
        self.last_positional_status = status;
    }

    pub(super) fn command_has_fallible_setup(&self, command: &SimpleCommand) -> bool {
        command_has_fallible_setup(command)
            || command
                .prefix
                .iter()
                .flat_map(|items| &items.0)
                .any(|item| match item {
                    CommandPrefixOrSuffixItem::AssignmentWord(assignment, _) => self
                        .readonly_variables
                        .contains(&assignment.name.to_string()),
                    _ => false,
                })
    }

    fn collect_static_function_call(
        &mut self,
        tokens: &[String],
        command: &SimpleCommand,
    ) -> Result<bool, String> {
        let Some(name) = direct_command_name(tokens) else {
            return Ok(false);
        };
        let Some(definitions) = self.functions.get(name).cloned() else {
            return Ok(false);
        };
        let definitely_defined = definitions.iter().any(|definition| definition.is_definite);
        if !self.active_functions.insert(name.to_string()) {
            self.collect_static_tokens_after_function(tokens.to_vec(), command)?;
            return Ok(true);
        }
        let result = (|| {
            let command_index = unwrap::direct_command_index(tokens)
                .ok_or_else(|| "function call lost its command position".to_string())?;
            let arguments = tokens[command_index + 1..].to_vec();
            let mut alternatives = definitions.into_iter().map(Some).collect::<Vec<_>>();
            if !definitely_defined {
                alternatives.push(None);
            }
            self.collect_alternative_shell_states(alternatives, true, |collector, definition| {
                let Some(definition) = definition else {
                    return collector
                        .collect_static_tokens_after_function(tokens.to_vec(), command);
                };
                let source = expand_function_body(&definition.body, &arguments);
                let function_context =
                    collector
                        .positional_context
                        .as_ref()
                        .map(|context| PositionalContext {
                            zero_argument: context.zero_argument.clone(),
                            arguments: arguments.clone(),
                            possible_arguments: Vec::new(),
                        });
                collector.with_positional_context(function_context, |collector| {
                    collector.collect_source(&source)
                })?;
                let success = collector.last_positional_success.is_some();
                let failure = collector.last_positional_failure.is_some();
                collector.last_positional_success = success
                    .then(|| collector.positional_context.clone())
                    .flatten();
                collector.last_positional_failure = failure
                    .then(|| collector.positional_context.clone())
                    .flatten();
                Ok(())
            })?;
            Ok(())
        })();
        self.active_functions.remove(name);
        result.map(|()| true)
    }
}

pub(super) fn command_has_fallible_setup(command: &SimpleCommand) -> bool {
    command
        .prefix
        .iter()
        .flat_map(|items| &items.0)
        .chain(command.suffix.iter().flat_map(|items| &items.0))
        .any(|item| matches!(item, CommandPrefixOrSuffixItem::IoRedirect(_)))
}