diff --git a/frontend/.vscode/tasks.json b/frontend/.vscode/tasks.json
index e6d25b9352..e902e3dfd5 100644
--- a/frontend/.vscode/tasks.json
+++ b/frontend/.vscode/tasks.json
@@ -70,6 +70,27 @@
 			"options": {
 				"cwd": "${workspaceFolder}"
 			},
+		},
+		{
+			"label": "Clean",
+			"type": "shell",
+			"command": "sh ./scripts/clean.sh",
+			"windows": {
+				"options": {
+					"shell": {
+						"executable": "cmd.exe",
+						"args": [
+							"/d",
+							"/c",
+							".\\scripts\\clean.cmd"
+						]
+					}
+				}
+			},
+			"group": "build",
+			"options": {
+				"cwd": "${workspaceFolder}"
+			},
 		}
 	]
 }
\ No newline at end of file
diff --git a/frontend/app_flowy/.vscode/launch.json b/frontend/app_flowy/.vscode/launch.json
index e0d00d7407..c7236062fd 100644
--- a/frontend/app_flowy/.vscode/launch.json
+++ b/frontend/app_flowy/.vscode/launch.json
@@ -40,12 +40,5 @@
             "preLaunchTask": "Generate Language Files",
             "cwd": "${workspaceRoot}"
         },
-        {
-            "name": "Clean",
-            "request": "launch",
-            "type": "dart",
-            "preLaunchTask": "Clean",
-            "cwd": "${workspaceRoot}"
-        }
     ]
 }
\ No newline at end of file
diff --git a/frontend/app_flowy/.vscode/tasks.json b/frontend/app_flowy/.vscode/tasks.json
index 8d012c40e5..fcf7a23250 100644
--- a/frontend/app_flowy/.vscode/tasks.json
+++ b/frontend/app_flowy/.vscode/tasks.json
@@ -72,7 +72,7 @@
 			},
 		},
 		{
-			"label": "Clean FlowySDK",
+			"label": "Clean",
 			"type": "shell",
 			"command": "sh ./scripts/clean.sh",
 			"windows": {
@@ -87,7 +87,10 @@
 					}
 				}
 			},
-			"group": "build",
+			"group": {
+				"kind": "build",
+				"isDefault": true,
+			},
 			"options": {
 				"cwd": "${workspaceFolder}/../"
 			},
diff --git a/frontend/rust-lib/dart-ffi/Cargo.toml b/frontend/rust-lib/dart-ffi/Cargo.toml
index b84944b88f..96755db623 100644
--- a/frontend/rust-lib/dart-ffi/Cargo.toml
+++ b/frontend/rust-lib/dart-ffi/Cargo.toml
@@ -36,4 +36,4 @@ http_server = ["flowy-sdk/http_server", "flowy-sdk/use_bunyan"]
 #use_protobuf= ["protobuf"]
 
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["pb_gen", "dart"] }
\ No newline at end of file
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "dart"] }
\ No newline at end of file
diff --git a/frontend/rust-lib/dart-notify/Cargo.toml b/frontend/rust-lib/dart-notify/Cargo.toml
index 0d0cddf676..4f55804e53 100644
--- a/frontend/rust-lib/dart-notify/Cargo.toml
+++ b/frontend/rust-lib/dart-notify/Cargo.toml
@@ -19,4 +19,4 @@ lib-dispatch = {path = "../lib-dispatch" }
 dart = ["lib-infra/dart"]
 
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["pb_gen"] }
\ No newline at end of file
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen"] }
\ No newline at end of file
diff --git a/frontend/rust-lib/flowy-error/Cargo.toml b/frontend/rust-lib/flowy-error/Cargo.toml
index cf2a48e2e2..b7a816b026 100644
--- a/frontend/rust-lib/flowy-error/Cargo.toml
+++ b/frontend/rust-lib/flowy-error/Cargo.toml
@@ -30,4 +30,4 @@ db = ["flowy-database", "lib-sqlite", "r2d2"]
 dart = ["flowy-error-code/dart", "lib-infra/dart"]
 
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["pb_gen"] }
\ No newline at end of file
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen"] }
\ No newline at end of file
diff --git a/frontend/rust-lib/flowy-folder/Cargo.toml b/frontend/rust-lib/flowy-folder/Cargo.toml
index f51c3fb4eb..1fc78f4197 100644
--- a/frontend/rust-lib/flowy-folder/Cargo.toml
+++ b/frontend/rust-lib/flowy-folder/Cargo.toml
@@ -56,4 +56,4 @@ flowy_unit_test = ["lib-ot/flowy_unit_test", "flowy-sync/flowy_unit_test"]
 dart = ["lib-infra/dart", "flowy-folder/dart", "flowy-folder/dart",]
 
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["pb_gen", "proto_gen"] }
\ No newline at end of file
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen", "proto_gen"] }
\ No newline at end of file
diff --git a/frontend/rust-lib/flowy-net/Cargo.toml b/frontend/rust-lib/flowy-net/Cargo.toml
index 46537f8f4b..34e4942b38 100644
--- a/frontend/rust-lib/flowy-net/Cargo.toml
+++ b/frontend/rust-lib/flowy-net/Cargo.toml
@@ -50,4 +50,4 @@ dart = [
 ]
 
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["pb_gen"] }
\ No newline at end of file
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen"] }
\ No newline at end of file
diff --git a/frontend/rust-lib/flowy-user/Cargo.toml b/frontend/rust-lib/flowy-user/Cargo.toml
index f37c35c4be..307c59d62c 100644
--- a/frontend/rust-lib/flowy-user/Cargo.toml
+++ b/frontend/rust-lib/flowy-user/Cargo.toml
@@ -48,4 +48,4 @@ http_server = []
 dart = ["lib-infra/dart"]
 
 [build-dependencies]
