diff --git a/helpers/_defer.function.fish b/helpers/_defer.function.fish
index 983181555838f4d4c802cb0b8cda694fe599999c..1eab106e7a65a23dc02ffa8bfd452a871fe12b02 100644
--- a/helpers/_defer.function.fish
+++ b/helpers/_defer.function.fish
@@ -1,26 +1,39 @@
+#!/bin/false
+
 . (status dirname)/_error.function.fish
 
+
+# =_defer
+#
+# Import with `. (status dirname)/_defer.function.fish`. Use with `_defer (<name> | _) <body>.
+# e.g.
+# > touch foo && _defer rm_foo 'rm -f '(realpath foo)
+#
+# This creates a new function with a name of something like
+# `__cleanup__<script_path>__rm_foo__<uuid>` with `--on-event fish_exit` which
+# erases itself on execution (subject to change).
+
+
 # workaround for `status filename` not returning the script that sourced
 [ (count $argv) -eq 1 ] || _error 'Need to supply script name to source command'
 set current_filename $argv[1]
 
 if [ "$current_filename" = "Standard input" ]
-  set -e current_filename
+    set -e current_filename
 else
-  set current_filename (systemd-escape --path (basename $current_filename))
+    set current_filename (systemd-escape --path (basename $current_filename) 2>/dev/null)
 end
 
 function _defer --argument name body
-  if [ (count $argv) -ne 2 ] || [ -z "$argv[1]" ] || [ -z "$argv[2]" ]
-    echo Invalid params '(expected 2, name and body, both non-empty)'
-    return 1
-  end
-
-  set name __cleanup__"$current_filename"__(string escape --style=var "$name")__(uuidgen)
-# need \; and \<newline> for line break escaping and command delimiting
-  echo                                                                                          \
-  function $name --on-event fish_exit                                                         \;\
-    $body                                                                                     \;\
-    functions --erase $name                                                                   \;\
-  end | source
+    if [ (count $argv) -ne 2 ] || [ -z "$argv[1]" ] || [ -z "$argv[2]" ]
+        _error '_defer: Invalid params (expected 2, name and body, both non-empty)' argv
+    end
+
+    set name __cleanup__"$current_filename"__(string escape --style=var "$name")__(uuidgen)
+    # need \; and \<newline> for line break escaping and command delimiting
+    echo \
+        function $name --on-event fish_exit \; \
+        $body \; \
+        functions --erase $name \; \
+        end | source
 end
diff --git a/helpers/_error.function.fish b/helpers/_error.function.fish
index ecf5efabe9d84d385514f406ab64304f7700b6ba..e54a5e670da03fb3d1599215c973510388e2f05e 100644
--- a/helpers/_error.function.fish
+++ b/helpers/_error.function.fish
@@ -1,11 +1,34 @@
 # use syntax: 
 # . (dirname (status current-filename))/../helpers/_error.function.fish
 
+# TODO change syntax to vars can appear everywhere with prefix (eg _error this is a @foo @bar call) executes to `This is a foo=<foo> bar=<bar> call`
+# TODO add script name to source params so we can atleast add a prefix
+function _log -a label -a color --description 'Call with `_error <msg> <var1> <var2> … <var_n>` to abort the program' --no-scope-shadowing
+    for i in $argv[2..]
+        set out $out {$i}="$$i"
+    end
+
+    begin
+        set_color brred
+        echo -n $argv[1]
+        set_color red
+        echo ' ('(string join ', ' $out)')'
+        set_color normal
+    end 1>&2
+    exit 1
+end
+
 function _error --description 'Call with `_error <msg> <var1> <var2> … <var_n>` to abort the program' --no-scope-shadowing
-  for i in $argv[2..]
-    set out $out {$i}="$$i"
-  end
+    for i in $argv[2..]
+        set out $out {$i}="$$i"
+    end
 
-  begin; set_color brred; echo -n $argv[1]; set_color red; echo ' ('(string join ', ' $out)')'; set_color normal; end 1>&2
-  exit 1
+    begin
+        set_color brred
+        echo -n $argv[1]
+        set_color red
+        echo ' ('(string join ', ' $out)')'
+        set_color normal
+    end 1>&2
+    exit 1
 end
