feat: add lesson export and import functionality

- Implemented `LessonExport` and `LessonImport` actions in the action module.
- Added corresponding command parsing for lesson export and import.
- Created functions to handle lesson export and import in the memory module.
- Updated state management to reflect changes after lesson operations.
- Introduced deferred operations for handling asynchronous tasks in the event loop.
- Enhanced the tool execution context to include graduated checks for file operations.
- Added OAuth support with PKCE for secure authorization flows.
- Implemented a loopback server for handling OAuth redirects.
- Refactored various modules to improve code organization and maintainability.
This commit is contained in:
asepharyana
2026-07-11 18:23:01 +07:00
parent cc03bd79b6
commit c1ad206a00
49 changed files with 1088 additions and 12 deletions
+52
View File
@@ -0,0 +1,52 @@
use crate::app::mode::ModeKind;
#[derive(Debug, Clone, PartialEq)]
pub enum Command {
Help,
Quit,
Resume,
LessonCreate(String),
LessonExport(String),
LessonImport(String),
Mode(ModeKind),
Clear,
Save,
LessonList,
Unknown(String),
}
pub fn parse_command(text: &str) -> Command {
let text = text.trim();
if !text.starts_with('/') {
return Command::Unknown(text.to_string());
}
let parts: Vec<&str> = text.splitn(3, ' ').collect();
let cmd = parts[0];
let arg1 = parts.get(1).copied().unwrap_or("");
let arg2 = parts.get(2).copied().unwrap_or("");
match cmd {
"/help" => Command::Help,
"/quit" => Command::Quit,
"/resume" => Command::Resume,
"/clear" => Command::Clear,
"/save" => Command::Save,
"/mode" => {
let mode = match arg1 {
"chat" | "c" => ModeKind::Chat,
"agents" | "a" => ModeKind::Agents,
"bash" | "b" => ModeKind::Bash,
"workflow" | "w" => ModeKind::Workflow,
"help" | "h" => ModeKind::Help,
"settings" | "s" => ModeKind::Settings,
_ => return Command::Unknown(format!("unknown mode: {}", arg1)),
};
Command::Mode(mode)
}
"/lesson" if arg1 == "export" && !arg2.is_empty() => Command::LessonExport(arg2.to_string()),
"/lesson" if arg1 == "import" && !arg2.is_empty() => Command::LessonImport(arg2.to_string()),
"/lesson" if arg1 == "list" || arg1 == "ls" => Command::LessonList,
"/lesson" if !arg1.is_empty() => Command::LessonCreate(arg1.to_string()),
"/lesson" => Command::LessonList,
_ => Command::Unknown(cmd.to_string()),
}
}
+3 -1
View File
@@ -1,8 +1,10 @@
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use crate::app::runtime::actions::Action;
use crate::app::runtime::commands::apply_command;
use crate::app::state::rest::AppStateRest;
use crate::app::state::types::Overlay;
use crate::controller::command::parse_command;
pub fn handle_key(key: KeyEvent, state: &AppStateRest) -> Vec<Action> {
match key.code {
@@ -18,7 +20,7 @@ pub fn handle_key(key: KeyEvent, state: &AppStateRest) -> Vec<Action> {
}
let text = state.input.buffer.clone();
if text.starts_with('/') {
return Vec::new();
return apply_command(parse_command(&text));
}
vec![Action::SubmitInput(text)]
}