-lib-infra = { path = "../../../shared-lib/lib-infra", features = ["pb_gen"] }
\ No newline at end of file
+lib-infra = { path = "../../../shared-lib/lib-infra", features = ["protobuf_file_gen"] }
\ No newline at end of file
diff --git a/frontend/scripts/clean.cmd b/frontend/scripts/clean.cmd
index b503f675b9..5ee373268c 100644
--- a/frontend/scripts/clean.cmd
+++ b/frontend/scripts/clean.cmd
@@ -4,4 +4,4 @@ cargo clean
 cd ../../shared-lib
 cargo clean
 
-rmdir /s/q lib-infra/.cache
\ No newline at end of file
+rmdir /s/q "lib-infra/.cache"
\ No newline at end of file
diff --git a/frontend/scripts/makefile/protobuf.toml b/frontend/scripts/makefile/protobuf.toml
index 4d594b3532..82084bd3e9 100644
--- a/frontend/scripts/makefile/protobuf.toml
+++ b/frontend/scripts/makefile/protobuf.toml
@@ -1,5 +1,10 @@
 
 [tasks.install_protobuf]
+mac_alias = "install-protobuf"
+windows_alias = "install-protobuf-windows"
+linux_alias = "install-protobuf"
+
+[tasks.install-protobuf]
 condition_script = [
     """
     if ! command -v protoc-gen-dart
@@ -13,21 +18,8 @@ condition_script = [
 ]
 run_task = { name = ["install_protobuf_compiler"] }
 
-[tasks.install_protobuf_compiler]
-script = """
-echo "Install protoc_plugin (Dart)"
-dart pub global activate protoc_plugin
-"""
-script_runner = "@shell"
 
-[tasks.install_protobuf_compiler.linux]
-script = """
-echo "Install protoc_plugin (Dart)"
-dart pub global activate protoc_plugin
-"""
-script_runner = "@shell"
-
-[tasks.install_protobuf_compiler.windows]
+[tasks.install-protobuf-windows]
 script = """
 ret = which dart
 if is_empty ${ret}
@@ -44,6 +36,22 @@ end
 """
 script_runner = "@duckscript"
 
+
+[tasks.install_protobuf_compiler]
+script = """
+echo "Install protoc_plugin (Dart)"
+dart pub global activate protoc_plugin
+"""
+script_runner = "@shell"
+
+[tasks.install_protobuf_compiler.linux]
+script = """
+echo "Install protoc_plugin (Dart)"
+dart pub global activate protoc_plugin
+"""
+script_runner = "@shell"
+
+
 [tasks.check_protoc_cmd]
 script = [
     """
diff --git a/shared-lib/flowy-collaboration/Cargo.toml b/shared-lib/flowy-collaboration/Cargo.toml
index c758d9d855..3afb107b70 100644
--- a/shared-lib/flowy-collaboration/Cargo.toml
+++ b/shared-lib/flowy-collaboration/Cargo.toml
@@ -29,7 +29,7 @@ futures = "0.3.15"
 async-stream = "0.3.2"
 
 [build-dependencies]
-lib-infra = { path = "../lib-infra", features = ["pb_gen"] }
+lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }
 
 [features]
 dart = ["lib-infra/dart"]
