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
+7 -1
View File
@@ -5,6 +5,7 @@ use anyhow::{Result, anyhow};
use super::super::Tool;
use super::super::ToolCtx;
use super::super::resolve_path;
use super::super::check_graduated_checks;
use super::helpers::arg_str;
pub struct Edit;
@@ -55,6 +56,7 @@ impl Tool for Edit {
if reason.trim().is_empty() {
anyhow::bail!("reason must be a non-empty string");
}
let check_matches = check_graduated_checks(&rel, &new_str, &ctx.graduated_checks);
let replace_all = args.get("replace_all").and_then(|v| v.as_bool()).unwrap_or(false);
let path: PathBuf = resolve_path(&ctx.workspaces, &rel)?;
if !path.exists() {
@@ -89,6 +91,10 @@ impl Tool for Edit {
} else {
content.len() - new_content.len()
};
Ok(format!("edited {} ({} byte delta)", rel, bytes_diff as isize))
if check_matches.is_empty() {
Ok(format!("edited {} ({} byte delta)", rel, bytes_diff as isize))
} else {
Ok(format!("edited {} ({} byte delta). Graduated checks matched: {}", rel, bytes_diff as isize, check_matches.join(", ")))
}
}
}
+7 -1
View File
@@ -4,6 +4,7 @@ use anyhow::{Result, anyhow};
use super::super::Tool;
use super::super::ToolCtx;
use super::super::resolve_path;
use super::super::check_graduated_checks;
use super::helpers::arg_str;
pub struct Write;
@@ -45,6 +46,7 @@ impl Tool for Write {
if reason.trim().is_empty() {
anyhow::bail!("reason must be a non-empty string");
}
let check_matches = check_graduated_checks(&rel, &content, &ctx.graduated_checks);
let path = resolve_path(&ctx.workspaces, &rel)?;
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
@@ -52,6 +54,10 @@ impl Tool for Write {
}
fs::write(&path, &content)
.map_err(|e| anyhow!("failed to write '{}': {}", rel, e))?;
Ok(format!("wrote {} bytes to {}", content.len(), rel))
if check_matches.is_empty() {
Ok(format!("wrote {} bytes to {}", content.len(), rel))
} else {
Ok(format!("wrote {} bytes to {}. Graduated checks matched: {}", content.len(), rel, check_matches.join(", ")))
}
}
}