diff --git a/Cargo.toml b/Cargo.toml
index 5197f0b..ec83427 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -35,7 +35,7 @@ pathdiff = "0.2.3"
 serde = { version = "1.0.217", features = ["derive"], optional = true }
 serde_json = { version = "1.0.138", optional = true }
 # shulkerbox = { version = "0.1.0", default-features = false, optional = true }
-shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "e9f2b9b91d72322ec2e063ce7b83415071306468", default-features = false, optional = true }
+shulkerbox = { git = "https://github.com/moritz-hoelting/shulkerbox", rev = "7392887c127b1aae0d78d573a02332c2fa2591c8", default-features = false, optional = true }
 strsim = "0.11.1"
 strum = { version = "0.27.0", features = ["derive"] }
 thiserror = "2.0.11"
diff --git a/src/semantic/mod.rs b/src/semantic/mod.rs
index b1c9f0d..8266fb3 100644
--- a/src/semantic/mod.rs
+++ b/src/semantic/mod.rs
@@ -162,16 +162,14 @@ impl Function {
         let child_scope = SemanticScope::with_parent(scope);
 
         if let Some(parameters) = self.parameters().as_ref().map(ConnectedList::elements) {
-            if let Some(incompatible) = self
-                .annotations()
-                .iter()
-                .find(|a| ["tick", "load"].contains(&a.assignment().identifier.span.str()))
-            {
+            if let Some(incompatible) = self.annotations().iter().find(|a| {
+                ["tick", "load", "uninstall"].contains(&a.assignment().identifier.span.str())
+            }) {
                 let err =
                     error::Error::IncompatibleFunctionAnnotation(IncompatibleFunctionAnnotation {
                         span: incompatible.assignment().identifier.span.clone(),
                         reason:
-                            "functions with the `tick` or `load` annotation cannot have parameters"
+                            "functions with the `tick`, `load` or `uninstall` annotation cannot have parameters"
                                 .to_string(),
                     });
                 handler.receive(err.clone());
diff --git a/src/transpile/expression.rs b/src/transpile/expression.rs
index 9b8fd5a..729ff83 100644
--- a/src/transpile/expression.rs
+++ b/src/transpile/expression.rs
@@ -1722,10 +1722,16 @@ impl Transpiler {
                 .to_hex_lowercase()
                 .split_off(16)
             })
-            .collect();
+            .collect::<Vec<_>>();
 
         self.temp_counter = self.temp_counter.wrapping_add(amount);
 
+        self.temp_data_storage_locations.extend(
+            paths
+                .iter()
+                .map(|path| (storage_name.clone(), path.clone())),
+        );
+
         (storage_name, paths)
     }
 }
diff --git a/src/transpile/function.rs b/src/transpile/function.rs
index 630f7d1..994c9f2 100644
--- a/src/transpile/function.rs
+++ b/src/transpile/function.rs
@@ -85,8 +85,8 @@ impl Transpiler {
                     Ok("shu/".to_string() + &md5::hash(hash_data).to_hex_lowercase())
                 },
                 |val| match val {
-                    TranspileAnnotationValue::None => Ok(identifier_span.str().to_string()),
-                    TranspileAnnotationValue::Expression(expr) => expr
+                    TranspileAnnotationValue::None(_) => Ok(identifier_span.str().to_string()),
+                    TranspileAnnotationValue::Expression(expr, _) => expr
                         .comptime_eval(scope, handler)
                         .ok()
                         .and_then(|val| val.to_string_no_macro())
@@ -101,10 +101,10 @@ impl Transpiler {
                             handler.receive(err.clone());
                             err
                         }),
-                    TranspileAnnotationValue::Map(_) => {
+                    TranspileAnnotationValue::Map(_, span) => {
                         let err =
                             TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
-                                annotation: identifier_span.clone(),
+                                annotation: span.clone(),
                                 message: "Deobfuscate annotation cannot be a map.".to_string(),
                             });
                         handler.receive(err.clone());
diff --git a/src/transpile/mod.rs b/src/transpile/mod.rs
index ea9d503..7779460 100644
--- a/src/transpile/mod.rs
+++ b/src/transpile/mod.rs
@@ -64,28 +64,34 @@ pub struct FunctionData {
 #[derive(Debug, Clone, PartialEq, Eq, Hash, EnumIs)]
 pub enum TranspileAnnotationValue {
     /// No value.
-    None,
+    None(Span),
     /// A single expression.
-    Expression(Expression),
+    Expression(Expression, Span),
     /// A map of key-value pairs.
-    Map(BTreeMap<String, TranspileAnnotationValue>),
+    Map(BTreeMap<String, TranspileAnnotationValue>, Span),
 }
 
-impl From<Option<AnnotationValue>> for TranspileAnnotationValue {
-    fn from(value: Option<AnnotationValue>) -> Self {
+impl TranspileAnnotationValue {
+    /// Creates a new `TranspileAnnotationValue` from an [`AnnotationValue`] and [`Span`] of the key.
+    #[must_use]
+    pub fn from_annotation_value(value: Option<AnnotationValue>, key_span: &Span) -> Self {
         match value {
-            None => Self::None,
-            Some(AnnotationValue::Single { value, .. }) => Self::Expression(value),
+            None => Self::None(key_span.clone()),
+            Some(AnnotationValue::Single { value, .. }) => {
+                let span = value.span();
+                Self::Expression(value, span)
+            }
             Some(AnnotationValue::Multiple { list, .. }) => {
+                let span = list.span();
                 let map = list
                     .into_elements()
                     .map(|elem| {
                         let key = elem.identifier.span.str().to_string();
-                        let value = Self::from(elem.value);
+                        let value = Self::from_annotation_value(elem.value, &elem.identifier.span);
                         (key, value)
                     })
                     .collect();
-                Self::Map(map)
+                Self::Map(map, span)
             }
         }
     }
@@ -112,9 +118,11 @@ impl Debug for FunctionData {
                 impl Debug for AnnotationValueWrapper<'_> {
                     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                         match self.0 {
-                            TranspileAnnotationValue::None => None::<u8>.fmt(f),
-                            TranspileAnnotationValue::Expression(expr) => expr.span().str().fmt(f),
-                            TranspileAnnotationValue::Map(map) => AnnotationsWrapper(map).fmt(f),
+                            TranspileAnnotationValue::None(_) => None::<u8>.fmt(f),
+                            TranspileAnnotationValue::Expression(expr, _) => {
+                                expr.span().str().fmt(f)
+                            }
+                            TranspileAnnotationValue::Map(map, _) => AnnotationsWrapper(map).fmt(f),
                         }
                     }
                 }
diff --git a/src/transpile/transpiler.rs b/src/transpile/transpiler.rs
index 89b2300..83e79f5 100644
--- a/src/transpile/transpiler.rs
+++ b/src/transpile/transpiler.rs
@@ -1,11 +1,12 @@
 //! Transpiler for `Shulkerscript`
 
 use std::{
-    collections::{BTreeMap, HashSet},
+    collections::{BTreeMap, BTreeSet, HashSet},
     ops::Deref,
     sync::{Arc, OnceLock},
 };
 
+use itertools::Itertools;
 use shulkerbox::datapack::{self, Command, Datapack, Execute};
 
 use crate::{
@@ -21,7 +22,10 @@ use crate::{
         },
         AnnotationAssignment,
     },
-    transpile::util::{MacroString, MacroStringPart},
+    transpile::{
+        error::IllegalAnnotationContent,
+        util::{MacroString, MacroStringPart},
+    },
 };
 
 use super::{
@@ -38,6 +42,7 @@ pub struct Transpiler {
     pub(super) datapack: shulkerbox::datapack::Datapack,
     pub(super) setup_cmds: Vec<Command>,
     pub(super) initialized_constant_scores: HashSet<i64>,
+    pub(super) temp_data_storage_locations: BTreeSet<(String, String)>,
     pub(super) temp_counter: usize,
     /// Top-level [`Scope`] for each program identifier
     pub(super) scopes: BTreeMap<String, Arc<Scope>>,
@@ -53,6 +58,7 @@ impl Transpiler {
             datapack: shulkerbox::datapack::Datapack::new(main_namespace_name, pack_format),
             setup_cmds: Vec::new(),
             initialized_constant_scores: HashSet::new(),
+            temp_data_storage_locations: BTreeSet::new(),
             temp_counter: 0,
             scopes: BTreeMap::new(),
         }
@@ -110,10 +116,10 @@ impl Transpiler {
             }
         }
 
-        let mut always_transpile_functions = Vec::new();
-
-        {
-            let functions = self.scopes.iter().flat_map(|(_, scope)| {
+        let functions = self
+            .scopes
+            .iter()
+            .flat_map(|(_, scope)| {
                 scope
                     .get_local_variables()
                     .read()
@@ -122,16 +128,24 @@ impl Transpiler {
                     .filter_map(|data| data.as_function().map(|(data, _, _)| data))
                     .cloned()
                     .collect::<Vec<_>>()
-            });
-            for data in functions {
+            })
+            .unique_by(|data| (data.namespace.clone(), data.identifier_span.clone()))
+            .collect::<Vec<_>>();
+
+        let always_transpile_functions = functions
+            .iter()
+            .filter_map(|data| {
                 let always_transpile_function = data.annotations.contains_key("tick")
                     || data.annotations.contains_key("load")
-                    || data.annotations.contains_key("deobfuscate");
+                    || data.annotations.contains_key("deobfuscate")
+                    || data.annotations.contains_key("uninstall");
                 if always_transpile_function {
-                    always_transpile_functions.push(data.identifier_span.clone());
-                };
-            }
-        }
+                    Some(data.identifier_span.clone())
+                } else {
+                    None
+                }
+            })
+            .collect::<Vec<_>>();
 
         tracing::trace!(
             "Transpiling functions requested by user: {:?}",
@@ -163,6 +177,48 @@ impl Transpiler {
             );
         }
 
+        let uninstall_function_cmds = functions
+            .iter()
+            .filter_map(|data| data.annotations.get("uninstall").map(|val| (data, val)))
+            .map(|(function, val)| match val {
+                TranspileAnnotationValue::None(_) => {
+                    let identifier_span = &function.identifier_span;
+                    let scope = self
+                        .scopes
+                        .entry(identifier_span.source_file().identifier().to_owned())
+                        .or_insert_with(Scope::with_internal_functions)
+                        .to_owned();
+                    let (function_path, _) =
+                        self.get_or_transpile_function(identifier_span, None, &scope, handler)?;
+                    let uninstall_cmd = Command::Raw(format!("function {function_path}"));
+                    Ok(uninstall_cmd)
+                }
+                TranspileAnnotationValue::Expression(_, span)
+                | TranspileAnnotationValue::Map(_, span) => {
+                    let error =
+                        TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
+                            annotation: span.clone(),
+                            message: "uninstall annotation must not have a value".to_string(),
+                        });
+                    handler.receive(error.clone());
+                    Err(error)
+                }
+            })
+            .collect::<TranspileResult<Vec<_>>>()?;
+
+        self.datapack
+            .add_uninstall_commands(uninstall_function_cmds);
+
+        let temp_data_uninstall_cmds = self
+            .temp_data_storage_locations
+            .iter()
+            .map(|(storage_name, path)| {
+                Command::Raw(format!("data remove storage {storage_name} {path}"))
+            })
+            .collect();
+        self.datapack
+            .add_uninstall_commands(temp_data_uninstall_cmds);
+
         Ok(self.datapack)
     }
 
@@ -208,7 +264,10 @@ impl Transpiler {
                         } = annotation.assignment();
                         (
                             key.span().str().to_string(),
-                            TranspileAnnotationValue::from(value.clone()),
+                            TranspileAnnotationValue::from_annotation_value(
+                                value.clone(),
+                                &key.span,
+                            ),
                         )
                     })
                     .collect();