\ No newline at end of file
diff --git a/shared-lib/flowy-error-code/Cargo.toml b/shared-lib/flowy-error-code/Cargo.toml
index 691230c2eb..4da6b7c0a1 100644
--- a/shared-lib/flowy-error-code/Cargo.toml
+++ b/shared-lib/flowy-error-code/Cargo.toml
@@ -11,7 +11,7 @@ protobuf = {version = "2.18.0"}
 derive_more = {version = "0.99", features = ["display"]}
 
 [build-dependencies]
-lib-infra = { path = "../lib-infra", features = ["pb_gen"] }
+lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }
 
 [features]
 dart = ["lib-infra/dart"]
\ No newline at end of file
diff --git a/shared-lib/flowy-folder-data-model/Cargo.toml b/shared-lib/flowy-folder-data-model/Cargo.toml
index 44cd3412b3..3e8764b1bc 100644
--- a/shared-lib/flowy-folder-data-model/Cargo.toml
+++ b/shared-lib/flowy-folder-data-model/Cargo.toml
@@ -21,7 +21,7 @@ serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 
 [build-dependencies]
-lib-infra = { path = "../lib-infra", features = ["pb_gen"] }
+lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }
 
 [features]
 default = []
diff --git a/shared-lib/flowy-user-data-model/Cargo.toml b/shared-lib/flowy-user-data-model/Cargo.toml
index 561764adea..6911aaecaa 100644
--- a/shared-lib/flowy-user-data-model/Cargo.toml
+++ b/shared-lib/flowy-user-data-model/Cargo.toml
@@ -19,7 +19,7 @@ fancy-regex = "0.5.0"
 lazy_static = "1.4"
 
 [build-dependencies]
-lib-infra = { path = "../lib-infra", features = ["pb_gen"] }
+lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }
 
 [dev-dependencies]
 quickcheck = "0.9.2"
diff --git a/shared-lib/lib-infra/Cargo.toml b/shared-lib/lib-infra/Cargo.toml
index fc2338ec0d..7644241ec3 100644
--- a/shared-lib/lib-infra/Cargo.toml
+++ b/shared-lib/lib-infra/Cargo.toml
@@ -47,6 +47,6 @@ proto_gen = [
     "console",
     "toml"
 ]
-pb_gen = ["cmd_lib", "protoc-rust", "walkdir", "protoc-bin-vendored",]
+protobuf_file_gen = ["cmd_lib", "protoc-rust", "walkdir", "protoc-bin-vendored",]
 dart_event = ["walkdir", "flowy-ast", "tera", "syn"]
 dart = ["proto_gen", "dart_event"]
\ No newline at end of file
diff --git a/shared-lib/lib-infra/src/code_gen/dart_event/dart_event.rs b/shared-lib/lib-infra/src/code_gen/dart_event/dart_event.rs
index afb089458b..eea9c1160b 100644
--- a/shared-lib/lib-infra/src/code_gen/dart_event/dart_event.rs
+++ b/shared-lib/lib-infra/src/code_gen/dart_event/dart_event.rs
@@ -1,10 +1,10 @@
 use super::event_template::*;
 use crate::code_gen::flowy_toml::{parse_crate_config_from, CrateConfig};
-use crate::code_gen::util::{cache_dir, is_crate_dir, is_hidden, read_file};
+use crate::code_gen::util::{is_crate_dir, is_hidden, path_string_with_component, read_file};
 use flowy_ast::{event_ast::*, *};
 use std::fs::File;
 use std::io::Write;
-use std::path::Path;
+use std::path::PathBuf;
 use syn::Item;
 use walkdir::WalkDir;
 
@@ -33,18 +33,22 @@ pub fn gen(crate_name: &str) {
         }
     }
 
-    let dart_event_folder = format!(
-        "{}/{}/lib/dispatch/dart_event/{}",
-        std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap(),
-        std::env::var("FLUTTER_FLOWY_SDK_PATH").unwrap(),
-        crate_name
-    );
+    let dart_event_folder: PathBuf = [
+        &std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap(),
+        &std::env::var("FLUTTER_FLOWY_SDK_PATH").unwrap(),
+        "lib",
+        "dispatch",
+        "dart_event",
+        crate_name,
+    ]
+    .iter()
+    .collect();
 
