Enhance tool documentation and add new features

- Added module-level documentation for memory tools (`remember`, `recall`, `forget`) to clarify their purpose.
- Improved documentation in `recall.rs` and `remember.rs` to describe the functionality and flow of memory entry operations.
- Updated `mod.rs` to include descriptions for the tool trait and execution context.
- Enhanced `plan.rs` with detailed comments on plan-mode signaling tools.
- Documented text search tools in `search.rs` to explain their functionality.
- Improved sequential-thinking tool documentation in `seqthink.rs`.
- Added safety filter documentation in `shell_filter` for credential and git operations.
- Enhanced utility tools documentation, including `cd`, `dir_cache_update`, and `todowrite`.
- Improved rendering documentation in view modules (`chat`, `markdown`, `status`, `workflow`) to clarify rendering flows and purposes.
This commit is contained in:
asepharyana
2026-07-12 11:28:39 +07:00
parent 7158d362fd
commit 2efd40ca88
124 changed files with 2379 additions and 19 deletions
+11
View File
@@ -1,5 +1,16 @@
//! Bash mode: handles submitting a shell command from the bash input panel.
use crate::app::state::rest::AppStateRest;
/// Launch a background bash job for the submitted command.
///
/// Flow: ignore empty input → spawn the job (fire-and-forget, the job's
/// output is polled elsewhere via `bgbash::control`) → mark state dirty
/// so the TUI re-renders.
///
/// Why: the returned `BashJob` handle is intentionally dropped — this
/// function only needs to kick the job off; the job registers itself in
/// the shared jobs map for later polling.
pub fn handle_bash_submit(state: &mut AppStateRest, command: String) {
if !command.is_empty() {
let _ = crate::app::bgbash::job::spawn_bash_job(command);
+30
View File
@@ -1,6 +1,11 @@
//! Editor mode: a minimal in-TUI line editor for viewing/modifying a file,
//! with bounded undo history.
use crate::app::state::rest::AppStateRest;
use crate::app::state::types::Overlay;
/// State for the built-in line editor overlay: buffer contents, cursor
/// position, and a bounded undo stack.
#[derive(Debug, Clone)]
pub struct EditorState {
pub path: String,
@@ -23,6 +28,8 @@ impl Default for EditorState {
}
impl EditorState {
/// Create a fresh editor state for `path`, seeded with existing content
/// (or a single empty line for a new file).
pub fn open(path: String, existing_content: Option<Vec<String>>) -> Self {
let content = existing_content.unwrap_or_else(|| vec![String::new()]);
EditorState {
@@ -32,12 +39,20 @@ impl EditorState {
}
}
/// Insert a new empty line immediately after the cursor line.
///
/// Why: snapshots content to the undo stack first, matching every other
/// mutating method here.
pub fn insert_line_after(&mut self) {
self.save_undo();
let pos = (self.cursor_line + 1).min(self.content.len());
self.content.insert(pos, String::new());
}
/// Push a snapshot of the current content onto the undo stack, capped at 50 entries.
///
/// Why: `remove(0)` on overflow bounds memory use at the cost of O(n)
/// shifting; the cap (50) keeps that cost negligible in practice.
fn save_undo(&mut self) {
self.undo_stack.push(self.content.clone());
if self.undo_stack.len() > 50 {
@@ -45,6 +60,7 @@ impl EditorState {
}
}
/// Move the cursor down one line, clamping the column to the new line's length.
pub fn cursor_down(&mut self) {
if self.cursor_line + 1 < self.content.len() {
self.cursor_line += 1;
@@ -54,6 +70,7 @@ impl EditorState {
);
}
/// Insert a character at the cursor and advance the cursor past it.
pub fn insert_char(&mut self, c: char) {
self.save_undo();
if let Some(line) = self.content.get_mut(self.cursor_line) {
@@ -62,6 +79,11 @@ impl EditorState {
}
}
/// Delete the character before the cursor (backspace).
///
/// Flow: if not at column 0, remove the preceding char on this line →
/// otherwise (start of line, not the first line) merge this line into
/// the previous one, joining at the old line's end.
pub fn delete_left(&mut self) {
self.save_undo();
if let Some(line) = self.content.get_mut(self.cursor_line) {
@@ -78,11 +100,18 @@ impl EditorState {
}
}
/// Join all lines with `\n` into the full file contents, for saving.
pub fn as_string(&self) -> String {
self.content.join("\n")
}
}
/// Feed a chunk of typed text into the active editor, translating newlines
/// and tabs into editor operations.
///
/// Flow: no-op if no editor is open → for each char: `\n`/`\r` inserts a
/// line and moves down, `\t` inserts two spaces, everything else inserts
/// the char directly → mark state dirty.
pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
let editor = &mut state.misc.editor;
if editor.is_none() {
@@ -108,6 +137,7 @@ pub fn handle_editor_input(state: &mut AppStateRest, text: String) {
state.dirty = true;
}
/// Close the editor overlay without saving, clearing editor state.
pub fn handle_editor_dismiss(state: &mut AppStateRest) {
state.misc.editor = None;
state.misc.overlay = Overlay::None;
+12
View File
@@ -1,3 +1,6 @@
//! Effort mode: cycles the agent's reasoning effort level, which scales the
//! LLM's temperature and max_tokens for subsequent turns.
use crate::app::state::rest::AppStateRest;
pub const EFFORT_LEVELS: &[&str] = &["low", "medium", "high", "xhigh", "max"];
@@ -17,15 +20,24 @@ pub fn generation_params(level: usize, base_max_tokens: u32) -> (f32, u32) {
(temperature, max_tokens.max(256))
}
/// Return the current effort level index, clamped to a valid `EFFORT_LEVELS` slot.
///
/// Why: clamping guards against a stale/out-of-range value in loaded state
/// (e.g. after `EFFORT_LEVELS` shrinks between versions).
pub fn current_effort(state: &AppStateRest) -> usize {
state.misc.effort_level.min(EFFORT_LEVELS.len() - 1)
}
/// Return the current effort level's display name (e.g. "medium").
pub fn current_effort_str(state: &AppStateRest) -> &'static str {
let idx = current_effort(state);
EFFORT_LEVELS[idx]
}
/// Advance to the next effort level, wrapping around, and toast the new value.
///
/// Flow: compute `(current + 1) % len` → store it → push an info toast with
/// the new level's label → mark state dirty.
pub fn cycle_effort(state: &mut AppStateRest) {
let current = current_effort(state);
state.misc.effort_level = (current + 1) % EFFORT_LEVELS.len();
+8
View File
@@ -1,3 +1,5 @@
//! Help mode: static help text and the action that opens/closes the help overlay.
use crate::app::runtime::actions::Action;
use crate::app::state::types::Overlay;
@@ -27,6 +29,12 @@ Slash commands:
/lesson import Import lessons
/clear Clear transcript";
/// Route an incoming action while the help overlay is open.
///
/// Flow: `CloseOverlay` passes through unchanged; any other action is
/// treated as "open help" (idempotent — re-opens the overlay it's already on).
///
/// Return: the `Action` to actually dispatch.
pub fn handle_help_action(action: &Action) -> Action {
match action {
Action::CloseOverlay => Action::CloseOverlay,
+3
View File
@@ -1,5 +1,8 @@
//! Key input mode: raw text capture overlay used for one-off key/text prompts.
use crate::app::state::rest::AppStateRest;
/// Replace the input buffer with the given text and mark state dirty.
pub fn handle_key_text(state: &mut AppStateRest, text: String) {
state.input.buffer = text;
state.dirty = true;
+3
View File
@@ -1,3 +1,5 @@
//! Loading mode: transient overlay shown while waiting on an async operation.
use crate::app::state::rest::AppStateRest;
pub const LOADING_MESSAGES: &[&str] = &[
@@ -7,6 +9,7 @@ pub const LOADING_MESSAGES: &[&str] = &[
"almost done...",
];
/// Mark state dirty to force a re-render (e.g. to advance the loading spinner/message).
pub fn resolve_loading(state: &mut AppStateRest) {
state.dirty = true;
}
+6
View File
@@ -1,5 +1,11 @@
//! MCP mode: overlay for connecting to a configured MCP server.
use crate::app::state::rest::AppStateRest;
/// Placeholder entry point for connecting to an MCP server by name.
///
/// Why: not yet wired to `McpManager::connect_stdio` — currently just
/// marks state dirty so the overlay re-renders.
pub fn connect_mcp(state: &mut AppStateRest, server_name: &str) {
let _ = server_name;
state.dirty = true;
+5
View File
@@ -1,3 +1,6 @@
//! TUI mode definitions and per-mode input/action handlers, one submodule
//! per overlay/mode (bash, editor, effort, mcp, quit confirm, rewind, etc.).
use serde::{Deserialize, Serialize};
pub mod bash;
@@ -11,6 +14,8 @@ pub mod rewind;
pub mod settings;
pub mod todo;
/// Which input/overlay mode the TUI is currently in; drives both key
/// routing (`controller/input.rs`) and rendering.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ModeKind {
Chat,
+6
View File
@@ -1,5 +1,11 @@
//! Quit-confirm mode: the "are you sure?" overlay shown before exiting.
use crate::app::runtime::actions::Action;
/// Translate the user's yes/no answer on the quit-confirm overlay into an action.
///
/// Return: `Action::ForceQuit` if confirmed, otherwise `Action::CloseOverlay`
/// to dismiss the prompt without quitting.
pub fn handle_quit_confirm(yes: bool) -> Action {
if yes {
Action::ForceQuit
+3
View File
@@ -1,3 +1,6 @@
//! Rewind mode: restores a file to a pre-edit snapshot stored in the
//! session's SQLite blob store.
use crate::app::state::rest::AppStateRest;
use sha2::Digest;
+14
View File
@@ -1,5 +1,19 @@
//! Settings-mode helper logic for the TUI settings overlay.
//!
//! Flow: exposes small mutation functions (currently just cycling the
//! internet access mode) invoked by keybindings while the settings overlay
//! is active.
use crate::model::settings::{Settings, InternetMode};
/// Advance the internet access mode to the next value in the cycle.
///
/// Flow: Off -> ReadOnly -> Full -> Off, wrapping around.
///
/// Why: used by a settings-toggle keybinding to step through modes
/// without needing a dropdown/menu.
///
/// Return: nothing; mutates `settings.internet_mode` in place.
pub fn cycle_internet_mode(settings: &mut Settings) {
settings.internet_mode = match settings.internet_mode {
InternetMode::Off => InternetMode::ReadOnly,
+13
View File
@@ -1,6 +1,19 @@
//! Todo-mode helper logic for the TUI todo-list overlay.
//!
//! Flow: exposes the toggle handler invoked by a keybinding to show/hide
//! the todo overlay.
use crate::app::state::rest::AppStateRest;
use crate::app::state::types::Overlay;
/// Toggle the todo-list overlay open or closed.
///
/// Flow: if the todo overlay is currently shown, hide it (set to `Overlay::None`);
/// otherwise show it.
///
/// Why: marks state dirty so the TUI re-renders on the next frame.
///
/// Return: nothing; mutates `state.misc.overlay` and `state.dirty` in place.
pub fn handle_todo_toggle(state: &mut AppStateRest) {
if state.misc.overlay == Overlay::Todo {
state.misc.overlay = Overlay::None;
+20
View File
@@ -1,6 +1,21 @@
//! Workflow-mode helper logic for the TUI workflow overlay.
//!
//! Flow: exposes a dismiss handler invoked by a keybinding to close the
//! workflow overlay, and a status query used elsewhere to check whether
//! it is currently showing.
use crate::app::state::rest::AppStateRest;
use crate::app::state::types::Overlay;
/// Close the workflow overlay if it is the currently active overlay.
///
/// Flow: check `state.misc.overlay == Overlay::Workflow`, reset to `Overlay::None`
/// if so, then mark state dirty regardless.
///
/// Why: no-ops safely if another overlay is showing, so it can be called
/// unconditionally from a dismiss keybinding.
///
/// Return: nothing; mutates `state.misc.overlay` and `state.dirty` in place.
pub fn handle_workflow_dismiss(state: &mut AppStateRest) {
if state.misc.overlay == Overlay::Workflow {
state.misc.overlay = Overlay::None;
@@ -8,6 +23,11 @@ pub fn handle_workflow_dismiss(state: &mut AppStateRest) {
state.dirty = true;
}
/// Report whether the workflow overlay is currently displayed.
///
/// Flow: compare `state.misc.overlay` against `Overlay::Workflow`.
///
/// Return: `"active"` if the workflow overlay is shown, `"idle"` otherwise.
pub fn workflow_status(state: &AppStateRest) -> &str {
if state.misc.overlay == Overlay::Workflow {
"active"