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:
+7
-1
@@ -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(", ")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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(", ")))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,13 @@ pub trait Tool: Send + Sync {
|
||||
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String>;
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GraduatedCheck {
|
||||
pub name: String,
|
||||
pub pattern: String,
|
||||
pub rule: String,
|
||||
}
|
||||
|
||||
pub struct ToolCtx {
|
||||
pub workspaces: Vec<PathBuf>,
|
||||
pub session_dir: PathBuf,
|
||||
@@ -30,6 +37,17 @@ pub struct ToolCtx {
|
||||
pub dir_cache: std::sync::Arc<tokio::sync::RwLock<super::app::state::misc::DirCache>>,
|
||||
pub internet_mode: super::model::settings::InternetMode,
|
||||
pub origin: crate::app::state::types::Origin,
|
||||
pub graduated_checks: Vec<GraduatedCheck>,
|
||||
}
|
||||
|
||||
pub fn check_graduated_checks(path: &str, content: &str, checks: &[GraduatedCheck]) -> Vec<String> {
|
||||
let mut matches = Vec::new();
|
||||
for check in checks {
|
||||
if path.contains(&check.pattern) || content.contains(&check.rule) {
|
||||
matches.push(check.name.clone());
|
||||
}
|
||||
}
|
||||
matches
|
||||
}
|
||||
|
||||
impl ToolCtx {
|
||||
@@ -47,6 +65,7 @@ pub struct ToolCtxBuilder {
|
||||
pub dir_cache: std::sync::Arc<tokio::sync::RwLock<super::app::state::misc::DirCache>>,
|
||||
pub internet_mode: super::model::settings::InternetMode,
|
||||
pub origin: crate::app::state::types::Origin,
|
||||
pub graduated_checks: Vec<GraduatedCheck>,
|
||||
}
|
||||
|
||||
impl Default for ToolCtxBuilder {
|
||||
@@ -60,6 +79,7 @@ impl Default for ToolCtxBuilder {
|
||||
dir_cache: std::sync::Arc::new(tokio::sync::RwLock::new(super::app::state::misc::DirCache::new())),
|
||||
internet_mode: super::model::settings::InternetMode::Off,
|
||||
origin: crate::app::state::types::Origin::Main,
|
||||
graduated_checks: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -72,6 +92,8 @@ impl ToolCtxBuilder {
|
||||
pub fn worktrees_dir(mut self, v: PathBuf) -> Self { self.worktrees_dir = v; self }
|
||||
pub fn internet_mode(mut self, v: super::model::settings::InternetMode) -> Self { self.internet_mode = v; self }
|
||||
pub fn origin(mut self, v: crate::app::state::types::Origin) -> Self { self.origin = v; self }
|
||||
#[expect(dead_code)]
|
||||
pub fn graduated_checks(mut self, v: Vec<GraduatedCheck>) -> Self { self.graduated_checks = v; self }
|
||||
pub fn build(self) -> ToolCtx {
|
||||
ToolCtx {
|
||||
workspaces: self.workspaces,
|
||||
@@ -82,6 +104,7 @@ impl ToolCtxBuilder {
|
||||
dir_cache: self.dir_cache,
|
||||
internet_mode: self.internet_mode,
|
||||
origin: self.origin,
|
||||
graduated_checks: self.graduated_checks,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user