Repository: lt-mayonesa/hexagon (language: python)

Issue:
need to support pydantic v2, huge refactor


Line: 12
https://github.com/lt-mayonesa/hexagon/blob/c66c8765e3c00c17dc9a66af1eba16eed78fdd1e//Pipfile#L9-L19


Discussion / hints:

Fix patch:
diff --git a/.github/workflows/01-python-package.yml b/.github/workflows/01-python-package.yml
index c7f98455..a8b4919a 100644
--- a/.github/workflows/01-python-package.yml
+++ b/.github/workflows/01-python-package.yml
@@ -91,13 +91,18 @@ jobs:
           name: hexagon-${{ github.sha }}
           path: dist
 
+      - name: Display structure of downloaded files
+        run: ls -R
+        working-directory: dist
+
       - uses: ./.github/actions/download-i18n
 
       - name: Setup e2e dependencies
         run: |
           python -m pip install --upgrade pip
           pip install pytest
-          python -m pip install --find-links=./dist hexagon
+          artifact=$(ls dist/*.tar.gz)
+          python -m pip install $artifact
 
       - name: e2e tests
         uses: nick-fields/retry@v2
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 7f48da0f..a7c58360 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -1,5 +1,8 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
+  <component name="Black">
+    <option name="sdkName" value="Pipenv (hexagon)" />
+  </component>
   <component name="ProjectRootManager" version="2" languageLevel="JDK_16" project-jdk-name="Pipenv (hexagon)" project-jdk-type="Python SDK">
     <output url="file://$PROJECT_DIR$/out" />
   </component>
diff --git a/.vscode/launch.json b/.vscode/launch.json
index ff00d3a8..caab0545 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -84,7 +84,7 @@
             "subProcess": true,
             "env": {
                 "HEXAGON_STORAGE_PATH": "${workspaceFolder}/tests_e2e/update_hexagon/storage",
-                "HEXAGON_TEST_VERSION_OVERRIDE": "0.1.0",
+                "HEXAGON_TEST_LOCAL_VERSION_OVERRIDE": "0.1.0",
                 "HEXAGON_TEST_SHELL": "HEXAGON_TEST_SHELL",
                 "HEXAGON_CHANGELOG_FILE_PATH_TEST_OVERRIDE": "${workspaceFolder}/tests_e2e/update_hexagon/CHANGELOG.md",
                 "HEXAGON_THEME": "default"
@@ -106,7 +106,7 @@
                 "HEXAGON_TEST_SHELL": "HEXAGON_TEST_SHELL",
                 "HEXAGON_SEND_TELEMETRY": "0",
                 "HEXAGON_DEPENDENCY_UPDATER_MOCK_ENABLED": "1",
-                "HEXAGON_TEST_VERSION_OVERRIDE": "1000.1.0"
+                "HEXAGON_TEST_LOCAL_VERSION_OVERRIDE": "1000.1.0"
             }
         },
         {
diff --git a/Pipfile b/Pipfile
index 6317851b..fdc55b53 100644
--- a/Pipfile
+++ b/Pipfile
@@ -8,11 +8,11 @@ inquirerpy = "*"
 rich = "*"
 clipboard = "*"
 "ruamel.yaml" = "*"
-# TODO: need to support pydantic v2, huge refactor
-pydantic = "<2.0.0"
+pydantic = "*"
 typing-extensions = "*"
 packaging = "*"
 markdown = "*"
+pydantic-settings = "*"
 
 [dev-packages]
 flake8 = "*"
diff --git a/Pipfile.lock b/Pipfile.lock
index 2872f101..be5d18be 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "27bdeed286604525abc239d92f89616cbead6e1bf13a8371bdfaedb93a044948"
+            "sha256": "de91bf66c02aeca88551e81b4bd0a8da375abc336f3ea3d80205b9a99552ce35"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -16,6 +16,14 @@
         ]
     },
     "default": {
+        "annotated-types": {
+            "hashes": [
+                "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53",
+                "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==0.7.0"
+        },
         "clipboard": {
             "hashes": [
                 "sha256:a72a78e9c9bf68da1c3f29ee022417d13ec9e3824b511559fd2b702b1dd5b817"
@@ -34,12 +42,12 @@
         },
         "markdown": {
             "hashes": [
-                "sha256:d43323865d89fc0cb9b20c75fc8ad313af307cc087e84b657d9eec768eddeadd",
-                "sha256:e1ac7b3dc550ee80e602e71c1d168002f062e49f1b11e26a36264dafd4df2ef8"
+                "sha256:48f276f4d8cfb8ce6527c8f79e2ee29708508bf4d40aa410fbc3b4ee832c850f",
+                "sha256:ed4f41f6daecbeeb96e576ce414c41d2d876daa9a16cb35fa8ed8c2ddfad0224"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==3.5.2"
+            "version": "==3.6"
         },
         "markdown-it-py": {
             "hashes": [
@@ -59,12 +67,12 @@
         },
         "packaging": {
             "hashes": [
-                "sha256:048fb0e9405036518eaaf48a55953c750c11e1a1b68e0dd1a9d62ed0c092cfc5",
-                "sha256:8c491190033a9af7e1d931d0b5dacc2ef47509b34dd0de67ed209b5203fc88c7"
+                "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002",
+                "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.7'",
-            "version": "==23.2"
+            "markers": "python_version >= '3.8'",
+            "version": "==24.1"
         },
         "pfzy": {
             "hashes": [
@@ -76,77 +84,155 @@
         },
         "prompt-toolkit": {
             "hashes": [
-                "sha256:3527b7af26106cbc65a040bcc84839a3566ec1b051bb0bfe953631e704b0ff7d",
-                "sha256:a11a29cb3bf0a28a387fe5122cdb649816a957cd9261dcedf8c9f1fef33eacf6"
+                "sha256:0d7bfa67001d5e39d02c224b663abc33687405033a8c422d0d675a5a13361d10",
+                "sha256:1e1b29cb58080b1e69f207c893a1a7bf16d127a5c30c9d17a25a5d77792e5360"
             ],
             "markers": "python_full_version >= '3.7.0'",
-            "version": "==3.0.43"
+            "version": "==3.0.47"
         },
         "pydantic": {
             "hashes": [
-                "sha256:08b6ec0917c30861e3fe71a93be1648a2aa4f62f866142ba21670b24444d7fd8",
-                "sha256:0fbb503bbbbab0c588ed3cd21975a1d0d4163b87e360fec17a792f7d8c4ff29f",
-                "sha256:1245f4f61f467cb3dfeced2b119afef3db386aec3d24a22a1de08c65038b255f",
-                "sha256:13e86a19dca96373dcf3190fcb8797d40a6f12f154a244a8d1e8e03b8f280593",
-                "sha256:21efacc678a11114c765eb52ec0db62edffa89e9a562a94cbf8fa10b5db5c046",
-                "sha256:23d47a4b57a38e8652bcab15a658fdb13c785b9ce217cc3a729504ab4e1d6bc9",
-                "sha256:24a7679fab2e0eeedb5a8924fc4a694b3bcaac7d305aeeac72dd7d4e05ecbebf",
-                "sha256:282613a5969c47c83a8710cc8bfd1e70c9223feb76566f74683af889faadc0ea",
-                "sha256:336709883c15c050b9c55a63d6c7ff09be883dbc17805d2b063395dd9d9d0022",
-                "sha256:412ab4a3f6dbd2bf18aefa9f79c7cca23744846b31f1d6555c2ee2b05a2e14ca",
-                "sha256:466669501d08ad8eb3c4fecd991c5e793c4e0bbd62299d05111d4f827cded64f",
-                "sha256:46f17b832fe27de7850896f3afee50ea682220dd218f7e9c88d436788419dca6",
-                "sha256:49a46a0994dd551ec051986806122767cf144b9702e31d47f6d493c336462597",
-                "sha256:4ae57b4d8e3312d486e2498d42aed3ece7b51848336964e43abbf9671584e67f",
-                "sha256:53e3819bd20a42470d6dd0fe7fc1c121c92247bca104ce608e609b59bc7a77ee",
-                "sha256:596f12a1085e38dbda5cbb874d0973303e34227b400b6414782bf205cc14940c",
-                "sha256:646b2b12df4295b4c3148850c85bff29ef6d0d9621a8d091e98094871a62e5c7",
-                "sha256:798a3d05ee3b71967844a1164fd5bdb8c22c6d674f26274e78b9f29d81770c4e",
-                "sha256:7f4fcec873f90537c382840f330b90f4715eebc2bc9925f04cb92de593eae054",
-                "sha256:82d886bd3c3fbeaa963692ef6b643159ccb4b4cefaf7ff1617720cbead04fd1d",
-                "sha256:8e3a76f571970fcd3c43ad982daf936ae39b3e90b8a2e96c04113a369869dc87",
-                "sha256:8ee853cd12ac2ddbf0ecbac1c289f95882b2d4482258048079d13be700aa114c",
-                "sha256:9d578ac4bf7fdf10ce14caba6f734c178379bd35c486c6deb6f49006e1ba78a7",
-                "sha256:a42c7d17706911199798d4c464b352e640cab4351efe69c2267823d619a937e5",
-                "sha256:aad4e10efa5474ed1a611b6d7f0d130f4aafadceb73c11d9e72823e8f508e663",
-                "sha256:ad8c2bc677ae5f6dbd3cf92f2c7dc613507eafe8f71719727cbc0a7dec9a8c01",
-                "sha256:bc3ed06ab13660b565eed80887fcfbc0070f0aa0691fbb351657041d3e874efe",
-                "sha256:bfb113860e9288d0886e3b9e49d9cf4a9d48b441f52ded7d96db7819028514cc",
-                "sha256:c37c28449752bb1f47975d22ef2882d70513c546f8f37201e0fec3a97b816eee",
-                "sha256:c66609e138c31cba607d8e2a7b6a5dc38979a06c900815495b2d90ce6ded35b4",
-                "sha256:d604be0f0b44d473e54fdcb12302495fe0467c56509a2f80483476f3ba92b33c",
-                "sha256:d986e115e0b39604b9eee3507987368ff8148222da213cd38c359f6f57b3b347",
-                "sha256:dba49d52500c35cfec0b28aa8b3ea5c37c9df183ffc7210b10ff2a415c125c4a",
-                "sha256:e897c9f35281f7889873a3e6d6b69aa1447ceb024e8495a5f0d02ecd17742a7f",
-                "sha256:f9f674b5c3bebc2eba401de64f29948ae1e646ba2735f884d1594c5f675d6f2a",
-                "sha256:fa7790e94c60f809c95602a26d906eba01a0abee9cc24150e4ce2189352deb1b"
+                "sha256:6f62c13d067b0755ad1c21a34bdd06c0c12625a22b0fc09c6b149816604f7c2a",
+                "sha256:73ee9fddd406dc318b885c7a2eab8a6472b68b8fb5ba8150949fc3db939f23c8"
             ],
             "index": "pypi",
-            "markers": "python_version >= '3.7'",
-            "version": "==1.10.14"
+            "markers": "python_version >= '3.8'",
+            "version": "==2.8.2"
+        },
+        "pydantic-core": {
+            "hashes": [
+                "sha256:035ede2e16da7281041f0e626459bcae33ed998cca6a0a007a5ebb73414ac72d",
+                "sha256:04024d270cf63f586ad41fff13fde4311c4fc13ea74676962c876d9577bcc78f",
+                "sha256:0827505a5c87e8aa285dc31e9ec7f4a17c81a813d45f70b1d9164e03a813a686",
+                "sha256:084659fac3c83fd674596612aeff6041a18402f1e1bc19ca39e417d554468482",
+                "sha256:10d4204d8ca33146e761c79f83cc861df20e7ae9f6487ca290a97702daf56006",
+                "sha256:11b71d67b4725e7e2a9f6e9c0ac1239bbc0c48cce3dc59f98635efc57d6dac83",
+                "sha256:150906b40ff188a3260cbee25380e7494ee85048584998c1e66df0c7a11c17a6",
+                "sha256:175873691124f3d0da55aeea1d90660a6ea7a3cfea137c38afa0a5ffabe37b88",
+                "sha256:177f55a886d74f1808763976ac4efd29b7ed15c69f4d838bbd74d9d09cf6fa86",
+                "sha256:19c0fa39fa154e7e0b7f82f88ef85faa2a4c23cc65aae2f5aea625e3c13c735a",
+                "sha256:1eedfeb6089ed3fad42e81a67755846ad4dcc14d73698c120a82e4ccf0f1f9f6",
+                "sha256:225b67a1f6d602de0ce7f6c1c3ae89a4aa25d3de9be857999e9124f15dab486a",
+                "sha256:242b8feb3c493ab78be289c034a1f659e8826e2233786e36f2893a950a719bb6",
+                "sha256:254ec27fdb5b1ee60684f91683be95e5133c994cc54e86a0b0963afa25c8f8a6",
+                "sha256:25e9185e2d06c16ee438ed39bf62935ec436474a6ac4f9358524220f1b236e43",
+                "sha256:26ab812fa0c845df815e506be30337e2df27e88399b985d0bb4e3ecfe72df31c",
+                "sha256:26ca695eeee5f9f1aeeb211ffc12f10bcb6f71e2989988fda61dabd65db878d4",
+                "sha256:26dc97754b57d2fd00ac2b24dfa341abffc380b823211994c4efac7f13b9e90e",
+                "sha256:270755f15174fb983890c49881e93f8f1b80f0b5e3a3cc1394a255706cabd203",
+                "sha256:2aafc5a503855ea5885559eae883978c9b6d8c8993d67766ee73d82e841300dd",
+                "sha256:2d036c7187b9422ae5b262badb87a20a49eb6c5238b2004e96d4da1231badef1",
+                "sha256:33499e85e739a4b60c9dac710c20a08dc73cb3240c9a0e22325e671b27b70d24",
+                "sha256:37eee5b638f0e0dcd18d21f59b679686bbd18917b87db0193ae36f9c23c355fc",
+                "sha256:38cf1c40a921d05c5edc61a785c0ddb4bed67827069f535d794ce6bcded919fc",
+                "sha256:3acae97ffd19bf091c72df4d726d552c473f3576409b2a7ca36b2f535ffff4a3",
+                "sha256:3c5ebac750d9d5f2706654c638c041635c385596caf68f81342011ddfa1e5598",
+                "sha256:3d482efec8b7dc6bfaedc0f166b2ce349df0011f5d2f1f25537ced4cfc34fd98",
+                "sha256:407653af5617f0757261ae249d3fba09504d7a71ab36ac057c938572d1bc9331",
+                "sha256:40a783fb7ee353c50bd3853e626f15677ea527ae556429453685ae32280c19c2",
+                "sha256:41e81317dd6a0127cabce83c0c9c3fbecceae981c8391e6f1dec88a77c8a569a",
+                "sha256:41f4c96227a67a013e7de5ff8f20fb496ce573893b7f4f2707d065907bffdbd6",
+                "sha256:469f29f9093c9d834432034d33f5fe45699e664f12a13bf38c04967ce233d688",
+                "sha256:4745f4ac52cc6686390c40eaa01d48b18997cb130833154801a442323cc78f91",
+                "sha256:4868f6bd7c9d98904b748a2653031fc9c2f85b6237009d475b1008bfaeb0a5aa",
+                "sha256:4aa223cd1e36b642092c326d694d8bf59b71ddddc94cdb752bbbb1c5c91d833b",
+                "sha256:4dd484681c15e6b9a977c785a345d3e378d72678fd5f1f3c0509608da24f2ac0",
+                "sha256:4f2790949cf385d985a31984907fecb3896999329103df4e4983a4a41e13e840",
+                "sha256:512ecfbefef6dac7bc5eaaf46177b2de58cdf7acac8793fe033b24ece0b9566c",
+                "sha256:516d9227919612425c8ef1c9b869bbbee249bc91912c8aaffb66116c0b447ebd",
+                "sha256:53e431da3fc53360db73eedf6f7124d1076e1b4ee4276b36fb25514544ceb4a3",
+                "sha256:595ba5be69b35777474fa07f80fc260ea71255656191adb22a8c53aba4479231",
+                "sha256:5b5ff4911aea936a47d9376fd3ab17e970cc543d1b68921886e7f64bd28308d1",
+                "sha256:5d41e6daee2813ecceea8eda38062d69e280b39df793f5a942fa515b8ed67953",
+                "sha256:5e999ba8dd90e93d57410c5e67ebb67ffcaadcea0ad973240fdfd3a135506250",
+                "sha256:5f239eb799a2081495ea659d8d4a43a8f42cd1fe9ff2e7e436295c38a10c286a",
+                "sha256:635fee4e041ab9c479e31edda27fcf966ea9614fff1317e280d99eb3e5ab6fe2",
+                "sha256:65db0f2eefcaad1a3950f498aabb4875c8890438bc80b19362cf633b87a8ab20",
+                "sha256:6b507132dcfc0dea440cce23ee2182c0ce7aba7054576efc65634f080dbe9434",
+                "sha256:6b9d9bb600328a1ce523ab4f454859e9d439150abb0906c5a1983c146580ebab",
+                "sha256:70c8daf4faca8da5a6d655f9af86faf6ec2e1768f4b8b9d0226c02f3d6209703",
+                "sha256:77bf3ac639c1ff567ae3b47f8d4cc3dc20f9966a2a6dd2311dcc055d3d04fb8a",
+                "sha256:784c1214cb6dd1e3b15dd8b91b9a53852aed16671cc3fbe4786f4f1db07089e2",
+                "sha256:7eb6a0587eded33aeefea9f916899d42b1799b7b14b8f8ff2753c0ac1741edac",
+                "sha256:7ed1b0132f24beeec5a78b67d9388656d03e6a7c837394f99257e2d55b461611",
+                "sha256:8ad4aeb3e9a97286573c03df758fc7627aecdd02f1da04516a86dc159bf70121",
+                "sha256:964faa8a861d2664f0c7ab0c181af0bea66098b1919439815ca8803ef136fc4e",
+                "sha256:9dc1b507c12eb0481d071f3c1808f0529ad41dc415d0ca11f7ebfc666e66a18b",
+                "sha256:9ebfef07dbe1d93efb94b4700f2d278494e9162565a54f124c404a5656d7ff09",
+                "sha256:a45f84b09ac9c3d35dfcf6a27fd0634d30d183205230a0ebe8373a0e8cfa0906",
+                "sha256:a4f55095ad087474999ee28d3398bae183a66be4823f753cd7d67dd0153427c9",
+                "sha256:a6d511cc297ff0883bc3708b465ff82d7560193169a8b93260f74ecb0a5e08a7",
+                "sha256:a8ad4c766d3f33ba8fd692f9aa297c9058970530a32c728a2c4bfd2616d3358b",
+                "sha256:aa2f457b4af386254372dfa78a2eda2563680d982422641a85f271c859df1987",
+                "sha256:b03f7941783b4c4a26051846dea594628b38f6940a2fdc0df00b221aed39314c",
+                "sha256:b0dae11d8f5ded51699c74d9548dcc5938e0804cc8298ec0aa0da95c21fff57b",
+                "sha256:b91ced227c41aa29c672814f50dbb05ec93536abf8f43cd14ec9521ea09afe4e",
+                "sha256:bc633a9fe1eb87e250b5c57d389cf28998e4292336926b0b6cdaee353f89a237",
+                "sha256:bebb4d6715c814597f85297c332297c6ce81e29436125ca59d1159b07f423eb1",
+                "sha256:c336a6d235522a62fef872c6295a42ecb0c4e1d0f1a3e500fe949415761b8a19",
+                "sha256:c6514f963b023aeee506678a1cf821fe31159b925c4b76fe2afa94cc70b3222b",
+                "sha256:c693e916709c2465b02ca0ad7b387c4f8423d1db7b4649c551f27a529181c5ad",
+                "sha256:c81131869240e3e568916ef4c307f8b99583efaa60a8112ef27a366eefba8ef0",
+                "sha256:d02a72df14dfdbaf228424573a07af10637bd490f0901cee872c4f434a735b94",
+                "sha256:d2a8fa9d6d6f891f3deec72f5cc668e6f66b188ab14bb1ab52422fe8e644f312",
+                "sha256:d2b27e6af28f07e2f195552b37d7d66b150adbaa39a6d327766ffd695799780f",
+                "sha256:d2fe69c5434391727efa54b47a1e7986bb0186e72a41b203df8f5b0a19a4f669",
+                "sha256:d3f3ed29cd9f978c604708511a1f9c2fdcb6c38b9aae36a51905b8811ee5cbf1",
+                "sha256:d573faf8eb7e6b1cbbcb4f5b247c60ca8be39fe2c674495df0eb4318303137fe",
+                "sha256:e0bbdd76ce9aa5d4209d65f2b27fc6e5ef1312ae6c5333c26db3f5ade53a1e99",
+                "sha256:e7c4ea22b6739b162c9ecaaa41d718dfad48a244909fe7ef4b54c0b530effc5a",
+                "sha256:e93e1a4b4b33daed65d781a57a522ff153dcf748dee70b40c7258c5861e1768a",
+                "sha256:e97fdf088d4b31ff4ba35db26d9cc472ac7ef4a2ff2badeabf8d727b3377fc52",
+                "sha256:e9fa4c9bf273ca41f940bceb86922a7667cd5bf90e95dbb157cbb8441008482c",
+                "sha256:eaad4ff2de1c3823fddf82f41121bdf453d922e9a238642b1dedb33c4e4f98ad",
+                "sha256:f1f62b2413c3a0e846c3b838b2ecd6c7a19ec6793b2a522745b0869e37ab5bc1",
+                "sha256:f6d6cff3538391e8486a431569b77921adfcdef14eb18fbf19b7c0a5294d4e6a",
+                "sha256:f9aa05d09ecf4c75157197f27cdc9cfaeb7c5f15021c6373932bf3e124af029f",
+                "sha256:fa2fddcb7107e0d1808086ca306dcade7df60a13a6c347a7acf1ec139aa6789a",
+                "sha256:faa6b09ee09433b87992fb5a2859efd1c264ddc37280d2dd5db502126d0e7f27"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==2.20.1"
+        },
+        "pydantic-settings": {
+            "hashes": [
+                "sha256:bb6849dc067f1687574c12a639e231f3a6feeed0a12d710c1382045c5db1c315",
+                "sha256:ed81c3a0f46392b4d7c0a565c05884e6e54b3456e6f0fe4d8814981172dc9a88"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.8'",
+            "version": "==2.4.0"
         },
         "pygments": {
             "hashes": [
-                "sha256:b27c2826c47d0f3219f29554824c30c5e8945175d888647acd804ddd04af846c",
-                "sha256:da46cec9fd2de5be3a8a784f434e4c4ab670b4ff54d605c4c2717e9d49c4c367"
+                "sha256:786ff802f32e91311bff3889f6e9a86e81505fe99f2735bb6d60ae0c5004f199",
+                "sha256:b8e6aca0523f3ab76fee51799c488e38782ac06eafcf95e7ba832985c8e7b13a"
             ],
-            "markers": "python_version >= '3.7'",
-            "version": "==2.17.2"
+            "markers": "python_version >= '3.8'",
+            "version": "==2.18.0"
         },
         "pyperclip": {
             "hashes": [
-                "sha256:105254a8b04934f0bc84e9c24eb360a591aaf6535c9def5f29d92af107a9bf57"
+                "sha256:b7de0142ddc81bfc5c7507eea19da920b92252b548b96186caf94a5e2527d310"
             ],
-            "version": "==1.8.2"
+            "version": "==1.9.0"
+        },
+        "python-dotenv": {
+            "hashes": [
+                "sha256:e324ee90a023d808f1959c46bcbc04446a10ced277783dc6ee09987c37ec10ca",
+                "sha256:f7b63ef50f1b690dddf550d03497b66d609393b40b564ed0d674909a68ebf16a"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==1.0.1"
         },
         "rich": {
             "hashes": [
-                "sha256:5cb5123b5cf9ee70584244246816e9114227e0b98ad9176eede6ad54bf5403fa",
-                "sha256:6da14c108c4866ee9520bbffa71f6fe3962e193b7da68720583850cd4548e235"
+                "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222",
+                "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"
             ],
             "index": "pypi",
             "markers": "python_full_version >= '3.7.0'",
-            "version": "==13.7.0"
+            "version": "==13.7.1"
         },
         "ruamel.yaml": {
             "hashes": [
@@ -214,12 +300,12 @@
         },
         "typing-extensions": {
             "hashes": [
-                "sha256:23478f88c37f27d76ac8aee6c905017a143b0b1b886c3c9f66bc2fd94f9f5783",
-                "sha256:af72aea155e91adfc61c3ae9e0e342dbc0cba726d6cba4b6c72c1f34e47291cd"
+                "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
+                "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==4.9.0"
+            "version": "==4.12.2"
         },
         "wcwidth": {
             "hashes": [
@@ -232,40 +318,40 @@
     "develop": {
         "attrs": {
             "hashes": [
-                "sha256:935dc3b529c262f6cf76e50877d35a4bd3c1de194fd41f47a2b7ae8f19971f30",
-                "sha256:99b87a485a5820b23b879f04c2305b44b951b502fd64be915879d77a7e8fc6f1"
+                "sha256:377b47448cb61fea38533f671fba0d0f8a96fd58facd4dc518e3dac9dbea0905",
+                "sha256:adbdec84af72d38be7628e353a09b6a6790d15cd71819f6e9d7b0faa8a125745"
             ],
             "markers": "python_version >= '3.7'",
-            "version": "==23.2.0"
+            "version": "==24.1.0"
         },
         "black": {
             "hashes": [
-                "sha256:2818cf72dfd5d289e48f37ccfa08b460bf469e67fb7c4abb07edc2e9f16fb63f",
-                "sha256:41622020d7120e01d377f74249e677039d20e6344ff5851de8a10f11f513bf93",
-                "sha256:4acf672def7eb1725f41f38bf6bf425c8237248bb0804faa3965c036f7672d11",
-                "sha256:4be5bb28e090456adfc1255e03967fb67ca846a03be7aadf6249096100ee32d0",
-                "sha256:4f1373a7808a8f135b774039f61d59e4be7eb56b2513d3d2f02a8b9365b8a8a9",
-                "sha256:56f52cfbd3dabe2798d76dbdd299faa046a901041faf2cf33288bc4e6dae57b5",
-                "sha256:65b76c275e4c1c5ce6e9870911384bff5ca31ab63d19c76811cb1fb162678213",
-                "sha256:65c02e4ea2ae09d16314d30912a58ada9a5c4fdfedf9512d23326128ac08ac3d",
-                "sha256:6905238a754ceb7788a73f02b45637d820b2f5478b20fec82ea865e4f5d4d9f7",
-                "sha256:79dcf34b33e38ed1b17434693763301d7ccbd1c5860674a8f871bd15139e7837",
-                "sha256:7bb041dca0d784697af4646d3b62ba4a6b028276ae878e53f6b4f74ddd6db99f",
-                "sha256:7d5e026f8da0322b5662fa7a8e752b3fa2dac1c1cbc213c3d7ff9bdd0ab12395",
-                "sha256:9f50ea1132e2189d8dff0115ab75b65590a3e97de1e143795adb4ce317934995",
-                "sha256:a0c9c4a0771afc6919578cec71ce82a3e31e054904e7197deacbc9382671c41f",
-                "sha256:aadf7a02d947936ee418777e0247ea114f78aff0d0959461057cae8a04f20597",
-                "sha256:b5991d523eee14756f3c8d5df5231550ae8993e2286b8014e2fdea7156ed0959",
-                "sha256:bf21b7b230718a5f08bd32d5e4f1db7fc8788345c8aea1d155fc17852b3410f5",
-                "sha256:c45f8dff244b3c431b36e3224b6be4a127c6aca780853574c00faf99258041eb",
-                "sha256:c7ed6668cbbfcd231fa0dc1b137d3e40c04c7f786e626b405c62bcd5db5857e4",
-                "sha256:d7de8d330763c66663661a1ffd432274a2f92f07feeddd89ffd085b5744f85e7",
-                "sha256:e19cb1c6365fd6dc38a6eae2dcb691d7d83935c10215aef8e6c38edee3f77abd",
-                "sha256:e2af80566f43c85f5797365077fb64a393861a3730bd110971ab7a0c94e873e7"
+                "sha256:09cdeb74d494ec023ded657f7092ba518e8cf78fa8386155e4a03fdcc44679e6",
+                "sha256:1f13f7f386f86f8121d76599114bb8c17b69d962137fc70efe56137727c7047e",
+                "sha256:2500945420b6784c38b9ee885af039f5e7471ef284ab03fa35ecdde4688cd83f",
+                "sha256:2b59b250fdba5f9a9cd9d0ece6e6d993d91ce877d121d161e4698af3eb9c1018",
+                "sha256:3c4285573d4897a7610054af5a890bde7c65cb466040c5f0c8b732812d7f0e5e",
+                "sha256:505289f17ceda596658ae81b61ebbe2d9b25aa78067035184ed0a9d855d18afd",
+                "sha256:62e8730977f0b77998029da7971fa896ceefa2c4c4933fcd593fa599ecbf97a4",
+                "sha256:649f6d84ccbae73ab767e206772cc2d7a393a001070a4c814a546afd0d423aed",
+                "sha256:6e55d30d44bed36593c3163b9bc63bf58b3b30e4611e4d88a0c3c239930ed5b2",
+                "sha256:707a1ca89221bc8a1a64fb5e15ef39cd755633daa672a9db7498d1c19de66a42",
+                "sha256:72901b4913cbac8972ad911dc4098d5753704d1f3c56e44ae8dce99eecb0e3af",
+                "sha256:73bbf84ed136e45d451a260c6b73ed674652f90a2b3211d6a35e78054563a9bb",
+                "sha256:7c046c1d1eeb7aea9335da62472481d3bbf3fd986e093cffd35f4385c94ae368",
+                "sha256:81c6742da39f33b08e791da38410f32e27d632260e599df7245cccee2064afeb",
+                "sha256:837fd281f1908d0076844bc2b801ad2d369c78c45cf800cad7b61686051041af",
+                "sha256:972085c618ee94f402da1af548a4f218c754ea7e5dc70acb168bfaca4c2542ed",
+                "sha256:9e84e33b37be070ba135176c123ae52a51f82306def9f7d063ee302ecab2cf47",
+                "sha256:b19c9ad992c7883ad84c9b22aaa73562a16b819c1d8db7a1a1a49fb7ec13c7d2",
+                "sha256:d6417535d99c37cee4091a2f24eb2b6d5ec42b144d50f1f2e436d9fe1916fe1a",
+                "sha256:eab4dd44ce80dea27dc69db40dab62d4ca96112f87996bca68cd75639aeb2e4c",
+                "sha256:f490dbd59680d809ca31efdae20e634f3fae27fba3ce0ba3208333b713bc3920",
+                "sha256:fb6e2c0b86bbd43dee042e48059c9ad7830abd5c94b0bc518c0eeec57c3eddc1"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==24.3.0"
+            "version": "==24.8.0"
         },
         "click": {
             "hashes": [
@@ -275,23 +361,31 @@
             "markers": "python_version >= '3.7'",
             "version": "==8.1.7"
         },
+        "exceptiongroup": {
+            "hashes": [
+                "sha256:3111b9d131c238bec2f8f516e123e14ba243563fb135d3fe885990585aa7795b",
+                "sha256:47c2edf7c6738fafb49fd34290706d1a1a2f4d1c6df275526b62cbb4aa5393cc"
+            ],
+            "markers": "python_version < '3.11'",
+            "version": "==1.2.2"
+        },
         "flake8": {
             "hashes": [
-                "sha256:33f96621059e65eec474169085dc92bf26e7b2d47366b70be2f67ab80dc25132",
-                "sha256:a6dfbb75e03252917f2473ea9653f7cd799c3064e54d4c8140044c5c065f53c3"
+                "sha256:049d058491e228e03e67b390f311bbf88fce2dbaa8fa673e7aea87b7198b8d38",
+                "sha256:597477df7860daa5aa0fdd84bf5208a043ab96b8e96ab708770ae0364dd03213"
             ],
             "index": "pypi",
             "markers": "python_full_version >= '3.8.1'",
-            "version": "==7.0.0"
+            "version": "==7.1.1"
         },
         "flake8-bugbear": {
             "hashes": [
-                "sha256:663ef5de80cd32aacd39d362212983bc4636435a6f83700b4ed35acbd0b7d1b8",
-                "sha256:f9cb5f2a9e792dd80ff68e89a14c12eed8620af8b41a49d823b7a33064ac9658"
+                "sha256:cb430dd86bc821d79ccc0b030789a9c87a47a369667f12ba06e80f11305e8258",
+                "sha256:ff8d4ba5719019ebf98e754624c30c05cef0dadcf18a65d91c7567300e52a130"
             ],
             "index": "pypi",
             "markers": "python_full_version >= '3.8.1'",
-            "version": "==24.2.6"
+            "version": "==24.4.26"
         },
         "iniconfig": {
             "hashes": [
@@ -319,11 +413,12 @@
         },
         "packaging": {
             "hashes": [
-                "sha256:2ddfb553fdf02fb784c234c7ba6ccc288296ceabec964ad2eae3777778130bc5",
-                "sha256:eb82c5e3e56209074766e6885bb04b8c38a0c015d0a30036ebe7ece34c9989e9"
+                "sha256:026ed72c8ed3fcce5bf8950572258698927fd1dbda10a5e981cdf0ac37f4f002",
+                "sha256:5b8f2217dbdbd2f7f384c41c628544e6d52f2d0f53c6d0c3ea61aa5d1d7ff124"
             ],
-            "markers": "python_version >= '3.7'",
-            "version": "==24.0"
+            "index": "pypi",
+            "markers": "python_version >= '3.8'",
+            "version": "==24.1"
         },
         "pathspec": {
             "hashes": [
@@ -335,27 +430,27 @@
         },
         "platformdirs": {
             "hashes": [
-                "sha256:0614df2a2f37e1a662acbd8e2b25b92ccf8632929bc6d43467e17fe89c75e068",
-                "sha256:ef0cc731df711022c174543cb70a9b5bd22e5a9337c8624ef2c2ceb8ddad8768"
+                "sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee",
+                "sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==4.2.0"
+            "version": "==4.2.2"
         },
         "pluggy": {
             "hashes": [
-                "sha256:7db9f7b503d67d1c5b95f59773ebb58a8c1c288129a88665838012cfb07b8981",
-                "sha256:8c85c2876142a764e5b7548e7d9a0e0ddb46f5185161049a79b7e974454223be"
+                "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1",
+                "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==1.4.0"
+            "version": "==1.5.0"
         },
         "pycodestyle": {
             "hashes": [
-                "sha256:41ba0e7afc9752dfb53ced5489e89f8186be00e599e712660695b7a75ff2663f",
-                "sha256:44fe31000b2d866f2e41841b18528a505fbd7fef9017b04eff4e2648a0fadc67"
+                "sha256:46f0fb92069a7c28ab7bb558f05bfc0110dac69a0cd23c61ea0040283a9d78b3",
+                "sha256:6838eae08bbce4f6accd5d5572075c63626a15ee3e6f842df996bf62f6d73521"
             ],
             "markers": "python_version >= '3.8'",
-            "version": "==2.11.1"
+            "version": "==2.12.1"
         },
         "pyflakes": {
             "hashes": [
@@ -367,12 +462,29 @@
         },
         "pytest": {
             "hashes": [
-                "sha256:267f6563751877d772019b13aacbe4e860d73fe8f651f28112e9ac37de7513ae",
-                "sha256:3e4f16fe1c0a9dc9d9389161c127c3edc5d810c38d6793042fb81d9f48a59fca"
+                "sha256:4ba08f9ae7dcf84ded419494d229b48d0903ea6407b030eaec46df5e6a73bba5",
+                "sha256:c132345d12ce551242c87269de812483f5bcc87cdbb4722e48487ba194f9fdce"
+            ],
+            "index": "pypi",
+            "markers": "python_version >= '3.8'",
+            "version": "==8.3.2"
+        },
+        "tomli": {
+            "hashes": [
+                "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc",
+                "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"
+            ],
+            "markers": "python_version < '3.11'",
+            "version": "==2.0.1"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d",
+                "sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8"
             ],
             "index": "pypi",
             "markers": "python_version >= '3.8'",
-            "version": "==8.0.1"
+            "version": "==4.12.2"
         }
     }
 }
diff --git a/hexagon/actions/internal/install_cli.py b/hexagon/actions/internal/install_cli.py
index 9775318b..00a313f2 100644
--- a/hexagon/actions/internal/install_cli.py
+++ b/hexagon/actions/internal/install_cli.py
@@ -1,7 +1,7 @@
 import os
 from pathlib import Path
 
-from pydantic import FilePath, validator, DirectoryPath
+from pydantic import FilePath, DirectoryPath, field_validator
 
 from hexagon.runtime.dependencies import scan_and_install_dependencies
 from hexagon.runtime.singletons import configuration
@@ -25,14 +25,13 @@ class Args(ToolArgs):
         prompt_default=str(os.path.expanduser(os.path.join("~", ".local", "bin"))),
     )
 
-    @validator("src_path")
-    def is_yaml(cls, arg):
-        if isinstance(arg, str):
-            if arg.endswith(".yaml") or arg.endswith(".yml"):
-                return arg
-        else:
-            if arg.value.suffix == ".yaml" or arg.value.suffix == ".yml":
-                return arg
+    @field_validator("src_path")
+    @classmethod
+    def is_yaml(cls, arg: PositionalArg[FilePath]):
+        if arg.value is None:
+            return arg
+        if arg.value.suffix == ".yaml" or arg.value.suffix == ".yml":
+            return arg
         raise ValueError(_("error.actions.internal.install_cli.select_valid_file"))
 
 
diff --git a/hexagon/domain/errors.py b/hexagon/domain/errors.py
deleted file mode 100644
index 7f111a42..00000000
--- a/hexagon/domain/errors.py
+++ /dev/null
@@ -1,2 +0,0 @@
-class LoggedException(Exception):
-    pass
diff --git a/hexagon/domain/tool/__init__.py b/hexagon/domain/tool/__init__.py
index 92d21980..5068eb11 100644
--- a/hexagon/domain/tool/__init__.py
+++ b/hexagon/domain/tool/__init__.py
@@ -48,7 +48,7 @@ class GroupTool(Tool):
     tools: Union[str, List[Union[ActionTool, GroupTool]]]
 
 
-GroupTool.update_forward_refs()
+GroupTool.model_rebuild()
 
 
 class ToolGroupConfigFile(BaseModel):
diff --git a/hexagon/runtime/configuration.py b/hexagon/runtime/configuration.py
index df5298db..9f65acd1 100644
--- a/hexagon/runtime/configuration.py
+++ b/hexagon/runtime/configuration.py
@@ -93,7 +93,9 @@ def save(self) -> Tuple[Cli, List[Tool], List[Env]]:
 
     def add_tool(self, tool: Union[ActionTool, GroupTool]):
         self.__config.tools.append(tool)
-        self.__yaml["tools"].append(tool.dict(exclude_none=True, exclude_unset=True))
+        self.__yaml["tools"].append(
+            tool.model_dump(mode="json", exclude_none=True, exclude_unset=True)
+        )
 
     def update_custom_tools_path(self, value, comment=None, position=0):
         self.__config.cli.custom_tools_dir = value
diff --git a/hexagon/runtime/execute/action.py b/hexagon/runtime/execute/action.py
index 072a1e93..579ee06b 100644
--- a/hexagon/runtime/execute/action.py
+++ b/hexagon/runtime/execute/action.py
@@ -123,6 +123,7 @@ def _execute_python_module(
     except HexagonError as e:
         raise e
     except Exception:
+        # TODO: pass along traceback
         raise ActionExecuteError(action_id, custom_tools_path)
 
 
@@ -237,9 +238,9 @@ def __load_module(module: str):
 
 def __call_subprocess(command: str, env: Optional[Env], tool: ActionTool):
     env_vars = os.environ.copy()
-    env_vars[ENVVAR_EXECUTION_TOOL] = tool.json()
+    env_vars[ENVVAR_EXECUTION_TOOL] = tool.model_dump_json()
     if env:
-        env_vars[ENVVAR_EXECUTION_ENV] = env.json()
+        env_vars[ENVVAR_EXECUTION_ENV] = env.model_dump_json()
     return (
         subprocess.call(command, shell=True, env=env_vars),
         command,
diff --git a/hexagon/runtime/execute/tool.py b/hexagon/runtime/execute/tool.py
index a2124afb..22ad3dbe 100644
--- a/hexagon/runtime/execute/tool.py
+++ b/hexagon/runtime/execute/tool.py
@@ -10,9 +10,9 @@
 )
 from hexagon.runtime.execute.action import execute_action
 from hexagon.runtime.parse_args import parse_cli_args
-from hexagon.support.input.args import CliArgs
 from hexagon.runtime.wax import search_by_name_or_alias, select_env, select_tool
 from hexagon.support.hooks import HexagonHooks
+from hexagon.support.input.args import CliArgs
 from hexagon.support.tracer import tracer
 
 
diff --git a/hexagon/runtime/options.py b/hexagon/runtime/options.py
index af84909d..789bfbda 100644
--- a/hexagon/runtime/options.py
+++ b/hexagon/runtime/options.py
@@ -1,22 +1,18 @@
 import datetime
-from typing import Any, Dict, Optional
+from typing import Optional, Type
 
-from pydantic import BaseSettings, ValidationError
+from pydantic import ValidationError
 from pydantic.types import DirectoryPath
+from pydantic_settings import (
+    BaseSettings,
+    PydanticBaseSettingsSource,
+    SettingsConfigDict,
+    InitSettingsSource,
+)
 
 from hexagon.runtime.yaml import YamlValidationError
 
 
-def _local_settings_source(settings: BaseSettings) -> Dict[str, Any]:
-    from hexagon.support.storage import (
-        HEXAGON_STORAGE_APP,
-        HexagonStorageKeys,
-        load_user_data,
-    )
-
-    return load_user_data(HexagonStorageKeys.options.value, HEXAGON_STORAGE_APP) or {}
-
-
 def _save_settings_to_source(options):
     from hexagon.support.storage import (
         HEXAGON_STORAGE_APP,
@@ -33,11 +29,29 @@ def _save_settings_to_source(options):
     )
 
 
+class UserDataSettingsSource(InitSettingsSource):
+    def __init__(self, settings_cls: type[BaseSettings]):
+        from hexagon.support.storage import (
+            HEXAGON_STORAGE_APP,
+            HexagonStorageKeys,
+            load_user_data,
+        )
+
+        local_options = (
+            load_user_data(HexagonStorageKeys.options.value, app=HEXAGON_STORAGE_APP)
+            or {}
+        )
+
+        super().__init__(settings_cls, local_options)
+
+
 class KeymapOptions(BaseSettings):
     create_dir: str = "c-p"
 
 
 class Options(BaseSettings):
+    model_config = SettingsConfigDict(env_prefix="HEXAGON_")
+
     theme: Optional[str] = "default"
     update_time_between_checks: Optional[datetime.timedelta] = datetime.timedelta(
         days=1
@@ -50,17 +64,21 @@ class Options(BaseSettings):
     hints_disabled: Optional[bool] = False
     keymap: KeymapOptions = KeymapOptions()
 
-    class Config:
-        env_prefix = "HEXAGON_"
-
-        @classmethod
-        def customise_sources(cls, init_settings, env_settings, file_secret_settings):
-            return (
-                init_settings,
-                env_settings,
-                _local_settings_source,
-                file_secret_settings,
-            )
+    @classmethod
+    def settings_customise_sources(
+        cls,
+        settings_cls: Type[BaseSettings],
+        init_settings: PydanticBaseSettingsSource,
+        env_settings: PydanticBaseSettingsSource,
+        dotenv_settings: PydanticBaseSettingsSource,
+        file_secret_settings: PydanticBaseSettingsSource,
+    ):
+        return (
+            init_settings,
+            env_settings,
+            UserDataSettingsSource(settings_cls),
+            file_secret_settings,
+        )
 
 
 def get_options(init_settings: dict) -> Options:
diff --git a/hexagon/runtime/parse_args.py b/hexagon/runtime/parse_args.py
index 9ce7a34d..a6d3f3ec 100644
--- a/hexagon/runtime/parse_args.py
+++ b/hexagon/runtime/parse_args.py
@@ -1,8 +1,9 @@
 import argparse
 import sys
-from typing import List
+from typing import List, get_origin
 
-from pydantic.fields import ModelField
+from pydantic import TypeAdapter
+from pydantic_core import PydanticUndefined
 
 from hexagon.support.input.args import (
     CliArgs,
@@ -10,7 +11,8 @@
     OptionalArg,
     PositionalArg,
 )
-from hexagon.utils.typing import should_support_multiple_args, field_info
+from hexagon.support.input.args.field_reference import FieldReference
+from hexagon.typing import should_support_multiple_args, field_type_information
 
 
 # noinspection PyProtectedMember
@@ -47,6 +49,7 @@ def parse_cli_args(args=None, model=CliArgs, **kwargs):
             + count,
         }
     )
+    # return model.model_validate_json(json.dumps(data))
     return model(**{k: v for k, v in data.items() if v is not None})
 
 
@@ -63,11 +66,11 @@ def init_arg_parser(
     if sys.version_info < (3, 8):
         __polyfill_extend_action(__p)
 
-    for field in model.__fields__.values():
-        if fields and field.name not in fields:
+    for name, field in model.model_fields.items():
+        if fields and name not in fields:
             continue
-        if field.type_ in [PositionalArg, OptionalArg]:
-            __add_parser_argument(__p, field)
+        if get_origin(field.annotation) in [PositionalArg, OptionalArg]:
+            __add_parser_argument(__p, FieldReference(name, field))
     return __p
 
 
@@ -81,16 +84,17 @@ def __call__(self, parser, namespace, values, option_string=None):
     __p.register("action", "extend", ExtendAction)
 
 
-def __add_parser_argument(parser, field: ModelField):
-    reprs = field.type_.cli_repr(field)
-    nargs, action, constant_default, is_bool = __config_base_on_type(field)
-    # type and default behavior is handled by pydantic
+def __add_parser_argument(parser, field: FieldReference):
+    reprs = field.info.annotation.cli_repr(field)
+    nargs, action, constant_default, is_bool = __config_base_on_type(field.info)
+
+    default = None if field.info.default is PydanticUndefined else field.info.default
     parser.add_argument(
         *[r for r in reprs if r],
         nargs=nargs,
         action=action,
         const=constant_default,
-        help=f"{field.field_info.description or field.name} (default: {field.default})",
+        help=f"{field.info.description or field.name} (default: {default})",
     )
     if is_bool:
         parser.add_argument(
@@ -109,22 +113,22 @@ def __config_base_on_type(field):
     :param field:
     :return:
     """
-    field_type, _, __ = field_info(field)
-    is_bool = field_type == bool
-    constant_default = "true" if is_bool else None
+    field_type, _, __ = field_type_information(field)
+    constant_default = "true" if field_type.is_bool else None
     return (
         (
             "*",
             "extend",
             constant_default,
-            is_bool,
+            field_type.is_bool,
         )
-        if field.type_ == OptionalArg and should_support_multiple_args(field)
+        if get_origin(field.annotation) == OptionalArg
+        and should_support_multiple_args(field)
         else (
             "?",
             "store",
             constant_default,
-            is_bool,
+            field_type.is_bool,
         )
     )
 
@@ -132,9 +136,9 @@ def __config_base_on_type(field):
 def __guess_optional_keys(extra: List[str]):
     """
     Return a dict by guessing optional keys from a list of arguments, e.g.:
-    ['--number', '123', '--name', 'John', '--name', 'Doe'] -> {'number': '123','name': ['John', 'Doe']}
-    ['123'] -> {'0': '123'}
-    ['123', '--letter', 'A'] -> {'0': '123', 'letter': 'A'}
+    ['--number', '123', '--name', 'John', '--name', 'Doe'] -> {'number': 123,'name': ['John', 'Doe']}
+    ['123'] -> {'0': 123}
+    ['123', '--letter', 'A'] -> {'0': 123, 'letter': 'A'}
     :param extra:
     :return:
     """
@@ -154,16 +158,16 @@ def __args_to_key_vals(extra):
         if current.startswith(ARGUMENT_KEY_PREFIX):
             if "=" in current:
                 key, value = current[2:].split("=")
-                result.append({key: value})
+                result.append({key: __adapt_value_type(value)})
                 i += 1
             elif not next_ or next_.startswith(ARGUMENT_KEY_PREFIX):
                 result.append({current[2:]: True})
                 i += 1
             else:
-                result.append({current[2:]: next_})
+                result.append({current[2:]: __adapt_value_type(next_)})
                 i += 2
         else:
-            result.append({str(arg_index): current})
+            result.append({str(arg_index): __adapt_value_type(current)})
             i += 1
             arg_index += 1
     return result
@@ -185,3 +189,18 @@ def __group_by_key_appending(result):
 
 def __bool_negated_key(name: str):
     return f"{ARGUMENT_KEY_PREFIX * 2}no-{name}"
+
+
+def __adapt_value_type(value: str):
+    result = __try_to_parse_type(value, bool)
+    if result is not None:
+        return result
+    return __try_to_parse_type(value, int) or __try_to_parse_type(value, float) or value
+
+
+def __try_to_parse_type(value: str, target: type):
+    adapter = TypeAdapter(target)
+    try:
+        return adapter.validate_strings(value)
+    except ValueError:
+        return None
diff --git a/hexagon/runtime/update/hexagon.py b/hexagon/runtime/update/hexagon.py
index dd3e4560..32240f54 100644
--- a/hexagon/runtime/update/hexagon.py
+++ b/hexagon/runtime/update/hexagon.py
@@ -26,9 +26,11 @@ def check_for_hexagon_updates():
 
     with log.status(_("msg.support.update.hexagon.checking_new_versions")):
         current_version = version.local(
-            override=os.getenv("HEXAGON_TEST_VERSION_OVERRIDE", None)
+            override=os.getenv("HEXAGON_TEST_LOCAL_VERSION_OVERRIDE", None)
+        )
+        latest_version = version.latest(
+            override=os.getenv("HEXAGON_TEST_LATEST_VERSION_OVERRIDE", None)
         )
-        latest_version = version.latest()
 
         if current_version >= latest_version:
             return
diff --git a/hexagon/runtime/update/version.py b/hexagon/runtime/update/version.py
index 18f0873c..71ec2039 100644
--- a/hexagon/runtime/update/version.py
+++ b/hexagon/runtime/update/version.py
@@ -23,9 +23,10 @@ def _local_version():
         return pkg_resources.require("hexagon")[0].version
 
 
-def latest():
+def latest(override: str = None):
     latest_release_request = Request(
-        f"https://api.github.com/repos/{REPO_ORG}/{REPO_NAME}/releases/latest"
+        override
+        or f"https://api.github.com/repos/{REPO_ORG}/{REPO_NAME}/releases/latest"
     )
     add_github_access_token(latest_release_request)
     latest_github_release = json.load(urlopen(latest_release_request))
diff --git a/hexagon/runtime/yaml.py b/hexagon/runtime/yaml.py
index 289e08f3..d664864f 100644
--- a/hexagon/runtime/yaml.py
+++ b/hexagon/runtime/yaml.py
@@ -44,15 +44,16 @@ def _error_printer(self, logger: Logger):
             )
         )
         for err in self.errors:
+            loc_ = tuple(x for x in err["loc"] if x not in ["ActionTool", "GroupTool"])
             logger.error(
                 os.linesep  # this \n can not go in the .po file because it breaks msgmt
                 + _("error.support.yaml.error_at").format(
-                    loc=".".join(map(lambda i: str(i), err["loc"])), message=err["msg"]
+                    loc=".".join(map(lambda i: str(i), loc_)), message=err["msg"]
                 )
             )
             if self.yaml_content and yaml:
                 (start, line_number, end) = self.__lines_of_error(
-                    err, self.yaml_content
+                    loc_, self.yaml_content
                 )
                 logger.example(
                     "\n".join(self.actual_yaml_file.splitlines()[start:end]),
@@ -64,8 +65,8 @@ def _error_printer(self, logger: Logger):
                 )
 
     @classmethod
-    def __lines_of_error(cls, err, ruamel_yaml):
-        line_number = cls.__yaml_line_number(ruamel_yaml, err["loc"])
+    def __lines_of_error(cls, loc, ruamel_yaml):
+        line_number = cls.__yaml_line_number(ruamel_yaml, loc)
         return (
             max(0, line_number - 3),
             line_number,
@@ -73,7 +74,9 @@ def __lines_of_error(cls, err, ruamel_yaml):
         )
 
     @classmethod
-    def __yaml_line_number(cls, yml, loc: list, line_count_hack: int = 0):
+    def __yaml_line_number(
+        cls, yml, loc: tuple, line_count_hack: int = 0, previous_lc=None
+    ):
         """
         Get line number of location by accessing YAML metadata
 
@@ -83,6 +86,8 @@ def __yaml_line_number(cls, yml, loc: list, line_count_hack: int = 0):
         for some reason ruamel .lc returns one less line_number for each level
         :return: the line number of loc
         """
+        if yml is None:
+            return previous_lc.line + line_count_hack
         if len(loc) == 1:
             try:
                 return (
@@ -96,5 +101,8 @@ def __yaml_line_number(cls, yml, loc: list, line_count_hack: int = 0):
                 ) + yml.lc.line
         else:
             return cls.__yaml_line_number(
-                yml[loc[0]], loc[1:], line_count_hack=line_count_hack + 1
+                yml[loc[0]],
+                loc[1:],
+                line_count_hack=line_count_hack + 1,
+                previous_lc=yml.lc,
             )
diff --git a/hexagon/support/input/args/__init__.py b/hexagon/support/input/args/__init__.py
index 48adf22d..31ec1227 100644
--- a/hexagon/support/input/args/__init__.py
+++ b/hexagon/support/input/args/__init__.py
@@ -1,8 +1,10 @@
 from typing import Optional, Union, Any, Callable
 
-from pydantic import (
+from pydantic.fields import (
     Field as PydanticField,
 )
+
+from hexagon.support.input.args.cli_args import CliArgs
 from hexagon.support.input.args.hexagon_args import (
     ARGUMENT_KEY_PREFIX,
     HexagonArg,
@@ -10,7 +12,6 @@
     OptionalArg,
 )
 from hexagon.support.input.args.tool_args import ToolArgs
-from hexagon.support.input.args.cli_args import CliArgs
 
 ARGUMENT_KEY_PREFIX = ARGUMENT_KEY_PREFIX
 HexagonArg = HexagonArg
diff --git a/hexagon/support/input/args/cli_args.py b/hexagon/support/input/args/cli_args.py
index 85fcbea8..e6a26d9f 100644
--- a/hexagon/support/input/args/cli_args.py
+++ b/hexagon/support/input/args/cli_args.py
@@ -10,7 +10,7 @@ class CliArgs(BaseModel):
     tool: PositionalArg[Optional[str]] = None
     env: PositionalArg[Optional[str]] = None
 
-    extra_args: Optional[Dict[str, Union[list, bool, int, str]]] = None
+    extra_args: Optional[Dict[str, Union[list, bool, int, float, str]]] = None
     raw_extra_args: Optional[List[str]] = None
     total_args: int
 
diff --git a/hexagon/support/input/args/errors.py b/hexagon/support/input/args/errors.py
new file mode 100644
index 00000000..780385bb
--- /dev/null
+++ b/hexagon/support/input/args/errors.py
@@ -0,0 +1,10 @@
+from hexagon.domain.hexagon_error import HexagonError
+
+
+class MultipleHintsNotSupportedError(HexagonError):
+    def __init__(self, types: tuple) -> None:
+        self.message = f"Multiple hints not supported: {types}"
+        super().__init__(self._error_printer)
+
+    def _error_printer(self, logger):
+        logger.error(self.message)
diff --git a/hexagon/support/input/args/field_reference.py b/hexagon/support/input/args/field_reference.py
new file mode 100644
index 00000000..b10c4fef
--- /dev/null
+++ b/hexagon/support/input/args/field_reference.py
@@ -0,0 +1,9 @@
+import dataclasses
+
+from pydantic.fields import FieldInfo
+
+
+@dataclasses.dataclass
+class FieldReference:
+    name: str
+    info: FieldInfo
diff --git a/hexagon/support/input/args/hexagon_args.py b/hexagon/support/input/args/hexagon_args.py
index 1738103f..b5edd5ff 100644
--- a/hexagon/support/input/args/hexagon_args.py
+++ b/hexagon/support/input/args/hexagon_args.py
@@ -1,9 +1,11 @@
 import abc
-from copy import copy
-from typing import Generic, Union, Callable, TypeVar
+from typing import Generic, Union, Callable, TypeVar, get_args, Any, get_origin
 
-from pydantic import ValidationError
-from pydantic.fields import ModelField
+from pydantic import GetCoreSchemaHandler
+from pydantic_core import core_schema
+from pydantic_core.core_schema import ValidatorFunctionWrapHandler
+
+from hexagon.support.input.args.field_reference import FieldReference
 
 ARGUMENT_KEY_PREFIX = "-"
 
@@ -17,10 +19,6 @@ class HexagonArg(Generic[T]):
     def __init__(self, value: T):
         self.__value__ = value
 
-    @classmethod
-    def __get_validators__(cls):
-        yield cls.arg_validator(cls)
-
     @property
     def value(self):
         if self.__model__:
@@ -46,56 +44,82 @@ def prompt(
             )
         return self.__model__.prompt(self.__field__, skip_trace, **kwargs)
 
-    def _init_refs(self, model, field):
+    def _init_refs(self, model: Any, field: FieldReference):
         self.__field__ = field
         self.__model__ = model
 
     @staticmethod
     @abc.abstractmethod
-    def cli_repr(field: ModelField):
+    def cli_repr(field: FieldReference):
         return
 
     def __str__(self, **kwargs):
         return str(self.__value__)
 
-    @staticmethod
-    def arg_validator(cls):
-        def validate(v, field: ModelField):
+    @classmethod
+    def __get_pydantic_core_schema__(
+        cls, source_type: Any, handler: GetCoreSchemaHandler
+    ) -> core_schema.CoreSchema:
+        origin = get_origin(source_type)
+        if origin is None:  # used as `x: Owner` without params
+            origin = source_type
+            item_tp = Any
+        else:
+            item_tp = get_args(source_type)[0]
+        # both calling handler(...) and handler.generate_schema(...)
+        # would work, but prefer the latter for conceptual and consistency reasons
+        item_schema = handler.generate_schema(item_tp)
+
+        def val_item(v: Any, handler: ValidatorFunctionWrapHandler) -> HexagonArg[Any]:
             if not isinstance(v, cls):
-                v = cls(v)
-            if not field.sub_fields:
-                # Generic parameters were not provided, ie: `name = PositionalArg`,
-                # so we don't try to validate them and just return the value as is
-                return v
-            value = field.sub_fields[0]
-
-            value.field_info.extra = copy(field.field_info.extra)
-            valid_value, error = value.validate(v.__value__, {}, loc="")
-            if error:
-                raise ValidationError([error], model=cls)
-            return cls(valid_value)
+                v = cls(handler(v))
+            return v
+
+        python_schema = core_schema.chain_schema(
+            # `chain_schema` means do the following steps in order:
+            [
+                # # Ensure the value is an instance of HexagonArg
+                # core_schema.is_instance_schema(cls),
+                # Use the item_schema to validate `items`
+                core_schema.no_info_wrap_validator_function(val_item, item_schema),
+            ]
+        )
 
-        return validate
+        return core_schema.json_or_python_schema(
+            # for JSON accept an object with name and item keys
+            json_schema=core_schema.chain_schema(
+                [
+                    item_schema,
+                    # after validating the json data convert it to python
+                    core_schema.no_info_before_validator_function(
+                        lambda v: cls(v),
+                        # note that we re-use the same schema here as below
+                        python_schema,
+                    ),
+                ]
+            ),
+            python_schema=python_schema,
+        )
 
 
 class PositionalArg(HexagonArg[T]):
     @staticmethod
-    def cli_repr(field: ModelField):
+    def cli_repr(field: FieldReference):
         return field.name, None
 
 
 class OptionalArg(HexagonArg[T]):
     @staticmethod
-    def cli_repr(field: ModelField):
-        def initials_from(name: str):
-            return "".join([w[0] for w in name.split("_")])
+    def cli_repr(field: FieldReference):
+        def initials():
+            return "".join([w[0] for w in field.name.split("_")])
 
         return (
             name_key(field.name.replace("_", "-")),
             alias_key(
-                field.alias.replace("_", "-")
-                if field.alias and field.alias != field.name
-                else initials_from(field.name)
+                field.info.alias.replace("_", "-")
+                if field.info.alias and field.info.alias != field.name
+                else initials()
             ),
         )
 
diff --git a/hexagon/support/input/args/tool_args.py b/hexagon/support/input/args/tool_args.py
index 067eee87..717655ef 100644
--- a/hexagon/support/input/args/tool_args.py
+++ b/hexagon/support/input/args/tool_args.py
@@ -1,49 +1,76 @@
 from inspect import isclass
-from typing import Optional, Dict, Union, List, Callable
+from typing import Optional, Dict, Union, List, Callable, get_origin
 
 from pydantic import BaseModel
-from pydantic.fields import ModelField
 
 from hexagon.support.input.args import HexagonArg
+from hexagon.support.input.args.field_reference import FieldReference
 from hexagon.support.input.args.hexagon_args import PositionalArg, OptionalArg
 
 
 class ToolArgs(BaseModel):
+    model_config = {
+        # pydantic config
+        "validate_assignment": True,
+        # hexagon config
+        "trace_on_access": True,
+        "trace_on_prompt": True,
+        "prompt_on_access": False,
+    }
     __tracer__ = None
     __prompt__ = None
     __fields_traced__ = set()
+    __fields_initialized__ = set()
+    __track_fields_set__ = False
 
     show_help: bool = False
     extra_args: Optional[Dict[str, Union[list, bool, int, str]]] = None
     raw_extra_args: Optional[List[str]] = None
 
     def __init__(self, **data):
-        for v in self.__fields__.values():
+        for _k, v in self.model_fields.items():
             if (
-                isclass(v.type_) and issubclass(v.type_, HexagonArg)
+                isclass(get_origin(v.annotation))
+                and issubclass(get_origin(v.annotation), HexagonArg)
             ) and not isinstance(v.default, HexagonArg):
                 v.default = (
                     PositionalArg(v.default)
-                    if v.type_ == PositionalArg
+                    if v.annotation == PositionalArg
                     else OptionalArg(v.default)
                 )
         super().__init__(**data)
+        self.__fields_initialized__ = set(self.model_fields_set)
+        # for each self.model_field that is not a HexagonArg, convert it to a HexagonArg
+        for k, v in self.model_fields.items():
+            if k in ["show_help", "extra_args", "raw_extra_args"]:
+                continue
+            value_ = self.__getattribute__(k, just_get=True)
+            if not isinstance(value_, HexagonArg):
+                self.__setattr__(
+                    k,
+                    (
+                        PositionalArg(value_)
+                        if get_origin(v.annotation) == PositionalArg
+                        else OptionalArg(value_)
+                    ),
+                )
+        self.__track_fields_set__ = True
 
     def __getattribute__(self, item, just_get=False):
         if just_get:
             return super().__getattribute__(item)
-        if item == "__fields__":
+        if item == "model_fields":
             return super().__getattribute__(item)
-        if item in self.__fields__:
-            field = self.__fields__[item]
+        if item in self.model_fields:
+            field = self.model_fields[item]
             value_ = self.__getattribute__(item, just_get=True)
 
             if (
                 hasattr(value_, "__value__")
                 and value_.__value__ is None
-                and self.__config__.prompt_on_access
+                and self.model_config["prompt_on_access"]
             ):
-                self.prompt(field)
+                self.prompt(FieldReference(item, field))
                 return self.__getattribute__(item, just_get=True)
 
         return super().__getattribute__(item)
@@ -53,31 +80,35 @@ def __setattr__(self, key, value):
         arg = self.__getattribute__(key, just_get=True)
         if isinstance(arg, HexagonArg):
             # noinspection PyProtectedMember
-            arg._init_refs(self, self.__fields__.get(key))
+            arg._init_refs(self, FieldReference(key, self.model_fields.get(key)))
+        if self.__track_fields_set__:
+            self.__fields_initialized__.add(key)
 
-    def trace(self, field: Union[ModelField, str], retrace=False):
+    def trace(self, field: Union[FieldReference, str], retrace=False):
         if not self.__tracer__:
             raise ValueError("Tracer not initialized. Did _with_tracer() get called?")
 
         model_field = (
-            field if isinstance(field, ModelField) else self.__fields__.get(field)
+            field
+            if isinstance(field, FieldReference)
+            else self.model_fields.get(field.name)
         )
         if not model_field:
             raise ValueError(
-                f"field [{field}] not found, must be a field name or a ModelField instance"
+                f"field [{field}] not found, must be a field name or a FieldReference instance"
             )
 
-        if not self.__config__.trace_on_access:
+        if not self.model_config["trace_on_access"]:
             return
 
         value_ = self.__getattribute__(model_field.name, just_get=True)
 
         if (
             retrace or model_field.name not in self.__fields_traced__
-        ) and model_field.name in self.__fields_set__:
-            if model_field.type_ == PositionalArg:
+        ) and model_field.name in self.__fields_initialized__:
+            if get_origin(model_field.info.annotation) == PositionalArg:
                 self.__tracer__.tracing(f"arg_{model_field.name}", value_.__value__)
-            elif model_field.type_ == OptionalArg:
+            elif get_origin(model_field.info.annotation) == OptionalArg:
                 n, a = OptionalArg.cli_repr(model_field)
                 self.__tracer__.tracing(
                     f"arg_{model_field.name}", value_.__value__, key=n, key_alias=a
@@ -86,7 +117,7 @@ def trace(self, field: Union[ModelField, str], retrace=False):
 
     def prompt(
         self,
-        field: Union[ModelField, str],
+        field: Union[FieldReference, str],
         skip_trace: Union[bool, Callable] = False,
         **kwargs,
     ):
@@ -94,11 +125,13 @@ def prompt(
             raise ValueError("prompt not initialized. Did _with_prompt() get called?")
 
         model_field = (
-            field if isinstance(field, ModelField) else self.__fields__.get(field)
+            field
+            if isinstance(field, FieldReference)
+            else self.model_fields.get(field.name)
         )
         if not model_field:
             raise ValueError(
-                f"field [{field}] not found, must be a field name or a ModelField instance"
+                f"field [{field}] not found, must be a field name or a FieldReference instance"
             )
 
         value_ = self.__prompt__.query_field(
@@ -111,35 +144,25 @@ def prompt(
         if callable(skip_trace):
             skip_trace = skip_trace(value_)
 
-        if not skip_trace and self.__config__.trace_on_prompt:
+        if not skip_trace and self.model_config["trace_on_prompt"]:
             self.trace(model_field, retrace=not skip_trace)
 
         return getattribute__.__value__
 
     def _with_tracer(self, tracer):
         self.__tracer__ = tracer
-        for k, field in self.__fields__.items():
+        for k, field in self.model_fields.items():
             arg = self.__getattribute__(k, just_get=True)
             if isinstance(arg, HexagonArg):
                 # noinspection PyProtectedMember
-                arg._init_refs(self, field)
+                arg._init_refs(self, FieldReference(k, field))
         return self
 
     def _with_prompt(self, prompt):
         self.__prompt__ = prompt
-        for k, field in self.__fields__.items():
+        for k, field in self.model_fields.items():
             arg = self.__getattribute__(k, just_get=True)
             if isinstance(arg, HexagonArg):
                 # noinspection PyProtectedMember
-                arg._init_refs(self, field)
+                arg._init_refs(self, FieldReference(k, field))
         return self
-
-    class Config:
-        # pydantic config
-        underscore_attrs_are_private = True
-        validate_assignment = True
-
-        # hexagon config
-        trace_on_access = True
-        trace_on_prompt = True
-        prompt_on_access = False
diff --git a/hexagon/support/input/prompt/prompt.py b/hexagon/support/input/prompt/prompt.py
index da712a37..138509b7 100644
--- a/hexagon/support/input/prompt/prompt.py
+++ b/hexagon/support/input/prompt/prompt.py
@@ -10,17 +10,18 @@
 from prompt_toolkit.buffer import ValidationState
 from prompt_toolkit.document import Document
 from prompt_toolkit.validation import ValidationError, Validator
-from pydantic import ValidationError as PydanticValidationError, DirectoryPath
-from pydantic.fields import ModelField, Validator as PydanticValidator
+from pydantic import ValidationError as PydanticValidationError, BeforeValidator
+from pydantic_core import SchemaValidator
 
 from hexagon.domain.hexagon_error import ListHexagonError
 from hexagon.runtime.singletons import options
 from hexagon.support.input.args import HexagonArg
+from hexagon.support.input.args.field_reference import FieldReference
 from hexagon.support.input.prompt.for_all_methods import for_all_methods
 from hexagon.support.input.prompt.hints import HintsBuilder
 from hexagon.support.input.types import path_validator
 from hexagon.support.output.printer import log
-from hexagon.utils.typing import field_info
+from hexagon.typing import field_type_information, TypeInformation
 
 
 class HexagonArgumentSetupError(ListHexagonError):
@@ -52,12 +53,30 @@ def validate(self, document: Document) -> None:
             raise ValidationError(message=e.args[0], cursor_position=len(document.text))
 
 
-def default_validator(model_field: ModelField, mapper=lambda x: x):
+def default_validator(field_reference: FieldReference, mapper=lambda x: x):
+    def __recurse_schema_until_model_fields_type(schema):
+        if schema.get("type") == "model-fields":
+            return schema["fields"]
+        return __recurse_schema_until_model_fields_type(schema["schema"])
+
     def func(cls, value):
-        value, error = model_field.validate(mapper(value), {}, loc="")
-        if error:
-            raise PydanticValidationError([error], model=cls)
-        return value
+        schema__ = cls.__pydantic_core_schema__
+        target_field_schema: Dict = __recurse_schema_until_model_fields_type(schema__)[
+            field_reference.name
+        ]["schema"]
+        validator = SchemaValidator(
+            {
+                "type": "definitions",
+                "schema": target_field_schema,
+                "definitions": schema__.get("definitions"),
+            }
+            if "definitions" in schema__
+            else target_field_schema
+        )
+        try:
+            return validator.validate_python(mapper(value))
+        except ValidationError as e:
+            raise PydanticValidationError([e], model=cls)
 
     return func
 
@@ -66,16 +85,17 @@ def list_mapper(v):
     return v.strip().split("\n")
 
 
-def set_default(invocation_extras, model_field: ModelField):
-    if model_field.field_info.extra.get("prompt_default"):
-        return {"default": model_field.field_info.extra["prompt_default"]}
+def set_default(invocation_extras, field_reference: FieldReference):
+    extras = field_reference.info.json_schema_extra or {}
+    if extras.get("prompt_default"):
+        return {"default": extras["prompt_default"]}
     if "default" in invocation_extras:
         return {"default": invocation_extras["default"]}
-    elif model_field.default:
+    elif field_reference.info.default:
         default = (
-            model_field.default.value
-            if isinstance(model_field.default, HexagonArg)
-            else model_field.default
+            field_reference.info.default.value
+            if isinstance(field_reference.info.default, HexagonArg)
+            else field_reference.info.default
         )
         if default is not None:
             return {"default": default}
@@ -101,7 +121,7 @@ class InquiryType(Enum):
 
 
 def _determine_expected_inquiry(
-    iterable, of_enum, type_, declaration_extras, invocation_extras
+    iterable, of_enum, field_type, declaration_extras, invocation_extras
 ) -> (InquiryType, dict):
     """
     TODO: move this to a separate module and unit test it
@@ -124,13 +144,13 @@ def _determine_expected_inquiry(
         )
     elif of_enum:
         query = InquiryType.ENUM if not searchable else InquiryType.ENUM_SEARCHABLE
-    elif issubclass(type_, Path):
+    elif issubclass(field_type.base_type, Path):
         query = InquiryType.PATH if not searchable else InquiryType.PATH_SEARCHABLE
-    elif issubclass(type_, bool):
+    elif issubclass(field_type.base_type, bool):
         query = InquiryType.BOOLEAN
-    elif issubclass(type_, int):
+    elif issubclass(field_type.base_type, int):
         query = InquiryType.INT
-    elif issubclass(type_, float):
+    elif issubclass(field_type.base_type, float):
         query = InquiryType.FLOAT
     elif extras.get("secret", False):
         query = InquiryType.SECRET
@@ -140,18 +160,18 @@ def _determine_expected_inquiry(
 
 @for_all_methods(log.status_aware, exclude=["query_field"])
 class Prompt:
-    def query_field(self, model_field: ModelField, model_class, **kwargs):
+    def query_field(self, field_reference: FieldReference, model_class, **kwargs):
         # TODO: this method is a mess, refactor it
-        declaration_extras = copy(model_field.field_info.extra)
+        declaration_extras = copy(field_reference.info.json_schema_extra or {})
         invocation_extras = copy(kwargs)
         inquiry_args = {
             "message": declaration_extras.get("prompt_message", None)
-            or f"Enter {model_field.name}:",
+            or f"Enter {field_reference.name}:",
             "instruction": declaration_extras.get("prompt_instruction", None),
         }
-        inquiry_args.update(set_default(invocation_extras, model_field))
+        inquiry_args.update(set_default(invocation_extras, field_reference))
 
-        field_type, iterable, of_enum = field_info(model_field)
+        field_type, iterable, of_enum = field_type_information(field_reference.info)
 
         inquiry_type, extras = _determine_expected_inquiry(
             iterable, of_enum, field_type, declaration_extras, invocation_extras
@@ -181,22 +201,14 @@ def query_field(self, model_field: ModelField, model_class, **kwargs):
             field_type=field_type,
         )
 
-        if model_field.sub_fields:
-            validators_ = {
-                "default": PydanticValidator(
-                    default_validator(model_field, mapper=mapper), check_fields=True
-                )
-            }
-            if model_field.class_validators:
-                validators_.update(
-                    {
-                        **model_field.class_validators,
-                    }
+        inquiry_args["validate"] = PromptValidator(
+            {
+                "default": BeforeValidator(
+                    default_validator(field_reference, mapper=mapper)
                 )
-            inquiry_args["validate"] = PromptValidator(
-                validators_,
-                model_class,
-            )
+            },
+            model_class,
+        )
 
         # FIXME: this is working by chance, it's not the best way to do it
         if "searchable" in invocation_extras:
@@ -208,7 +220,7 @@ def query_field(self, model_field: ModelField, model_class, **kwargs):
         except TypeError as e:
             if "__init__() got an unexpected keyword argument" in e.args[0]:
                 prop = re.search(".*\s('\w+')$", e.args[0]).group(1).strip()
-                err = HexagonArgumentSetupError(model_field.name, prop)
+                err = HexagonArgumentSetupError(field_reference.name, prop)
             else:
                 raise e
 
@@ -325,7 +337,7 @@ def setup_enum_list(
     self: Prompt,
     inquiry_type: Literal[InquiryType.ENUM_LIST, InquiryType.ENUM_LIST_SEARCHABLE],
     inquiry_args: Dict[str, Any],
-    field_type: Any,
+    field_type: TypeInformation,
     **_,
 ) -> (Callable, Callable):
     inquiry_args["choices"] = [
@@ -334,7 +346,7 @@ def setup_enum_list(
             value=x,
             enabled=("default" in inquiry_args and x in inquiry_args["default"]),
         )
-        for x in field_type.__args__[0]
+        for x in field_type.base_type.__args__[0]
     ]
     inquiry_args["default"] = ""
     if inquiry_type == InquiryType.ENUM_LIST_SEARCHABLE:
@@ -361,10 +373,12 @@ def setup_enum(
     self: Prompt,
     inquiry_type: Literal[InquiryType.ENUM, InquiryType.ENUM_SEARCHABLE],
     inquiry_args: Dict[str, Any],
-    field_type: Any,
+    field_type: TypeInformation,
     **_,
 ) -> (Callable, Callable):
-    inquiry_args["choices"] = [{"name": x.name, "value": x} for x in field_type]
+    inquiry_args["choices"] = [
+        {"name": x.name, "value": x} for x in field_type.base_type
+    ]
     if inquiry_type == InquiryType.ENUM_SEARCHABLE:
         inquiry_args["default"] = (
             inquiry_args["default"].value if "default" in inquiry_args else None
@@ -394,22 +408,10 @@ def setup_path(
     inquiry_type: Literal[InquiryType.PATH, InquiryType.PATH_SEARCHABLE],
     inquiry_args: Dict[str, Any],
     extras: Dict[str, Any],
-    field_type: Any,
+    field_type: TypeInformation,
 ) -> (Callable, Callable):
     if inquiry_type == InquiryType.PATH_SEARCHABLE:
         choices: List[Dict[str, Any] or Any] = []
-        if "glob_extra_choices" in extras:
-            extra_choices = extras["glob_extra_choices"]
-            for choice in extra_choices:
-                choices.append(
-                    choice
-                    if isinstance(choice, dict)
-                    else {
-                        "name": choice,
-                        "value": choice,
-                    }
-                )
-
         if "glob" in extras:
             choices = choices + [
                 f
@@ -428,7 +430,7 @@ def setup_path(
         inquiry_args["only_directories"] = (
             inquiry_args["only_directories"]
             if "only_directories" in inquiry_args
-            else issubclass(field_type, DirectoryPath)
+            else field_type.is_directory_path
         )
         return self.path, lambda x: x
 
diff --git a/hexagon/support/input/types.py b/hexagon/support/input/types.py
index 23c8a1cd..f319dd10 100644
--- a/hexagon/support/input/types.py
+++ b/hexagon/support/input/types.py
@@ -1,4 +1,3 @@
-from copy import copy
 from pathlib import Path
 from typing import Any
 
@@ -7,77 +6,10 @@
     DirectoryPath as PydanticDirectoryPath,
     errors,
 )
-from pydantic.errors import _PathValueError
 
+FilePath = PydanticFilePath
 
-class FileNotExistsError(_PathValueError):
-    code = "path.not_exists"
-    msg_template = 'file at path "{path}" does not exist.'
-
-
-class DirectoryNotExistsError(_PathValueError):
-    code = "path.not_exists"
-    msg_template = 'directory at path "{path}" does not exist.'
-
-
-class FilePath(PydanticFilePath):
-    @classmethod
-    def __get_validators__(cls, *args, **kwargs):
-        yield path_validator
-        yield cls.validate
-
-    @classmethod
-    def validate(cls, value: Path, field) -> Path:
-        declaration_extras = copy(field.field_info.extra)
-
-        extra_choices = declaration_extras.get("glob_extra_choices")
-        if extra_choices:
-            for extra_choice in extra_choices:
-                if (
-                    value.name == extra_choice
-                    if isinstance(extra_choice, str)
-                    else extra_choice["value"]
-                ):
-                    return value
-
-        if not declaration_extras.get("allow_nonexistent", False):
-            if not value.exists():
-                raise FileNotExistsError(path=value)
-
-            if not value.is_file():
-                raise errors.PathNotAFileError(path=value)
-
-        return value
-
-
-class DirectoryPath(PydanticDirectoryPath):
-    @classmethod
-    def __get_validators__(cls):
-        yield path_validator
-        yield cls.validate
-
-    @classmethod
-    def validate(cls, value: Path, field) -> Path:
-        declaration_extras = copy(field.field_info.extra)
-
-        extra_choices = declaration_extras.get("glob_extra_choices")
-        if extra_choices:
-            for extra_choice in extra_choices:
-                if (
-                    value.name == extra_choice
-                    if isinstance(extra_choice, str)
-                    else extra_choice["value"]
-                ):
-                    return value
-
-        if not declaration_extras.get("allow_nonexistent", False):
-            if not value.exists():
-                raise DirectoryNotExistsError(path=value)
-
-            if not value.is_dir():
-                raise errors.PathNotADirectoryError(path=value)
-
-        return value
+DirectoryPath = PydanticDirectoryPath
 
 
 def path_validator(v: Any) -> Path:
diff --git a/hexagon/support/tracer.py b/hexagon/support/tracer.py
index 9414fa2e..d76e1dee 100644
--- a/hexagon/support/tracer.py
+++ b/hexagon/support/tracer.py
@@ -53,7 +53,7 @@ def tracing(
         key_alias: str = None,
     ):
         if not value:
-            pass
+            return
 
         for t in self._trace:
             if t.ref == ref:
diff --git a/hexagon/typing/__init__.py b/hexagon/typing/__init__.py
new file mode 100644
index 00000000..cd457f19
--- /dev/null
+++ b/hexagon/typing/__init__.py
@@ -0,0 +1,61 @@
+from enum import Enum
+from typing import Any, Union, get_args, get_origin, Annotated
+
+from pydantic.fields import FieldInfo
+
+from hexagon.support.input.args import HexagonArg
+from hexagon.support.input.args.errors import MultipleHintsNotSupportedError
+from hexagon.typing.type_information import TypeInformation
+
+
+def should_support_multiple_args(field: Union[FieldInfo, Any]):
+    type_ = _field_type(field) if isinstance(field, FieldInfo) else field
+    if hasattr(type_, "__origin__"):
+        type_ = type_.__origin__
+    return type_ in [list, tuple, set]
+
+
+def field_type_information(field: FieldInfo):
+    type_ = _field_type(field)
+    iterable = should_support_multiple_args(field)
+    if iterable and hasattr(type_, "__args__") and len(type_.__args__):
+        of_enum = _is_enumerable(type_.__args__[0])
+    else:
+        of_enum = _is_enumerable(type_)
+
+    return TypeInformation(type_), iterable, of_enum
+
+
+def _field_type(field: FieldInfo):
+    p = None
+    t = field.annotation
+    ars = get_args(t)
+    while __needs_unpacking(t):
+        p = t
+        ars = get_args(t)
+        t = ars[0]
+    if get_origin(p) is Annotated:
+        return p
+    if len(ars) > 1 and ars[-1] is not type(None):
+        raise MultipleHintsNotSupportedError(ars)
+    return t
+
+
+def __needs_unpacking(t):
+    try:
+        if (
+            get_origin(t) is Union
+            or issubclass(get_origin(t), HexagonArg)
+            or get_origin(t) is Annotated
+        ):
+            return True
+    except TypeError:
+        return False
+
+
+def _is_enumerable(type_):
+    try:
+        res = issubclass(type_, Enum)
+    except TypeError:
+        res = False
+    return res
diff --git a/hexagon/typing/type_information.py b/hexagon/typing/type_information.py
new file mode 100644
index 00000000..93927288
--- /dev/null
+++ b/hexagon/typing/type_information.py
@@ -0,0 +1,17 @@
+from typing import Any, get_args, Annotated, get_origin
+
+
+class TypeInformation:
+    def __init__(self, type_info: Any):
+        self.type_info = type_info
+        self.base_type = (
+            get_args(type_info)[0] if get_origin(type_info) is Annotated else type_info
+        )
+
+    @property
+    def is_bool(self):
+        return self.base_type == bool
+
+    @property
+    def is_directory_path(self):
+        return get_args(self.type_info)[1].path_type == "dir"
diff --git a/hexagon/utils/__init__.py b/hexagon/utils/__init__.py
deleted file mode 100644
index e69de29b..00000000
diff --git a/hexagon/utils/typing.py b/hexagon/utils/typing.py
deleted file mode 100644
index fc69b144..00000000
--- a/hexagon/utils/typing.py
+++ /dev/null
@@ -1,34 +0,0 @@
-from enum import Enum
-from typing import Any, Union
-
-from pydantic.fields import ModelField
-
-
-def should_support_multiple_args(field: Union[ModelField, Any]):
-    type_ = _field_type(field) if isinstance(field, ModelField) else field
-    if hasattr(type_, "__origin__"):
-        type_ = type_.__origin__
-    return type_ in [list, tuple, set]
-
-
-def field_info(field: ModelField):
-    type_ = _field_type(field)
-    iterable = should_support_multiple_args(field)
-    if iterable and hasattr(type_, "__args__") and len(type_.__args__):
-        of_enum = _is_enumerable(type_.__args__[0])
-    else:
-        of_enum = _is_enumerable(type_)
-
-    return type_, iterable, of_enum
-
-
-def _field_type(field: ModelField):
-    return field.sub_fields[0].outer_type_
-
-
-def _is_enumerable(type_):
-    try:
-        res = issubclass(type_, Enum)
-    except TypeError:
-        res = False
-    return res
diff --git a/locales/en/LC_MESSAGES/hexagon.po b/locales/en/LC_MESSAGES/hexagon.po
index cb0f958f..2e2dc27b 100644
--- a/locales/en/LC_MESSAGES/hexagon.po
+++ b/locales/en/LC_MESSAGES/hexagon.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-10 00:53+0200\n"
+"POT-Creation-Date: 2024-08-05 00:00+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -83,16 +83,16 @@ msgid "action.actions.internal.install_cli.commands_path"
 msgstr ""
 "Where do you want your CLI commands to be saved? This should be on your path"
 
-#: hexagon/actions/internal/install_cli.py:36
+#: hexagon/actions/internal/install_cli.py:35
 msgid "error.actions.internal.install_cli.select_valid_file"
 msgstr "Please select a valid YAML file"
 
-#: hexagon/actions/internal/install_cli.py:79
+#: hexagon/actions/internal/install_cli.py:78
 msgid "msg.actions.internal.install_cli.success"
 msgstr ""
 "[green]✓ [white][u]All done! Now you can execute your project's CLI like:"
 
-#: hexagon/actions/internal/install_cli.py:89
+#: hexagon/actions/internal/install_cli.py:88
 msgid "msg.actions.internal.install_cli.not_in_path"
 msgstr "[yellow]⚠ {dir} is not in your $PATH[/yellow]"
 
@@ -138,33 +138,33 @@ msgstr "Added alias to {file}"
 msgid "error.domain.configuration.group_tool_file_not_found"
 msgstr "File {config_file_path} could not be found"
 
-#: hexagon/runtime/configuration.py:118
+#: hexagon/runtime/configuration.py:120
 msgid "msg.domain.configuration.install_cli_long_name"
 msgstr "Install CLI"
 
-#: hexagon/runtime/configuration.py:119
+#: hexagon/runtime/configuration.py:121
 msgid "msg.domain.configuration.install_cli_description"
 msgstr "Install a custom project CLI from a YAML file."
 
-#: hexagon/runtime/configuration.py:125
+#: hexagon/runtime/configuration.py:127
 msgid "msg.domain.configuration.get_json_schema_long_name"
 msgstr "Get JSON Schema"
 
-#: hexagon/runtime/configuration.py:127
+#: hexagon/runtime/configuration.py:129
 msgid "msg.domain.configuration.get_json_schema_description"
 msgstr ""
 "Generate JSON Schema for hexagon config file (app.yml). Which can be "
 "imported in your IDE, or used for validation."
 
-#: hexagon/runtime/configuration.py:145
+#: hexagon/runtime/configuration.py:147
 msgid "msg.domain.configuration.save_alias_long_name"
 msgstr "Save Last Command as Shell Alias"
 
-#: hexagon/runtime/configuration.py:152
+#: hexagon/runtime/configuration.py:154
 msgid "msg.domain.configuration.replay_long_name"
 msgstr "Replay Last Command"
 
-#: hexagon/runtime/configuration.py:158
+#: hexagon/runtime/configuration.py:160
 msgid "msg.domain.configuration.create_tool_long_name"
 msgstr "Create A New Tool"
 
@@ -173,7 +173,7 @@ msgstr "Create A New Tool"
 msgid "msg.support.dependencies.installing_dependencies"
 msgstr "Installing {runtime} dependencies at path: {path}"
 
-#: hexagon/runtime/execute/action.py:143
+#: hexagon/runtime/execute/action.py:144
 msgid "msg.support.execute.action.tool_help_epilog"
 msgstr ""
 "To support tool arguments either add a model extending\n"
@@ -327,7 +327,7 @@ msgstr "Please select a valid tool"
 msgid "error.support.yaml.invalid_yaml"
 msgstr "There were {errors_length} error(s) in your YAML file: {yaml_path}"
 
-#: hexagon/runtime/yaml.py:49
+#: hexagon/runtime/yaml.py:50
 msgid "error.support.yaml.error_at"
 msgstr "✗ [u][bold]{loc}[/bold] -> {message}[/u]"
 
@@ -423,12 +423,12 @@ msgstr "HintsBuilder should have at least one hint"
 msgid "msg.support.prompt.hints.help"
 msgstr "help:"
 
-#: hexagon/support/input/prompt/prompt.py:30
+#: hexagon/support/input/prompt/prompt.py:34
 msgid "error.support.input.prompt.prompt.invalid_argument_setup"
 msgstr ""
 "Config [b]{property}[/b] is not supported for argument [b]{argument}[/b]"
 
-#: hexagon/support/input/prompt/prompt.py:294
+#: hexagon/support/input/prompt/prompt.py:310
 msgid "msg.support.prompt.prompt.directory_created"
 msgstr "📁 created directory: {path} [green]✓[/green]"
 
diff --git a/locales/es/LC_MESSAGES/hexagon.po b/locales/es/LC_MESSAGES/hexagon.po
index 17dcc5fb..29ef5b9e 100644
--- a/locales/es/LC_MESSAGES/hexagon.po
+++ b/locales/es/LC_MESSAGES/hexagon.po
@@ -6,7 +6,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-10 00:53+0200\n"
+"POT-Creation-Date: 2024-08-05 00:00+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -86,15 +86,15 @@ msgstr ""
 "¿En qué directorio te gustaría que se guarden los comandos de CLI? Debería "
 "estar en el PATH de tu SO."
 
-#: hexagon/actions/internal/install_cli.py:36
+#: hexagon/actions/internal/install_cli.py:35
 msgid "error.actions.internal.install_cli.select_valid_file"
 msgstr "Por favor, selecciona un YAML valido (*.yaml o *.yml)"
 
-#: hexagon/actions/internal/install_cli.py:79
+#: hexagon/actions/internal/install_cli.py:78
 msgid "msg.actions.internal.install_cli.success"
 msgstr "¡Listo! Ahora podes ejecutar tu CLI de la siguiente manera:"
 
-#: hexagon/actions/internal/install_cli.py:89
+#: hexagon/actions/internal/install_cli.py:88
 msgid "msg.actions.internal.install_cli.not_in_path"
 msgstr "[yellow]⚠ {dir} no está en tu $PATH[/yellow]"
 
@@ -140,31 +140,31 @@ msgstr "Se agrego el alias al archivo {file}"
 msgid "error.domain.configuration.group_tool_file_not_found"
 msgstr "No se encontró el archivo {config_file_path}"
 
-#: hexagon/runtime/configuration.py:118
+#: hexagon/runtime/configuration.py:120
 msgid "msg.domain.configuration.install_cli_long_name"
 msgstr "Instalar CLI"
 
-#: hexagon/runtime/configuration.py:119
+#: hexagon/runtime/configuration.py:121
 msgid "msg.domain.configuration.install_cli_description"
 msgstr "Instalar una CLI desde un archivo YAML de configuración."
 
-#: hexagon/runtime/configuration.py:125
+#: hexagon/runtime/configuration.py:127
 msgid "msg.domain.configuration.get_json_schema_long_name"
 msgstr "Crear una nueva herramienta"
 
-#: hexagon/runtime/configuration.py:127
+#: hexagon/runtime/configuration.py:129
 msgid "msg.domain.configuration.get_json_schema_description"
 msgstr "Instalar una CLI desde un archivo YAML de configuración."
 
-#: hexagon/runtime/configuration.py:145
+#: hexagon/runtime/configuration.py:147
 msgid "msg.domain.configuration.save_alias_long_name"
 msgstr "Guardar el último comando ejecutado como shell alias"
 
-#: hexagon/runtime/configuration.py:152
+#: hexagon/runtime/configuration.py:154
 msgid "msg.domain.configuration.replay_long_name"
 msgstr "Repetir el último comando ejecutado"
 
-#: hexagon/runtime/configuration.py:158
+#: hexagon/runtime/configuration.py:160
 msgid "msg.domain.configuration.create_tool_long_name"
 msgstr "Crear una nueva herramienta"
 
@@ -173,7 +173,7 @@ msgstr "Crear una nueva herramienta"
 msgid "msg.support.dependencies.installing_dependencies"
 msgstr "Instalando dependencias de {runtime} en directorio {path}"
 
-#: hexagon/runtime/execute/action.py:143
+#: hexagon/runtime/execute/action.py:144
 msgid "msg.support.execute.action.tool_help_epilog"
 msgstr ""
 "Para usar argumentos de CLI agregar a tu script\n"
@@ -330,7 +330,7 @@ msgstr "Por favor, selecciona una herramienta valida"
 msgid "error.support.yaml.invalid_yaml"
 msgstr "Hay {errors_length} errores en el YAML: {yaml_path}"
 
-#: hexagon/runtime/yaml.py:49
+#: hexagon/runtime/yaml.py:50
 msgid "error.support.yaml.error_at"
 msgstr "✗ [u][bold]{loc}[/bold] -> {message}[/u]"
 
@@ -426,11 +426,11 @@ msgstr "No se configuró HintsBuilder correctamente."
 msgid "msg.support.prompt.hints.help"
 msgstr "ayuda:"
 
-#: hexagon/support/input/prompt/prompt.py:30
+#: hexagon/support/input/prompt/prompt.py:34
 msgid "error.support.input.prompt.prompt.invalid_argument_setup"
 msgstr "Configuración {property} no es válida para argumento {argument}"
 
-#: hexagon/support/input/prompt/prompt.py:294
+#: hexagon/support/input/prompt/prompt.py:310
 msgid "msg.support.prompt.prompt.directory_created"
 msgstr "📁 directorio creado: {path} [green]✓[/green]"
 
diff --git a/locales/hexagon.pot b/locales/hexagon.pot
index e1b7bcce..61fd8790 100644
--- a/locales/hexagon.pot
+++ b/locales/hexagon.pot
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2024-04-10 00:53+0200\n"
+"POT-Creation-Date: 2024-08-05 00:00+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -77,15 +77,15 @@ msgstr ""
 msgid "action.actions.internal.install_cli.commands_path"
 msgstr ""
 
-#: hexagon/actions/internal/install_cli.py:36
+#: hexagon/actions/internal/install_cli.py:35
 msgid "error.actions.internal.install_cli.select_valid_file"
 msgstr ""
 
-#: hexagon/actions/internal/install_cli.py:79
+#: hexagon/actions/internal/install_cli.py:78
 msgid "msg.actions.internal.install_cli.success"
 msgstr ""
 
-#: hexagon/actions/internal/install_cli.py:89
+#: hexagon/actions/internal/install_cli.py:88
 msgid "msg.actions.internal.install_cli.not_in_path"
 msgstr ""
 
@@ -129,31 +129,31 @@ msgstr ""
 msgid "error.domain.configuration.group_tool_file_not_found"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:118
+#: hexagon/runtime/configuration.py:120
 msgid "msg.domain.configuration.install_cli_long_name"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:119
+#: hexagon/runtime/configuration.py:121
 msgid "msg.domain.configuration.install_cli_description"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:125
+#: hexagon/runtime/configuration.py:127
 msgid "msg.domain.configuration.get_json_schema_long_name"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:127
+#: hexagon/runtime/configuration.py:129
 msgid "msg.domain.configuration.get_json_schema_description"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:145
+#: hexagon/runtime/configuration.py:147
 msgid "msg.domain.configuration.save_alias_long_name"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:152
+#: hexagon/runtime/configuration.py:154
 msgid "msg.domain.configuration.replay_long_name"
 msgstr ""
 
-#: hexagon/runtime/configuration.py:158
+#: hexagon/runtime/configuration.py:160
 msgid "msg.domain.configuration.create_tool_long_name"
 msgstr ""
 
@@ -162,7 +162,7 @@ msgstr ""
 msgid "msg.support.dependencies.installing_dependencies"
 msgstr ""
 
-#: hexagon/runtime/execute/action.py:143
+#: hexagon/runtime/execute/action.py:144
 msgid "msg.support.execute.action.tool_help_epilog"
 msgstr ""
 
@@ -310,7 +310,7 @@ msgstr ""
 msgid "error.support.yaml.invalid_yaml"
 msgstr ""
 
-#: hexagon/runtime/yaml.py:49
+#: hexagon/runtime/yaml.py:50
 msgid "error.support.yaml.error_at"
 msgstr ""
 
@@ -406,11 +406,11 @@ msgstr ""
 msgid "msg.support.prompt.hints.help"
 msgstr ""
 
-#: hexagon/support/input/prompt/prompt.py:30
+#: hexagon/support/input/prompt/prompt.py:34
 msgid "error.support.input.prompt.prompt.invalid_argument_setup"
 msgstr ""
 
-#: hexagon/support/input/prompt/prompt.py:294
+#: hexagon/support/input/prompt/prompt.py:310
 msgid "msg.support.prompt.prompt.directory_created"
 msgstr ""
 


Test patch:
diff --git a/tests/runtime/test_parse_cli_args.py b/tests/runtime/test_parse_cli_args.py
index 09aced55..949e88f7 100644
--- a/tests/runtime/test_parse_cli_args.py
+++ b/tests/runtime/test_parse_cli_args.py
@@ -1,10 +1,7 @@
-from typing import List, Set, Optional
-
 import pytest
-from pydantic import BaseModel
 
-from hexagon.runtime.parse_args import parse_cli_args, should_support_multiple_args
-from hexagon.support.input.args import PositionalArg, OptionalArg, ToolArgs
+from hexagon.runtime.parse_args import parse_cli_args
+from hexagon.support.input.args import OptionalArg, ToolArgs
 
 
 def test_no_cli_args_passed():
@@ -98,6 +95,16 @@ def test_cli_args_env_is_second_positional_argument():
                 "foo": True,
             },
         ),
+        (
+            ["--float", "1.23"],
+            {
+                "float": 1.23,
+            },
+        ),
+        (
+            ["--bool", "true", "--not-bool", "false"],
+            {"bool": True, "not-bool": False},
+        ),
     ],
 )
 def test_cli_args_all_extra_arguments_mapping(optional_args, expected):
@@ -158,29 +165,6 @@ def test_cli_args_should_show_help(args):
     assert actual.env is None
 
 
-def test_get_generic_type_hint():
-    class TestModel(BaseModel):
-        name: PositionalArg[str] = None
-        last_names: PositionalArg[List[str]] = None
-        age: OptionalArg[int] = None
-        friends: OptionalArg[List[int]] = None
-        enemies: OptionalArg[Set[int]] = None
-        relatives: OptionalArg[set] = None
-        parents: OptionalArg[tuple] = None
-        grand_parents: Optional[OptionalArg[tuple]] = None
-
-    model = TestModel()
-
-    assert should_support_multiple_args(model.__fields__["name"]) is False
-    assert should_support_multiple_args(model.__fields__["last_names"]) is True
-    assert should_support_multiple_args(model.__fields__["age"]) is False
-    assert should_support_multiple_args(model.__fields__["friends"]) is True
-    assert should_support_multiple_args(model.__fields__["enemies"]) is True
-    assert should_support_multiple_args(model.__fields__["relatives"]) is True
-    assert should_support_multiple_args(model.__fields__["parents"]) is True
-    assert should_support_multiple_args(model.__fields__["grand_parents"]) is True
-
-
 bool_input_keys = [
     "--proceed={val}",
     "--proceed {val}",
diff --git a/tests/runtime/test_typing.py b/tests/runtime/test_typing.py
new file mode 100644
index 00000000..15fcc816
--- /dev/null
+++ b/tests/runtime/test_typing.py
@@ -0,0 +1,119 @@
+from enum import Enum
+from pathlib import Path
+from typing import List, Set, Optional, Dict, Union, Any
+
+import pytest
+from pydantic import BaseModel, DirectoryPath as PydanticDirectoryPath
+
+from hexagon.domain.hexagon_error import HexagonError
+from hexagon.support.input.args import PositionalArg, OptionalArg
+from hexagon.support.input.types import DirectoryPath
+from hexagon.typing import should_support_multiple_args, field_type_information
+
+
+@pytest.mark.parametrize(
+    "field,expected",
+    [
+        ("name", False),
+        ("last_names", True),
+        ("age", False),
+        ("friends", True),
+        ("enemies", True),
+        ("relatives", True),
+        ("parents", True),
+        ("grand_parents", True),
+    ],
+)
+def test_get_generic_type_hint(field, expected):
+    class TestModel(BaseModel):
+        name: PositionalArg[str] = None
+        last_names: PositionalArg[List[str]] = None
+        age: OptionalArg[int] = None
+        friends: OptionalArg[List[int]] = None
+        enemies: OptionalArg[Set[int]] = None
+        relatives: OptionalArg[set] = None
+        parents: OptionalArg[tuple] = None
+        grand_parents: Optional[OptionalArg[tuple]] = None
+
+    model = TestModel()
+
+    assert should_support_multiple_args(model.model_fields[field]) is expected
+
+
+class Country(str, Enum):
+    BRAZIL = "Brazil"
+    USA = "USA"
+    CANADA = "Canada"
+
+
+class Pet(BaseModel):
+    name: str
+
+
+@pytest.mark.parametrize(
+    "field,expected_type,expected_iterable,expected_of_enum",
+    [
+        ("name", str, False, False),
+        ("last_names", List[str], True, False),
+        ("age", int, False, False),
+        ("friends", List[int], True, False),
+        ("enemies", Set[int], True, False),
+        ("relatives", set, True, False),
+        ("parents", tuple, True, False),
+        ("grand_parents", tuple, True, False),
+        ("great_grand_parents", tuple, True, False),
+        ("country", Country, False, True),
+        ("countries", List[Country], True, True),
+        ("pets", Dict[str, Pet], False, False),
+        ("random", int, False, False),
+        ("random_list", List[int], True, False),
+        ("nested", Dict[str, int], False, False),
+        ("file", Path, False, False),
+        ("file_hexagon", Path, False, False),
+    ],
+)
+def test_field_info(field, expected_type, expected_iterable, expected_of_enum):
+    class TestModel(BaseModel):
+        model_config = {"arbitrary_types_allowed": True}
+        name: PositionalArg[str] = None
+        last_names: PositionalArg[List[str]] = None
+        age: OptionalArg[int] = None
+        friends: OptionalArg[List[int]] = None
+        enemies: OptionalArg[Set[int]] = None
+        relatives: OptionalArg[set] = None
+        parents: OptionalArg[tuple] = None
+        grand_parents: Optional[OptionalArg[tuple]] = None
+        great_grand_parents: OptionalArg[Optional[tuple]] = None
+        country: OptionalArg[Country] = None
+        countries: OptionalArg[List[Country]] = None
+        pets: OptionalArg[Dict[str, Pet]] = None
+        random: int = None
+        random_list: List[int] = None
+        nested: Optional[Dict[str, int]] = None
+        file: OptionalArg[PydanticDirectoryPath] = None
+        file_hexagon: OptionalArg[DirectoryPath] = None
+
+    model = TestModel()
+    type_, iterable, of_enum = field_type_information(model.model_fields[field])
+
+    assert type_.base_type == expected_type
+    assert iterable == expected_iterable
+    assert of_enum == expected_of_enum
+
+
+@pytest.mark.parametrize(
+    "field",
+    [
+        "optional_arg_union",
+        "arg_basic",
+    ],
+)
+def test_field_with_multiple_types_should_raise_error(field):
+    class TestModel(BaseModel):
+        optional_arg_union: OptionalArg[Union[str, int]] = None
+        arg_basic: Union[List[str], Dict[str, int], Any] = None
+
+    model = TestModel()
+
+    with pytest.raises(HexagonError):
+        field_type_information(model.model_fields[field])
diff --git a/tests/runtime/update/test_update_hexagon.py b/tests/runtime/update/test_update_hexagon.py
index b7641ad4..032cb6ea 100644
--- a/tests/runtime/update/test_update_hexagon.py
+++ b/tests/runtime/update/test_update_hexagon.py
@@ -27,12 +27,12 @@ def _local_version_mock(override=None):
     return Version("0.1.0")
 
 
-def _latest_version_mock():
+def _latest_version_mock(override=None):
     return Version("0.2.0")
 
 
 def test_hexagon_updates_itself(monkeypatch):
-    monkeypatch.setenv("HEXAGON_TEST_VERSION_OVERRIDE", "0.1.0")
+    monkeypatch.setenv("HEXAGON_TEST_LOCAL_VERSION_OVERRIDE", "0.1.0")
     monkeypatch.setattr(prompt, "confirm", _confirm_mock)
     monkeypatch.setattr(version, "local", _local_version_mock)
     monkeypatch.setattr(version, "latest", _latest_version_mock)
diff --git a/tests_e2e/__specs/execute_tool.py b/tests_e2e/__specs/execute_tool.py
index 6c8aad74..ed0594c6 100644
--- a/tests_e2e/__specs/execute_tool.py
+++ b/tests_e2e/__specs/execute_tool.py
@@ -229,8 +229,8 @@ def test_execute_node_script_module_with_env_and_arguments():
                 "arg1",
                 "arg2",
                 "envvars:",
-                'HEXAGON_EXECUTION_TOOL = {"name": "node-module-env", "type": "shell", "icon": null, "alias": "nme", "long_name": "Node Module Env Test", "description": null, "envs": {"dev": [789, "ghi"], "qa": {"foo": "foo", "--bar": "bar"}}, "traced": true, "action": "node-module.js"}',  # noqa: E501
-                'HEXAGON_EXECUTION_ENV = {"name": "dev", "alias": "d", "long_name": "dev", "description": null}',
+                'HEXAGON_EXECUTION_TOOL = {"name":"node-module-env","type":"shell","icon":null,"alias":"nme","long_name":"Node Module Env Test","description":null,"envs":{"dev":[789,"ghi"],"qa":{"foo":"foo","--bar":"bar"}},"traced":true,"action":"node-module.js"}',  # noqa: E501
+                'HEXAGON_EXECUTION_ENV = {"name":"dev","alias":"d","long_name":"dev","description":null}',
             ]
         )
         .exit()
@@ -251,8 +251,8 @@ def test_execute_node_script_module_with_other_env():
                 "foo=foo",
                 "--bar=bar",
                 "envvars:",
-                'HEXAGON_EXECUTION_TOOL = {"name": "node-module-env", "type": "shell", "icon": null, "alias": "nme", "long_name": "Node Module Env Test", "description": null, "envs": {"dev": [789, "ghi"], "qa": {"foo": "foo", "--bar": "bar"}}, "traced": true, "action": "node-module.js"}',  # noqa: E501
-                'HEXAGON_EXECUTION_ENV = {"name": "qa", "alias": "q", "long_name": "qa", "description": null}',
+                'HEXAGON_EXECUTION_TOOL = {"name":"node-module-env","type":"shell","icon":null,"alias":"nme","long_name":"Node Module Env Test","description":null,"envs":{"dev":[789,"ghi"],"qa":{"foo":"foo","--bar":"bar"}},"traced":true,"action":"node-module.js"}',  # noqa: E501
+                'HEXAGON_EXECUTION_ENV = {"name":"qa","alias":"q","long_name":"qa","description":null}',
             ]
         )
         .exit()
diff --git a/tests_e2e/__specs/execute_tool_with_args.py b/tests_e2e/__specs/execute_tool_with_args.py
index aec6ef0d..68da7b2e 100644
--- a/tests_e2e/__specs/execute_tool_with_args.py
+++ b/tests_e2e/__specs/execute_tool_with_args.py
@@ -138,11 +138,11 @@ def test_arguments_type_matters():
             [
                 "There where 3 error(s) in your input",
                 "",
-                "✗ age -> value is not a valid integer",
+                "✗ age -> Input should be a valid integer, unable to parse string as an integer",
                 "",
-                "✗ nationality -> USA is not a valid nationality",
+                "✗ nationality -> Value error, USA is not a valid nationality",
                 "",
-                "✗ car_brand -> we don't accept Chevrolet cars",
+                "✗ car_brand -> Value error, we don't accept Chevrolet cars",
             ],
             ignore_blank_lines=False,
         )
@@ -594,63 +594,6 @@ def test_should_prompt_fuzzy_file_search():
     )
 
 
-def test_should_prompt_fuzzy_file_search_with_generic_choice_at_arg_level():
-    (
-        as_a_user(__file__)
-        .run_hexagon(
-            ["prompt", "prompt_fuzzy_file_generic"],
-            os_env_vars={"HEXAGON_THEME": "no_border"},
-        )
-        .then_output_should_be(
-            [
-                "",
-                "",
-                "Enter fuzzy_file_input",
-                "5/5",
-                "Any",
-                "01_file.txt",
-                "02_file.txt",
-                "03_file.txt",
-            ],
-        )
-        .carriage_return()
-        .then_output_should_be(
-            ["fuzzy_file_input_generic: Any"], discard_until_first_match=True
-        )
-        .exit()
-    )
-
-
-def test_should_prompt_fuzzy_file_search_with_multiple_generic_choice_at_arg_level():
-    (
-        as_a_user(__file__)
-        .run_hexagon(
-            ["prompt", "prompt_fuzzy_file_generic_multiple"],
-            os_env_vars={"HEXAGON_THEME": "no_border"},
-        )
-        .then_output_should_be(
-            [
-                "",
-                "",
-                "Enter fuzzy_file_input",
-                "7/7",
-                "Any",
-                "All",
-                "None",
-                "01_file.txt",
-                "02_file.txt",
-                "03_file.txt",
-            ],
-        )
-        .arrow_down()
-        .carriage_return()
-        .then_output_should_be(
-            ["fuzzy_file_input_generic: *"], discard_until_first_match=True
-        )
-        .exit()
-    )
-
-
 def test_should_prompt_fuzzy_file_search_with_generic_choice_at_prompt_level():
     (
         as_a_user(__file__)
diff --git a/tests_e2e/__specs/options.py b/tests_e2e/__specs/options.py
index 0472d041..bc8f8a9b 100644
--- a/tests_e2e/__specs/options.py
+++ b/tests_e2e/__specs/options.py
@@ -11,7 +11,7 @@
 
 storage_path = os.path.join(e2e_test_folder_path(__file__), "storage")
 base_env_vars = {"HEXAGON_THEME": "result_only", "HEXAGON_STORAGE_PATH": storage_path}
-local_options = {"update_time_between_checks": "2 0:00:00"}
+local_options = {"update_time_between_checks": "P2D"}
 cli_config = {
     "cli": {
         "name": "Test",
@@ -94,7 +94,7 @@ def test_env_variable():
         .given_a_cli_yaml(cli_config)
         .run_hexagon(
             ["print-options"],
-            {**base_env_vars, "HEXAGON_UPDATE_TIME_BETWEEN_CHECKS": "3 0:00:00"},
+            {**base_env_vars, "HEXAGON_UPDATE_TIME_BETWEEN_CHECKS": "P3D"},
         )
         .then_output_should_be(["update_time_between_checks: 3 days, 0:00:00"], True)
         .exit()
@@ -105,14 +105,14 @@ def test_options_in_configuration_file():
     _write_local_options(local_options)
 
     app = copy.deepcopy(cli_config)
-    app["cli"]["options"] = {"update_time_between_checks": "23 0:00:00"}
+    app["cli"]["options"] = {"update_time_between_checks": "P23D"}
 
     (
         as_a_user(__file__)
         .given_a_cli_yaml(app)
         .run_hexagon(
             ["print-options"],
-            {**base_env_vars, "HEXAGON_UPDATE_TIME_BETWEEN_CHECKS": "3 0:00:00"},
+            {**base_env_vars, "HEXAGON_UPDATE_TIME_BETWEEN_CHECKS": "P3D"},
         )
         .then_output_should_be(["update_time_between_checks: 23 days, 0:00:00"], True)
         .exit()
@@ -130,7 +130,7 @@ def test_invalid_local_options():
             [
                 "There were 1 error(s) in your YAML",
                 "",
-                "✗ update_time_between_checks -> invalid duration format",
+                "✗ update_time_between_checks -> Input should be a valid timedelta, invalid character in hour",
             ],
             ignore_blank_lines=False,
         )
diff --git a/tests_e2e/__specs/update_hexagon.py b/tests_e2e/__specs/update_hexagon.py
index d924535b..3e23184e 100644
--- a/tests_e2e/__specs/update_hexagon.py
+++ b/tests_e2e/__specs/update_hexagon.py
@@ -31,9 +31,12 @@ def _clear_last_check():
         os.remove(last_checked_storage_path)
 
 
+temp_file_path = os.path.join(test_folder_path, "github_version_mock.json")
+
 base_os_env_vars = {
     "HEXAGON_UPDATE_DISABLED": "false",
-    "HEXAGON_TEST_VERSION_OVERRIDE": "0.1.0",
+    "HEXAGON_TEST_LOCAL_VERSION_OVERRIDE": "0.1.0",
+    "HEXAGON_TEST_LATEST_VERSION_OVERRIDE": f"file://{temp_file_path}",
     "HEXAGON_STORAGE_PATH": storage_path,
 }
 
diff --git a/tests_e2e/__specs/validate_yaml.py b/tests_e2e/__specs/validate_yaml.py
index 49c73bc4..e9c4c32d 100644
--- a/tests_e2e/__specs/validate_yaml.py
+++ b/tests_e2e/__specs/validate_yaml.py
@@ -7,33 +7,33 @@ def test_show_errors_when_invalid_yaml():
         .run_hexagon()
         .then_output_should_be(
             [
-                "There were 6 error(s) in your YAML file:",
+                "There were 7 error(s) in your YAML file:",
                 "",
-                "✗ cli.command -> str type expected",
+                "✗ cli.command -> Input should be a valid string",
                 "2   name: Test",
                 "3   custom_tools_dir: .",
                 "4   command: [ 'test' ]",
                 "5",
                 "6 tools",
                 "",
-                "✗ envs -> field required",
+                "✗ envs -> Field required",
                 "1",
                 "",
-                "✗ tools.1.action -> field required",
+                "✗ tools.1.action -> Field required",
                 "13       '*': https://www.google.com/",
                 "14",
                 "15   - name: google-invalid",
                 "16     long_name: Google",
                 "17     type: web",
                 "",
-                "✗ tools.1.tools -> field required",
+                "✗ tools.1.tools -> Field required",
                 "13       '*': https://www.google.com/",
                 "14",
                 "15   - name: google-invalid",
                 "16     long_name: Google",
                 "17     type: web",
                 "",
-                "✗ tools.2.action -> none is not an allowed value",
+                "✗ tools.2.action.str -> Input should be a valid string",
             ],
             ignore_blank_lines=False,
         )
diff --git a/tests_e2e/execute_tool_with_args/python_module_only_optionals.py b/tests_e2e/execute_tool_with_args/python_module_only_optionals.py
index 43b73630..fe393479 100644
--- a/tests_e2e/execute_tool_with_args/python_module_only_optionals.py
+++ b/tests_e2e/execute_tool_with_args/python_module_only_optionals.py
@@ -1,4 +1,4 @@
-from typing import Any, Optional, List, Union
+from typing import Any, Optional, List
 
 from hexagon.domain.env import Env
 from hexagon.domain.tool import ActionTool
@@ -14,7 +14,7 @@ class Args(ToolArgs):
 
     name: OptionalArg[str]
     age: OptionalArg[str]
-    country: OptionalArg[Union[str, int]]
+    country: OptionalArg[str]
     likes: OptionalArg[List[str]]
 
 
diff --git a/tests_e2e/execute_tool_with_args/python_module_prompt.py b/tests_e2e/execute_tool_with_args/python_module_prompt.py
index 8640269f..a4246a06 100644
--- a/tests_e2e/execute_tool_with_args/python_module_prompt.py
+++ b/tests_e2e/execute_tool_with_args/python_module_prompt.py
@@ -1,7 +1,7 @@
 from enum import Enum
 from typing import Any, Optional, List
 
-from pydantic import validator
+from pydantic import field_validator
 
 from hexagon.domain.env import Env
 from hexagon.domain.tool import ActionTool
@@ -39,39 +39,25 @@ class Args(ToolArgs):
         choices=["a sentence to match", "a sentence not to match", "something else"],
     )
     fuzzy_file_input: OptionalArg[FilePath] = Arg(None, searchable=True, glob="*.txt")
-    fuzzy_file_input_extra_choices: OptionalArg[FilePath] = Arg(
-        None,
-        searchable=True,
-        glob="*.txt",
-        glob_extra_choices=["Any"],
-    )
-    fuzzy_file_input_extra_dict_choices: OptionalArg[FilePath] = Arg(
-        None,
-        searchable=True,
-        glob="*.txt",
-        glob_extra_choices=[
-            {"name": "Any", "value": "any"},
-            {"name": "All", "value": "*"},
-            {"name": "None", "value": "none"},
-        ],
-    )
     proceed: OptionalArg[bool] = Arg(
         None, prompt_default=True, prompt_message="Do you want to continue?"
     )
 
-    @validator("age")
-    def validate_age(cls, arg):
+    @field_validator("age")
+    @classmethod
+    def validate_age(cls, arg: OptionalArg[int]):
         if arg:
-            v = int(arg) if isinstance(arg, str) else arg.value
-            if v < 18:
+            v = arg.value
+            if v and v < 18:
                 raise ValueError("age must be greater than 18")
         return arg
 
-    @validator("age")
-    def validate_age_max(cls, arg):
+    @field_validator("age")
+    @classmethod
+    def validate_age_max(cls, arg: OptionalArg[int]):
         if arg:
-            v = int(arg) if isinstance(arg, str) else arg.value
-            if v > 48:
+            v = arg.value
+            if v and v > 48:
                 raise ValueError("age must be less than 48")
         return arg
 
@@ -92,8 +78,6 @@ def main(
         "prompt_validate_type": prompt_validate_type,
         "prompt_fuzzy_search": prompt_fuzzy_search,
         "prompt_fuzzy_file": prompt_fuzzy_file,
-        "prompt_fuzzy_file_generic": prompt_fuzzy_file_generic,
-        "prompt_fuzzy_file_generic_multiple": prompt_fuzzy_file_generic_multiple,
         "prompt_fuzzy_file_invalid_extra": prompt_fuzzy_file_invalid_extra,
         "prompt_multiple_times": prompt_multiple_times,
         "prompt_boolean": prompt_boolean,
@@ -145,18 +129,6 @@ def prompt_fuzzy_file(cli_args):
     log.result(f"fuzzy_file_input: {cli_args.fuzzy_file_input.prompt()}")
 
 
-def prompt_fuzzy_file_generic(cli_args):
-    log.result(
-        f"fuzzy_file_input_generic: {cli_args.fuzzy_file_input_extra_choices.prompt()}"
-    )
-
-
-def prompt_fuzzy_file_generic_multiple(cli_args):
-    log.result(
-        f"fuzzy_file_input_generic: {cli_args.fuzzy_file_input_extra_dict_choices.prompt()}"
-    )
-
-
 def prompt_fuzzy_file_invalid_extra(cli_args):
     log.result(
         f"fuzzy_file_input_generic: {cli_args.fuzzy_file_input.prompt(glob_extra_choices=['Any'])}"
diff --git a/tests_e2e/update_hexagon/github_version_mock.json b/tests_e2e/update_hexagon/github_version_mock.json
new file mode 100644
index 00000000..fb6ca544
--- /dev/null
+++ b/tests_e2e/update_hexagon/github_version_mock.json
@@ -0,0 +1,3 @@
+{
+  "name": "v1.0.0"
+}
\ No newline at end of file