| 1 | //! `/rlm` command. |
| 2 | |
| 3 | use crate::commands::traits::{CommandInfo, RegisterCommand}; |
| 4 | use crate::localization::MessageId; |
| 5 | use crate::tui::app::{App, AppAction}; |
| 6 | |
| 7 | use super::CommandResult; |
| 8 | |
| 9 | pub(in crate::commands) const COMMAND_INFO: CommandInfo = CommandInfo { |
| 10 | name: "rlm", |
| 11 | aliases: &["recursive", "digui"], |
| 12 | usage: "/rlm [N] <file_or_text>", |
| 13 | description_id: MessageId::CmdRlmDescription, |
| 14 | }; |
| 15 | |
| 16 | pub(in crate::commands) struct RlmCmd; |
| 17 | |
| 18 | impl RegisterCommand for RlmCmd { |
| 19 | fn info() -> &'static CommandInfo { |
| 20 | &COMMAND_INFO |
| 21 | } |
| 22 | |
| 23 | fn execute(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 24 | rlm(app, arg) |
| 25 | } |
| 26 | } |
| 27 | |
| 28 | pub fn rlm(app: &mut App, arg: Option<&str>) -> CommandResult { |
| 29 | // The `[N]` depth prefix stays accepted so saved guidance and muscle memory |
| 30 | // keep working, but it was part of the retired open/configure/eval control |
| 31 | // surface. The session-persistent working context now owns one route, so |
| 32 | // the depth is parsed and dropped rather than rejected. |
| 33 | let (_legacy_depth, target) = match super::util::parse_depth_prefixed_arg(arg, 1) { |
| 34 | Ok(parsed) => parsed, |
| 35 | Err(message) => return CommandResult::error(message), |
| 36 | }; |
| 37 | let target = match target { |
| 38 | Some(p) if !p.trim().is_empty() => p.trim().to_string(), |
| 39 | _ => { |
| 40 | return CommandResult::error( |
| 41 | "Usage: /rlm [N] <file_or_text>\n\n\ |
| 42 | Works through a large file or block of text in a context that \ |
| 43 | stays loaded for the rest of the session." |
| 44 | .to_string(), |
| 45 | ); |
| 46 | } |
| 47 | }; |
| 48 | |
| 49 | let source = if resolves_to_existing_file(app, &target) { |
| 50 | format!("the workspace file `{target}`") |
| 51 | } else { |
| 52 | format!("this text: {target:?}") |
| 53 | }; |
| 54 | let message = format!( |
| 55 | "Use the session-persistent working context for this request. It stays alive across turns. Work on {source}. In a `repl` block, load a file into a normal Python variable when useful, retain useful variables and imports, inspect the durable transcript through `context_meta`, `search`, `peek`, or `chunk`, and use `sub_query` or `sub_rlm` only when extra reasoning genuinely helps. Do not use legacy `rlm` tool actions. Call `finalize(...)` only when ready to answer." |
| 56 | ); |
| 57 | |
| 58 | CommandResult::with_message_and_action( |
| 59 | "Loading that into a persistent working context...".to_string(), |
| 60 | AppAction::SendMessage(message), |
| 61 | ) |
| 62 | } |
| 63 | |
| 64 | fn resolves_to_existing_file(app: &App, input: &str) -> bool { |
| 65 | let path = std::path::Path::new(input); |
| 66 | let candidate = if path.is_absolute() { |
| 67 | path.to_path_buf() |
| 68 | } else { |
| 69 | app.workspace.join(path) |
| 70 | }; |
| 71 | candidate.is_file() |
| 72 | } |
| 73 |