diff --git a/helpers/_import.function.fish b/helpers/_import.function.fish
new file mode 100755
index 0000000000000000000000000000000000000000..5a2f52c24ee6e9e1f5cc6f6b4c236beefda0aa4d
--- /dev/null
+++ b/helpers/_import.function.fish
@@ -0,0 +1,49 @@
+#!/bin/false
+
+
+# =_import
+#
+# Call with `_import <path> [as <prefix>]. Imports exported symbols with a prefix.
+# e.g. `colors.fish` exports [`r`, `g`, `b`]. Then `_import colors.fish as cl`
+# defines three vars `$cl_r`, `$cl_g`, `$cl_b` while also checking possible
+# collisions (and aborting on such).
+#
+# (TODO or register functions? not sure which is better)
+
+status dirname
+. (status dirname)/_defer.function.fish (status filename)
+. (status dirname)/_error.function.fish
+
+function _import
+    function b9ef403f-b1cc-4ace-b1bf-218c9eeb334b --inherit-variable args
+        echo Error: `_import $args` is an illegal usage!
+        #exit 2
+        return 2
+    end
+    set _usage b9ef403f-b1cc-4ace-b1bf-218c9eeb334b
+    _defer _ 'functions -e b9ef403f-b1cc-4ace-b1bf-218c9eeb334b'
+
+    set SYM_TBL __SYMBOLS__
+
+    # parse args
+    set args (string match --regex --groups-only '(.+) as ([[:alnum:]]+)' "$argv")
+    [ $status -ne 0 ] && $_usage
+
+    set module_path $args[1]
+    set prefix $args[2]
+
+    [ ! -e "$module_path" ] || [ -z "$prefix" ] && $_usage
+
+    set -e $SYM_TBL
+    source $module_path || _error 'Could not source module' module_path
+    set -q $SYM_TBL || _error 'No sym table found in module' module_path SYM_TBL
+
+    for symbol in $$SYM_TBL
+        set symbol (string split --max 1 -- = $symbol)
+        [ (count $symbol) -eq 1 ] && set -a symbol $symbol
+
+        printf '%s ' set
+        [ $prefix = _ ] || printf '%s_' $prefix
+        printf '%s %s\n' $symbol
+    end
+end
diff --git a/helpers/numeric.fish b/helpers/numeric.fish
new file mode 100644
index 0000000000000000000000000000000000000000..cb1bedacf9b4c9ef5af16865d263c4cd8b95bf87
--- /dev/null
+++ b/helpers/numeric.fish
@@ -0,0 +1,17 @@
+#!/usr/bin/env false
+
+
+# matches numbers, pos/neg with/out decimal places
+function __t-nil_helper__is_numeric --argument-names x
+    string match --regex -- '^-?\d+(\.\d+)?$' $x >/dev/null
+end
+
+if functions -q @test
+    for t in 99 101 005 5.88 -2 -974 -4.5 -0.7
+        @test "is_numeric == true: "$t (__t-nil_helper__is_numeric $t) $status -eq 0
+    end
+
+    for t in 5. 3s s5 N/A
+        @test "is_numeric == false: "$t (__t-nil_helper__is_numeric $t) $status -ne 0
+    end
+end
diff --git a/helpers/std.fish b/helpers/std.fish
index b7c4760b1e61425f766172c6f6a50253ffbedf43..92f978848c656222c1f54e5ead34dd400ea3b283 100644
--- a/helpers/std.fish
+++ b/helpers/std.fish
@@ -1,4 +1,9 @@
-. (status dirname)/_error.function.fish
+set helpers (dirname (realpath (status filename)))
+. $helpers/_error.function.fish (status filename)
+. $helpers/_defer.function.fish (status filename)
+
+set -g __SYMBOLS__ memoize=_memoize_216d7666-6730-426a-9e4b-3ba3f9d31c20 \
+                   require=_require_983b0cc9-3314-4284-ac16-4372350fd657
 
 begin
     function _t-nil_helpers_std_find_end_of_flags --description "given \$argv, returns the index of the first non-flag-arg (either not starting with '-' or after encountering '--')"\nERROR\n"end not found: return 0"
@@ -20,7 +25,7 @@ begin
     end
 end
 
-function _memoize -a function_name
+function _memoize_216d7666-6730-426a-9e4b-3ba3f9d31c20 -a function_name
     set -l MEMOIZE_MAX 102400
 
     set inner_fn_name '__'$function_name'__memoize_inner'
@@ -53,3 +58,21 @@ function _memoize -a function_name
     end
     ' | source
 end
+
+# TODO tests (may require stuff like  https://github.com/fish-shell/fish-shell/issues/10019 to test stderr msgs)
+function require
+    switch (count $argv)
+        case 1:
+            set -l cmd $argv[1]
+            [ -x (which $cmd) ] || [ -x $cmd ] || return (_error "[ERROR] Script requirement $cmd not found")
+        case 2:
+            if [ $argv[2] != :opt ]
+                return (_error malformed argv)
+            end
+            set -l cmd $argv[2]
+            [ -x (which $cmd) ] || [ -x $cmd ] || begin; set_color -di yellow; echo "[WARN] Optional requirement $cmd not found"; return 5; end >&2
+    end
+    return (_error malformed argv)
+end
+
+