@@ -229,7 +288,7 @@ impl Transpiler {
                     VariableData::Function {
                         function_data,
                         path: OnceLock::new(),
-                        function_scope: scope.clone(),
+                        function_scope: Scope::with_parent(scope.clone()),
                     },
                 );
             }
diff --git a/src/transpile/variables.rs b/src/transpile/variables.rs
index 5755894..a397c79 100644
--- a/src/transpile/variables.rs
+++ b/src/transpile/variables.rs
@@ -805,11 +805,13 @@ impl Transpiler {
             return Err(error);
         }
         if let Some(deobfuscate_annotation) = deobfuscate_annotation {
-            let deobfuscate_annotation_value =
-                TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
+            let deobfuscate_annotation_value = TranspileAnnotationValue::from_annotation_value(
+                deobfuscate_annotation.assignment().value.clone(),
+                &deobfuscate_annotation.assignment().identifier.span,
+            );
 
             match deobfuscate_annotation_value {
-                TranspileAnnotationValue::Expression(expr) => {
+                TranspileAnnotationValue::Expression(expr, _) => {
                     if let Some(name_eval) = expr
                         .comptime_eval(scope, handler)
                         .ok()
@@ -834,8 +836,8 @@ impl Transpiler {
                         Err(error)
                     }
                 }
-                TranspileAnnotationValue::None => Ok(identifier.span.str().to_string()),
-                TranspileAnnotationValue::Map(_) => {
+                TranspileAnnotationValue::None(_) => Ok(identifier.span.str().to_string()),
+                TranspileAnnotationValue::Map(_, _) => {
                     let error =
                         TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
                             annotation: deobfuscate_annotation.span(),
@@ -888,10 +890,12 @@ impl Transpiler {
             return Err(error);
         }
         if let Some(deobfuscate_annotation) = deobfuscate_annotation {
-            let deobfuscate_annotation_value =
-                TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
+            let deobfuscate_annotation_value = TranspileAnnotationValue::from_annotation_value(
+                deobfuscate_annotation.assignment().value.clone(),
+                &deobfuscate_annotation.assignment().identifier.span,
+            );
 
-            if let TranspileAnnotationValue::Map(map) = deobfuscate_annotation_value {
+            if let TranspileAnnotationValue::Map(map, _) = deobfuscate_annotation_value {
                 if map.len() > 2 {
                     let error =
                         TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
@@ -904,8 +908,8 @@ impl Transpiler {
                 }
                 if let (Some(name), Some(target)) = (map.get("name"), map.get("target")) {
                     if let (
-                        TranspileAnnotationValue::Expression(objective),
-                        TranspileAnnotationValue::Expression(target),
+                        TranspileAnnotationValue::Expression(objective, _),
+                        TranspileAnnotationValue::Expression(target, _),
                     ) = (name, target)
                     {
                         if let (Some(name_eval), Some(target_eval)) = (
@@ -1022,11 +1026,13 @@ impl Transpiler {
             return Err(error);
         }
         if let Some(deobfuscate_annotation) = deobfuscate_annotation {
-            let deobfuscate_annotation_value =
-                TranspileAnnotationValue::from(deobfuscate_annotation.assignment().value.clone());
+            let deobfuscate_annotation_value = TranspileAnnotationValue::from_annotation_value(
+                deobfuscate_annotation.assignment().value.clone(),
+                &deobfuscate_annotation.assignment().identifier.span,
+            );
 
             match deobfuscate_annotation_value {
-                TranspileAnnotationValue::None => {
+                TranspileAnnotationValue::None(_) => {
                     let ident_str = declaration.identifier().span.str();
                     let name = if matches!(variable_type, KeywordKind::Int) {
                         ident_str.to_string()
@@ -1041,7 +1047,7 @@ impl Transpiler {
                     let targets = (0..len).map(|i| i.to_string()).collect();
                     Ok((name, targets))
                 }
-                TranspileAnnotationValue::Map(map) => {
+                TranspileAnnotationValue::Map(map, _) => {
                     // TODO: implement when map deobfuscate annotation is implemented
                     let error =
                         TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
@@ -1052,7 +1058,7 @@ impl Transpiler {
                     handler.receive(error.clone());
                     Err(error)
                 }
-                TranspileAnnotationValue::Expression(_) => {
+                TranspileAnnotationValue::Expression(_, _) => {
                     let error =
                         TranspileError::IllegalAnnotationContent(IllegalAnnotationContent {
                             annotation: deobfuscate_annotation.span(),