-    if !Path::new(&dart_event_folder).exists() {
-        std::fs::create_dir_all(&dart_event_folder).unwrap();
+    if !dart_event_folder.as_path().exists() {
+        std::fs::create_dir_all(dart_event_folder.as_path()).unwrap();
     }
 
-    let dart_event_file_path = format!("{}/dart_event.dart", dart_event_folder);
+    let dart_event_file_path = path_string_with_component(&dart_event_folder, vec!["dart_event.dart"]);
     println!("cargo:rerun-if-changed={}", dart_event_file_path);
 
     match std::fs::OpenOptions::new()
@@ -69,39 +73,9 @@ const DART_IMPORTED: &str = r#"
 part of '../../dispatch.dart';
 "#;
 
-pub fn write_dart_event_file(file_path: &str) {
-    let cache_dir = cache_dir();
-    let mut content = DART_IMPORTED.to_owned();
-    for path in WalkDir::new(cache_dir)
-        .into_iter()
-        .filter_map(|e| e.ok())
-        .filter(|e| e.path().file_stem().unwrap().to_str().unwrap() == "dart_event")
-        .map(|e| e.path().to_str().unwrap().to_string())
-    {
-        let file_content = read_file(path.as_ref()).unwrap();
-        content.push_str(&file_content);
-    }
-
-    match std::fs::OpenOptions::new()
-        .create(true)
-        .write(true)
-        .append(false)
-        .truncate(true)
-        .open(&file_path)
-    {
-        Ok(ref mut file) => {
-            file.write_all(content.as_bytes()).unwrap();
-            File::flush(file).unwrap();
-        }
-        Err(err) => {
-            panic!("Failed to write dart event file: {}", err);
-        }
-    }
-}
-
 #[derive(Debug)]
 pub struct DartEventCrate {
-    crate_path: String,
+    crate_path: PathBuf,
     event_files: Vec<String>,
 }
 
@@ -135,7 +109,8 @@ pub fn parse_event_crate(event_crate: &DartEventCrate) -> Vec<EventASTContext> {
         .event_files
         .iter()
         .map(|event_file| {
-            let file_path = format!("{}/{}", event_crate.crate_path, event_file);
+            let file_path = path_string_with_component(&event_crate.crate_path, vec![event_file.as_str()]);
+
             let file_content = read_file(file_path.as_ref()).unwrap();
             let ast = syn::parse_file(file_content.as_ref()).expect("Unable to parse file");
             ast.items
diff --git a/shared-lib/lib-infra/src/code_gen/flowy_toml.rs b/shared-lib/lib-infra/src/code_gen/flowy_toml.rs
index 954731ee02..32bdc8627f 100644
--- a/shared-lib/lib-infra/src/code_gen/flowy_toml.rs
+++ b/shared-lib/lib-infra/src/code_gen/flowy_toml.rs
@@ -1,4 +1,6 @@
+use crate::code_gen::util::path_buf_with_component;
 use std::fs;
+use std::path::{Path, PathBuf};
 
 #[derive(serde::Deserialize)]
 pub struct FlowyConfig {
@@ -7,7 +9,7 @@ pub struct FlowyConfig {
 }
 
 impl FlowyConfig {
-    pub fn from_toml_file(path: &str) -> Self {
+    pub fn from_toml_file(path: &Path) -> Self {
         let content = fs::read_to_string(path).unwrap();
         let config: FlowyConfig = toml::from_str(content.as_ref()).unwrap();
         config
@@ -15,34 +17,32 @@ impl FlowyConfig {
 }
 
 pub struct CrateConfig {
-    pub crate_path: String,
+    pub crate_path: PathBuf,
     pub folder_name: String,
     pub flowy_config: FlowyConfig,
 }
 
 impl CrateConfig {
-    pub fn proto_paths(&self) -> Vec<String> {
+    pub fn proto_paths(&self) -> Vec<PathBuf> {
         let proto_paths = self
             .flowy_config
             .proto_crates
             .iter()
-            .map(|name| format!("{}/{}", self.crate_path, name))
-            .collect::<Vec<String>>();
+            .map(|name| path_buf_with_component(&self.crate_path, vec![&name]))
+            .collect::<Vec<PathBuf>>();
         proto_paths
     }
 }
 
 pub fn parse_crate_config_from(entry: &walkdir::DirEntry) -> Option<CrateConfig> {
-    let path = entry.path().parent().unwrap();
-    let crate_path = path.to_str().unwrap().to_string();
-    let folder_name = path.file_stem().unwrap().to_str().unwrap().to_string();
-    let config_path = format!("{}/Flowy.toml", crate_path);
-
-    if !std::path::Path::new(&config_path).exists() {
+    let mut config_path = entry.path().parent().unwrap().to_path_buf();
+    config_path.push("Flowy.toml");
+    if !config_path.as_path().exists() {
         return None;
     }
-
-    let flowy_config = FlowyConfig::from_toml_file(config_path.as_ref());
+    let crate_path = entry.path().parent().unwrap().to_path_buf();
+    let flowy_config = FlowyConfig::from_toml_file(config_path.as_path());
+    let folder_name = crate_path.file_stem().unwrap().to_str().unwrap().to_string();
 
     Some(CrateConfig {
         crate_path,
diff --git a/shared-lib/lib-infra/src/code_gen/mod.rs b/shared-lib/lib-infra/src/code_gen/mod.rs
index c2651bcbcf..d06699b862 100644
--- a/shared-lib/lib-infra/src/code_gen/mod.rs
+++ b/shared-lib/lib-infra/src/code_gen/mod.rs
@@ -1,13 +1,13 @@
-#[cfg(feature = "pb_gen")]
+#[cfg(feature = "protobuf_file_gen")]
 pub mod protobuf_file;
 
 #[cfg(feature = "dart_event")]
 pub mod dart_event;
 
-#[cfg(any(feature = "pb_gen", feature = "dart_event"))]
+#[cfg(any(feature = "protobuf_file_gen", feature = "dart_event"))]
 mod flowy_toml;
 
-#[cfg(any(feature = "pb_gen", feature = "dart_event"))]
+#[cfg(any(feature = "protobuf_file_gen", feature = "dart_event"))]
 pub mod util;
 
 #[derive(serde::Serialize, serde::Deserialize)]
diff --git a/shared-lib/lib-infra/src/code_gen/protobuf_file/ast.rs b/shared-lib/lib-infra/src/code_gen/protobuf_file/ast.rs
index 8f7f1e052b..599ec9b20e 100644
--- a/shared-lib/lib-infra/src/code_gen/protobuf_file/ast.rs
+++ b/shared-lib/lib-infra/src/code_gen/protobuf_file/ast.rs
@@ -8,6 +8,7 @@ use crate::code_gen::util::*;
 use fancy_regex::Regex;
 use flowy_ast::*;
 use lazy_static::lazy_static;
+use std::path::PathBuf;
 use std::{fs::File, io::Read, path::Path};
 use syn::Item;
 use walkdir::WalkDir;
@@ -30,7 +31,7 @@ pub fn parse_crate_protobuf(crate_paths: Vec<String>) -> Vec<ProtobufCrateContex
         .collect::<Vec<ProtobufCrateContext>>()
 }
 
-fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<ProtoFile> {
+fn parse_files_protobuf(proto_crate_path: &PathBuf, proto_output_dir: &PathBuf) -> Vec<ProtoFile> {
     let mut gen_proto_vec: Vec<ProtoFile> = vec![];
     // file_stem https://doc.rust-lang.org/std/path/struct.Path.html#method.file_stem
     for (path, file_name) in WalkDir::new(proto_crate_path)
@@ -52,7 +53,8 @@ fn parse_files_protobuf(proto_crate_path: &str, proto_output_dir: &str) -> Vec<P
         let ast = syn::parse_file(read_file(&path).unwrap().as_ref())
             .unwrap_or_else(|_| panic!("Unable to parse file at {}", path));
         let structs = get_ast_structs(&ast);
-        let proto_file_path = format!("{}/{}.proto", &proto_output_dir, &file_name);
+        let proto_file = format!("{}.proto", &file_name);
+        let proto_file_path = path_string_with_component(&proto_output_dir, vec![&proto_file]);
         let mut proto_file_content = parse_or_init_proto_file(proto_file_path.as_ref());
 
         structs.iter().for_each(|s| {
diff --git a/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs b/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs
index f35a46d1f1..fc02a0f256 100644
--- a/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs
+++ b/shared-lib/lib-infra/src/code_gen/protobuf_file/mod.rs
@@ -6,11 +6,10 @@ mod proto_gen;
 mod proto_info;
 mod template;
 
+use crate::code_gen::util::path_string_with_component;
+use log::info;
 pub use proto_gen::*;
 pub use proto_info::*;
-
-#[cfg(feature = "proto_gen")]
-use log::info;
 use std::fs::File;
 use std::io::Write;
 use std::path::PathBuf;
@@ -85,10 +84,14 @@ fn generate_dart_protobuf_files(
         return;
     }
 
-    let workspace_dir = std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap();
-    let flutter_sdk_path = std::env::var("FLUTTER_FLOWY_SDK_PATH").unwrap();
-    let output = format!("{}/{}/lib/protobuf/{}", workspace_dir, flutter_sdk_path, name);
-    if !std::path::Path::new(&output).exists() {
+    let mut output = PathBuf::new();
+    output.push(std::env::var("CARGO_MAKE_WORKING_DIRECTORY").unwrap());
+    output.push(std::env::var("FLUTTER_FLOWY_SDK_PATH").unwrap());
+    output.push("lib");
+    output.push("protobuf");
+    output.push(name);
+
+    if !output.as_path().exists() {
         std::fs::create_dir_all(&output).unwrap();
     }
     check_pb_dart_plugin();
@@ -103,7 +106,8 @@ fn generate_dart_protobuf_files(
         };
     });
 
-    let protobuf_dart = format!("{}/protobuf.dart", output);
+    let protobuf_dart = path_string_with_component(&output, vec!["protobuf.dart"]);
+
     match std::fs::OpenOptions::new()
         .create(true)
         .write(true)
@@ -128,37 +132,26 @@ fn generate_dart_protobuf_files(
     }
 }
 
-fn check_pb_compiler() {
-    assert!(run_command("command -v protoc"), "protoc was not installed correctly");
-}
-
 fn check_pb_dart_plugin() {
     if cfg!(target_os = "windows") {
-        if !run_command("command -v protoc-gen-dart") {
-            panic!("{}", format!("\n❌ The protoc-gen-dart was not installed correctly."))
-        }
+        //Command::new("cmd")
+        //    .arg("/C")
+        //    .arg(cmd)
+        //    .status()
+        //    .expect("failed to execute process");
+        //panic!("{}", format!("\n❌ The protoc-gen-dart was not installed correctly."))
     } else {
-        if !run_command("command -v protoc-gen-dart") {
+        let is_success = Command::new("sh")
+            .arg("-c")
+            .arg("command -v protoc-gen-dart")
+            .status()
+            .expect("failed to execute process")
+            .success();
+
+        if !is_success {
             panic!("{}", format!("\n❌ The protoc-gen-dart was not installed correctly. \n✅ You can fix that by adding \"{}\" to your shell's config file.(.bashrc, .bash, etc.)", "dart pub global activate protoc_plugin"))
         }
-    };
-}
-
-fn run_command(cmd: &str) -> bool {
-    let output = if cfg!(target_os = "windows") {
-        Command::new("cmd")
-            .arg("/C")
-            .arg(cmd)
-            .status()
-            .expect("failed to execute process")
-    } else {
-        Command::new("sh")
-            .arg("-c")
-            .arg(cmd)
-            .status()
-            .expect("failed to execute process")
-    };
-    output.success()
+    }
 }
 
 #[cfg(feature = "proto_gen")]
diff --git a/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_gen.rs b/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_gen.rs
index d4ff2f56bb..60795bef60 100644
--- a/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_gen.rs
+++ b/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_gen.rs
@@ -23,14 +23,16 @@ impl ProtoGenerator {
             crate_info.create_crate_mod_file();
         }
 
-        let cache = ProtoCache::from_crate_contexts(&crate_contexts);
-        let cache_str = serde_json::to_string(&cache).unwrap();
-        let cache_dir = format!("{}/{}", cache_dir(), crate_name);
-        if !Path::new(&cache_dir).exists() {
-            std::fs::create_dir_all(&cache_dir).unwrap();
+        let proto_cache = ProtoCache::from_crate_contexts(&crate_contexts);
+        let proto_cache_str = serde_json::to_string(&proto_cache).unwrap();
+
+        let crate_cache_dir = path_buf_with_component(&cache_dir(), vec![crate_name]);
+        if !crate_cache_dir.as_path().exists() {
+            std::fs::create_dir_all(&crate_cache_dir).unwrap();
         }
 
-        let protobuf_cache_path = format!("{}/proto_cache", cache_dir);
+        let protobuf_cache_path = path_string_with_component(&crate_cache_dir, vec!["proto_cache"]);
+
         match std::fs::OpenOptions::new()
             .create(true)
             .write(true)
@@ -39,7 +41,7 @@ impl ProtoGenerator {
             .open(&protobuf_cache_path)
         {
             Ok(ref mut file) => {
-                file.write_all(cache_str.as_bytes()).unwrap();
+                file.write_all(proto_cache_str.as_bytes()).unwrap();
                 File::flush(file).unwrap();
             }
             Err(_err) => {
@@ -55,7 +57,8 @@ fn write_proto_files(crate_contexts: &[ProtobufCrateContext]) {
     for context in crate_contexts {
         let dir = context.protobuf_crate.proto_output_dir();
         context.files.iter().for_each(|info| {
-            let proto_file_path = format!("{}/{}.proto", dir, &info.file_name);
+            let proto_file = format!("{}.proto", &info.file_name);
+            let proto_file_path = path_string_with_component(&dir, vec![&proto_file]);
             save_content_to_file_with_diff_prompt(&info.generated_content, proto_file_path.as_ref());
         });
     }
@@ -77,7 +80,7 @@ fn write_rust_crate_mod_file(crate_contexts: &[ProtobufCrateContext]) {
                 mod_file_content.push_str("#![cfg_attr(rustfmt, rustfmt::skip)]\n");
                 mod_file_content.push_str("// Auto-generated, do not edit\n");
                 walk_dir(
-                    context.protobuf_crate.proto_output_dir().as_ref(),
+                    context.protobuf_crate.proto_output_dir(),
                     |e| !e.file_type().is_dir(),
                     |_, name| {
                         let c = format!("\nmod {};\npub use {}::*;\n", &name, &name);
diff --git a/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_info.rs b/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_info.rs
index 2471ded9ff..6c51c5b055 100644
--- a/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_info.rs
+++ b/shared-lib/lib-infra/src/code_gen/protobuf_file/proto_info.rs
@@ -3,6 +3,8 @@ use crate::code_gen::flowy_toml::{parse_crate_config_from, CrateConfig};
 use crate::code_gen::util::*;
 use std::fs::OpenOptions;
 use std::io::Write;
+use std::path::PathBuf;
+use std::str::FromStr;
 use walkdir::WalkDir;
 
 #[derive(Debug)]
@@ -22,7 +24,7 @@ impl ProtobufCrateContext {
     pub fn create_crate_mod_file(&self) {
         // mod model;
         // pub use model::*;
-        let mod_file_path = format!("{}/mod.rs", self.protobuf_crate.protobuf_crate_name());
+        let mod_file_path = path_string_with_component(&self.protobuf_crate.protobuf_crate_name(), vec!["mod.rs"]);
         let mut content = "#![cfg_attr(rustfmt, rustfmt::skip)]\n".to_owned();
         content.push_str("// Auto-generated, do not edit\n");
         content.push_str("mod model;\npub use model::*;");
@@ -58,8 +60,8 @@ impl ProtobufCrateContext {
 #[derive(Clone, Debug)]
 pub struct ProtobufCrate {
     pub folder_name: String,
-    pub proto_paths: Vec<String>,
-    pub crate_path: String,
+    pub proto_paths: Vec<PathBuf>,
+    pub crate_path: PathBuf,
 }
 
 impl ProtobufCrate {
@@ -72,24 +74,26 @@ impl ProtobufCrate {
         }
     }
 
-    fn protobuf_crate_name(&self) -> String {
-        format!("{}/src/protobuf", self.crate_path)
+    fn protobuf_crate_name(&self) -> PathBuf {
+        path_buf_with_component(&self.crate_path, vec!["src", "protobuf"])
     }
 
-    pub fn proto_output_dir(&self) -> String {
-        let dir = format!("{}/proto", self.protobuf_crate_name());
-        create_dir_if_not_exist(dir.as_ref());
+    pub fn proto_output_dir(&self) -> PathBuf {
+        let path = self.protobuf_crate_name();
+        let dir = path_buf_with_component(&path, vec!["proto"]);
+        create_dir_if_not_exist(&dir);
         dir
     }
 
-    pub fn create_output_dir(&self) -> String {
-        let dir = format!("{}/model", self.protobuf_crate_name());
-        create_dir_if_not_exist(dir.as_ref());
+    pub fn create_output_dir(&self) -> PathBuf {
+        let path = self.protobuf_crate_name();
+        let dir = path_buf_with_component(&path, vec!["model"]);
+        create_dir_if_not_exist(&dir);
         dir
     }
 
     pub fn proto_model_mod_file(&self) -> String {
-        format!("{}/mod.rs", self.create_output_dir())
+        path_string_with_component(&self.create_output_dir(), vec!["mod.rs"])
     }
 }
 
@@ -117,26 +121,3 @@ pub fn parse_crate_info_from_path(roots: Vec<String>) -> Vec<ProtobufCrate> {
     });
     protobuf_crates
 }
-
-pub struct FlutterProtobufInfo {
-    package_path: String,
-}
-impl FlutterProtobufInfo {
-    pub fn new(root: &str) -> Self {
-        FlutterProtobufInfo {
-            package_path: root.to_owned(),
-        }
-    }
-
-    pub fn model_dir(&self) -> String {
-        let model_dir = format!("{}/protobuf", self.package_path);
-        create_dir_if_not_exist(model_dir.as_ref());
-        model_dir
-    }
-
-    #[allow(dead_code)]
-    pub fn mod_file_path(&self) -> String {
-        let mod_file_path = format!("{}/protobuf.dart", self.package_path);
-        mod_file_path
-    }
-}
diff --git a/shared-lib/lib-infra/src/code_gen/util.rs b/shared-lib/lib-infra/src/code_gen/util.rs
index b25c8b8bb8..d63232481c 100644
--- a/shared-lib/lib-infra/src/code_gen/util.rs
+++ b/shared-lib/lib-infra/src/code_gen/util.rs
@@ -1,9 +1,10 @@
 use console::Style;
 use similar::{ChangeTag, TextDiff};
+use std::path::{Path, PathBuf};
+use std::str::FromStr;
 use std::{
     fs::{File, OpenOptions},
     io::{Read, Write},
-    path::Path,
 };
 use tera::Tera;
 use walkdir::WalkDir;
@@ -93,14 +94,26 @@ pub fn is_hidden(entry: &walkdir::DirEntry) -> bool {
     entry.file_name().to_str().map(|s| s.starts_with('.')).unwrap_or(false)
 }
 
-pub fn create_dir_if_not_exist(dir: &str) {
-    if !std::path::Path::new(&dir).exists() {
-        std::fs::create_dir_all(&dir).unwrap();
+pub fn create_dir_if_not_exist(dir: &PathBuf) {
+    if !dir.as_path().exists() {
+        std::fs::create_dir_all(dir).unwrap();
     }
 }
 
+pub fn path_string_with_component(path: &PathBuf, components: Vec<&str>) -> String {
+    path_buf_with_component(path, components).to_str().unwrap().to_string()
+}
+
+pub fn path_buf_with_component(path: &PathBuf, components: Vec<&str>) -> PathBuf {
+    let mut path_buf = path.clone();
+    for component in components {
+        path_buf.push(component);
+    }
+    path_buf
+}
+
 #[allow(dead_code)]
-pub fn walk_dir<F1, F2>(dir: &str, filter: F2, mut path_and_name: F1)
+pub fn walk_dir<P: AsRef<Path>, F1, F2>(dir: P, filter: F2, mut path_and_name: F1)
 where
     F1: FnMut(String, String),
     F2: Fn(&walkdir::DirEntry) -> bool,
@@ -153,6 +166,8 @@ pub fn get_tera(directory: &str) -> Tera {
     }
 }
 
-pub fn cache_dir() -> String {
-    format!("{}/.cache", env!("CARGO_MANIFEST_DIR"))
+pub fn cache_dir() -> PathBuf {
+    let mut path_buf = PathBuf::from_str(env!("CARGO_MANIFEST_DIR")).unwrap();
+    path_buf.push(".cache");
+    path_buf
 }
diff --git a/shared-lib/lib-ws/Cargo.toml b/shared-lib/lib-ws/Cargo.toml
index 76668c297d..53b55fd295 100644
--- a/shared-lib/lib-ws/Cargo.toml
+++ b/shared-lib/lib-ws/Cargo.toml
@@ -28,7 +28,7 @@ parking_lot = "0.11"
 dashmap = "4.0"
 
 [build-dependencies]
-lib-infra = { path = "../lib-infra", features = ["pb_gen"] }
+lib-infra = { path = "../lib-infra", features = ["protobuf_file_gen"] }
 
 [dev-dependencies]
 tokio = {version = "1", features = ["full"]}