diff --git a/Cargo.lock b/Cargo.lock
index 3e069de..3ffd2a4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -31,24 +31,6 @@ dependencies = [
"memchr",
]
-[[package]]
-name = "aligned"
-version = "0.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
-dependencies = [
- "as-slice",
-]
-
-[[package]]
-name = "aligned-vec"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
-dependencies = [
- "equator",
-]
-
[[package]]
name = "alloca"
version = "0.4.0"
@@ -135,38 +117,6 @@ version = "1.0.101"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
-[[package]]
-name = "arbitrary"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
-
-[[package]]
-name = "arg_enum_proc_macro"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "arrayvec"
-version = "0.7.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
-
-[[package]]
-name = "as-slice"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
-dependencies = [
- "stable_deref_trait",
-]
-
[[package]]
name = "async-trait"
version = "0.1.89"
@@ -190,49 +140,6 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
-[[package]]
-name = "av-scenechange"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
-dependencies = [
- "aligned",
- "anyhow",
- "arg_enum_proc_macro",
- "arrayvec",
- "log",
- "num-rational",
- "num-traits",
- "pastey 0.1.1",
- "rayon",
- "thiserror",
- "v_frame",
- "y4m",
-]
-
-[[package]]
-name = "av1-grain"
-version = "0.2.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
-dependencies = [
- "anyhow",
- "arrayvec",
- "log",
- "nom 8.0.0",
- "num-rational",
- "v_frame",
-]
-
-[[package]]
-name = "avif-serialize"
-version = "0.8.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
-dependencies = [
- "arrayvec",
-]
-
[[package]]
name = "base64"
version = "0.13.1"
@@ -251,27 +158,12 @@ version = "1.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
-[[package]]
-name = "bit_field"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e4b40c7323adcfc0a41c4b88143ed58346ff65a288fc144329c5c45e05d70c6"
-
[[package]]
name = "bitflags"
version = "2.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
-[[package]]
-name = "bitstream-io"
-version = "4.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
-dependencies = [
- "core2",
-]
-
[[package]]
name = "block-buffer"
version = "0.10.4"
@@ -291,36 +183,18 @@ dependencies = [
"serde",
]
-[[package]]
-name = "built"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
-
[[package]]
name = "bumpalo"
version = "3.19.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510"
-[[package]]
-name = "bytemuck"
-version = "1.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
-
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
-[[package]]
-name = "byteorder-lite"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
-
[[package]]
name = "bytes"
version = "1.11.1"
@@ -441,12 +315,6 @@ version = "0.7.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
-[[package]]
-name = "color_quant"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
-
[[package]]
name = "colorchoice"
version = "1.0.4"
@@ -468,19 +336,6 @@ dependencies = [
"static_assertions",
]
-[[package]]
-name = "console"
-version = "0.15.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "054ccb5b10f9f2cbf51eb355ca1d05c2d279ce1804688d0db74b4733a5aeafd8"
-dependencies = [
- "encode_unicode",
- "libc",
- "once_cell",
- "unicode-width",
- "windows-sys 0.59.0",
-]
-
[[package]]
name = "console"
version = "0.16.2"
@@ -539,15 +394,6 @@ version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
-[[package]]
-name = "core2"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
-dependencies = [
- "memchr",
-]
-
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -848,26 +694,6 @@ dependencies = [
"cfg-if",
]
-[[package]]
-name = "equator"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
-dependencies = [
- "equator-macro",
-]
-
-[[package]]
-name = "equator-macro"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -890,30 +716,14 @@ version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d817e038c30374a4bcb22f94d0a8a0e216958d4c3dcde369b1439fec4bdda6e6"
-[[package]]
-name = "exr"
-version = "1.74.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be"
-dependencies = [
- "bit_field",
- "half",
- "lebe",
- "miniz_oxide",
- "rayon-core",
- "smallvec",
- "zune-inflate",
-]
-
[[package]]
name = "fastembed"
-version = "5.8.1"
+version = "5.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59a3f841f27a44bcc32214f8df75cc9b6cea55dbbebbfe546735690eab5bb2d2"
+checksum = "58d74247f8cb93f94459e6f3599391f30c3f434f167f7109bd01a288db1bbe67"
dependencies = [
"anyhow",
"hf-hub",
- "image",
"ndarray",
"ort",
"safetensors",
@@ -928,35 +738,6 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
-[[package]]
-name = "fax"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab"
-dependencies = [
- "fax_derive",
-]
-
-[[package]]
-name = "fax_derive"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "fdeflate"
-version = "0.3.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
-dependencies = [
- "simd-adler32",
-]
-
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -1131,16 +912,6 @@ dependencies = [
"wasip2",
]
-[[package]]
-name = "gif"
-version = "0.14.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e"
-dependencies = [
- "color_quant",
- "weezl",
-]
-
[[package]]
name = "git2"
version = "0.20.4"
@@ -1220,13 +991,13 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]]
name = "hf-hub"
-version = "0.4.3"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "629d8f3bbeda9d148036d6b0de0a3ab947abd08ce90626327fc3547a49d59d97"
+checksum = "aef3982638978efa195ff11b305f51f1f22f4f0a6cabee7af79b383ebee6a213"
dependencies = [
"dirs",
"http",
- "indicatif 0.17.11",
+ "indicatif",
"libc",
"log",
"native-tls",
@@ -1235,16 +1006,10 @@ dependencies = [
"serde",
"serde_json",
"thiserror",
- "ureq 2.12.1",
- "windows-sys 0.60.2",
+ "ureq",
+ "windows-sys 0.61.2",
]
-[[package]]
-name = "hmac-sha256"
-version = "1.1.13"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d0f0ae375a85536cac3a243e3a9cda80a47910348abdea7e2c22f8ec556d586d"
-
[[package]]
name = "http"
version = "1.4.0"
@@ -1511,46 +1276,6 @@ dependencies = [
"winapi-util",
]
-[[package]]
-name = "image"
-version = "0.25.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
-dependencies = [
- "bytemuck",
- "byteorder-lite",
- "color_quant",
- "exr",
- "gif",
- "image-webp",
- "moxcms",
- "num-traits",
- "png",
- "qoi",
- "ravif",
- "rayon",
- "rgb",
- "tiff",
- "zune-core 0.5.1",
- "zune-jpeg 0.5.12",
-]
-
-[[package]]
-name = "image-webp"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
-dependencies = [
- "byteorder-lite",
- "quick-error",
-]
-
-[[package]]
-name = "imgref"
-version = "1.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
-
[[package]]
name = "indexmap"
version = "2.13.0"
@@ -1561,43 +1286,19 @@ dependencies = [
"hashbrown",
]
-[[package]]
-name = "indicatif"
-version = "0.17.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
-dependencies = [
- "console 0.15.11",
- "number_prefix",
- "portable-atomic",
- "unicode-width",
- "web-time",
-]
-
[[package]]
name = "indicatif"
version = "0.18.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88"
dependencies = [
- "console 0.16.2",
+ "console",
"portable-atomic",
"unicode-width",
"unit-prefix",
"web-time",
]
-[[package]]
-name = "interpolate_name"
-version = "0.2.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -1670,28 +1371,12 @@ version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
-[[package]]
-name = "lebe"
-version = "0.5.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8"
-
[[package]]
name = "libc"
version = "0.2.180"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
-[[package]]
-name = "libfuzzer-sys"
-version = "0.4.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
-dependencies = [
- "arbitrary",
- "cc",
-]
-
[[package]]
name = "libgit2-sys"
version = "0.18.3+1.9.2"
@@ -1706,6 +1391,16 @@ dependencies = [
"pkg-config",
]
+[[package]]
+name = "libloading"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60"
+dependencies = [
+ "cfg-if",
+ "windows-link",
+]
+
[[package]]
name = "libredox"
version = "0.1.12"
@@ -1775,21 +1470,6 @@ version = "0.4.29"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
-[[package]]
-name = "loop9"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
-dependencies = [
- "imgref",
-]
-
-[[package]]
-name = "lzma-rust2"
-version = "0.15.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1670343e58806300d87950e3401e820b519b9384281bbabfb15e3636689ffd69"
-
[[package]]
name = "macro_rules_attribute"
version = "0.2.2"
@@ -1825,16 +1505,6 @@ dependencies = [
"rawpointer",
]
-[[package]]
-name = "maybe-rayon"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
-dependencies = [
- "cfg-if",
- "rayon",
-]
-
[[package]]
name = "memchr"
version = "2.7.6"
@@ -1896,16 +1566,6 @@ dependencies = [
"syn",
]
-[[package]]
-name = "moxcms"
-version = "0.7.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
-dependencies = [
- "num-traits",
- "pxfm",
-]
-
[[package]]
name = "native-tls"
version = "0.2.14"
@@ -1938,12 +1598,6 @@ dependencies = [
"rawpointer",
]
-[[package]]
-name = "new_debug_unreachable"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
-
[[package]]
name = "nom"
version = "7.1.3"
@@ -1954,21 +1608,6 @@ dependencies = [
"minimal-lexical",
]
-[[package]]
-name = "nom"
-version = "8.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
-dependencies = [
- "memchr",
-]
-
-[[package]]
-name = "noop_proc_macro"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
-
[[package]]
name = "nu-ansi-term"
version = "0.50.3"
@@ -1978,16 +1617,6 @@ dependencies = [
"windows-sys 0.61.2",
]
-[[package]]
-name = "num-bigint"
-version = "0.4.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
-dependencies = [
- "num-integer",
- "num-traits",
-]
-
[[package]]
name = "num-complex"
version = "0.4.6"
@@ -2003,17 +1632,6 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050"
-[[package]]
-name = "num-derive"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
[[package]]
name = "num-integer"
version = "0.1.46"
@@ -2023,17 +1641,6 @@ dependencies = [
"num-traits",
]
-[[package]]
-name = "num-rational"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
-dependencies = [
- "num-bigint",
- "num-integer",
- "num-traits",
-]
-
[[package]]
name = "num-traits"
version = "0.2.19"
@@ -2043,12 +1650,6 @@ dependencies = [
"autocfg",
]
-[[package]]
-name = "number_prefix"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3"
-
[[package]]
name = "once_cell"
version = "1.21.3"
@@ -2141,27 +1742,22 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ort"
-version = "2.0.0-rc.11"
+version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a5df903c0d2c07b56950f1058104ab0c8557159f2741782223704de9be73c3c"
+checksum = "d7de3af33d24a745ffb8fab904b13478438d1cd52868e6f17735ef6e1f8bf133"
dependencies = [
+ "libloading",
"ndarray",
"ort-sys",
"smallvec",
"tracing",
- "ureq 3.2.0",
]
[[package]]
name = "ort-sys"
-version = "2.0.0-rc.11"
+version = "2.0.0-rc.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06503bb33f294c5f1ba484011e053bfa6ae227074bdb841e9863492dc5960d4b"
-dependencies = [
- "hmac-sha256",
- "lzma-rust2",
- "ureq 3.2.0",
-]
+checksum = "d7b497d21a8b6fbb4b5a544f8fadb77e801a09ae0add9e411d31c6f89e3c1e90"
[[package]]
name = "page_size"
@@ -2202,12 +1798,6 @@ version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
-[[package]]
-name = "pastey"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
-
[[package]]
name = "pastey"
version = "0.2.1"
@@ -2275,19 +1865,6 @@ dependencies = [
"plotters-backend",
]
-[[package]]
-name = "png"
-version = "0.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
-dependencies = [
- "bitflags",
- "crc32fast",
- "fdeflate",
- "flate2",
- "miniz_oxide",
-]
-
[[package]]
name = "portable-atomic"
version = "1.13.1"
@@ -2336,49 +1913,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "profiling"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
-dependencies = [
- "profiling-procmacros",
-]
-
-[[package]]
-name = "profiling-procmacros"
-version = "1.0.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
-dependencies = [
- "quote",
- "syn",
-]
-
-[[package]]
-name = "pxfm"
-version = "0.1.27"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
-dependencies = [
- "num-traits",
-]
-
-[[package]]
-name = "qoi"
-version = "0.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
-dependencies = [
- "bytemuck",
-]
-
-[[package]]
-name = "quick-error"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
-
[[package]]
name = "quote"
version = "1.0.44"
@@ -2423,56 +1957,6 @@ dependencies = [
"getrandom 0.3.4",
]
-[[package]]
-name = "rav1e"
-version = "0.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
-dependencies = [
- "aligned-vec",
- "arbitrary",
- "arg_enum_proc_macro",
- "arrayvec",
- "av-scenechange",
- "av1-grain",
- "bitstream-io",
- "built",
- "cfg-if",
- "interpolate_name",
- "itertools 0.14.0",
- "libc",
- "libfuzzer-sys",
- "log",
- "maybe-rayon",
- "new_debug_unreachable",
- "noop_proc_macro",
- "num-derive",
- "num-traits",
- "paste",
- "profiling",
- "rand",
- "rand_chacha",
- "simd_helpers",
- "thiserror",
- "v_frame",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "ravif"
-version = "0.12.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
-dependencies = [
- "avif-serialize",
- "imgref",
- "loop9",
- "quick-error",
- "rav1e",
- "rayon",
- "rgb",
-]
-
[[package]]
name = "rawpointer"
version = "0.2.1"
@@ -2622,12 +2106,6 @@ dependencies = [
"web-sys",
]
-[[package]]
-name = "rgb"
-version = "0.8.52"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
-
[[package]]
name = "ring"
version = "0.17.14"
@@ -2652,7 +2130,7 @@ dependencies = [
"base64 0.22.1",
"chrono",
"futures",
- "pastey 0.2.1",
+ "pastey",
"pin-project-lite",
"rmcp-macros",
"schemars",
@@ -2686,7 +2164,7 @@ dependencies = [
"clap",
"globset",
"ignore",
- "indicatif 0.18.3",
+ "indicatif",
"rpg-core",
"rpg-encoder",
"rpg-lift",
@@ -2739,7 +2217,7 @@ name = "rpg-lift"
version = "0.8.3"
dependencies = [
"globset",
- "indicatif 0.18.3",
+ "indicatif",
"rpg-core",
"rpg-encoder",
"rpg-parser",
@@ -2748,7 +2226,7 @@ dependencies = [
"tempfile",
"thiserror",
"tracing",
- "ureq 3.2.0",
+ "ureq",
]
[[package]]
@@ -2860,9 +2338,9 @@ dependencies = [
[[package]]
name = "rustls-webpki"
-version = "0.103.11"
+version = "0.103.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20a6af516fea4b20eccceaf166e8aa666ac996208e8a644ce3ef5aa783bc7cd4"
+checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e"
dependencies = [
"ring",
"rustls-pki-types",
@@ -3089,15 +2567,6 @@ version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
-[[package]]
-name = "simd_helpers"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
-dependencies = [
- "quote",
-]
-
[[package]]
name = "slab"
version = "0.4.12"
@@ -3138,7 +2607,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5851699c4033c63636f7ea4cf7b7c1f1bf06d0cc03cfb42e711de5a5c46cf326"
dependencies = [
"base64 0.13.1",
- "nom 7.1.3",
+ "nom",
"serde",
"unicode-segmentation",
]
@@ -3267,20 +2736,6 @@ dependencies = [
"cfg-if",
]
-[[package]]
-name = "tiff"
-version = "0.10.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f"
-dependencies = [
- "fax",
- "flate2",
- "half",
- "quick-error",
- "weezl",
- "zune-jpeg 0.4.21",
-]
-
[[package]]
name = "time"
version = "0.3.47"
@@ -3810,26 +3265,6 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
-[[package]]
-name = "ureq"
-version = "2.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
-dependencies = [
- "base64 0.22.1",
- "flate2",
- "log",
- "native-tls",
- "once_cell",
- "rustls",
- "rustls-pki-types",
- "serde",
- "serde_json",
- "socks",
- "url",
- "webpki-roots 0.26.11",
-]
-
[[package]]
name = "ureq"
version = "3.2.0"
@@ -3851,7 +3286,7 @@ dependencies = [
"ureq-proto",
"utf-8",
"webpki-root-certs",
- "webpki-roots 1.0.6",
+ "webpki-roots",
]
[[package]]
@@ -3896,17 +3331,6 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-[[package]]
-name = "v_frame"
-version = "0.3.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
-dependencies = [
- "aligned-vec",
- "num-traits",
- "wasm-bindgen",
-]
-
[[package]]
name = "valuable"
version = "0.1.1"
@@ -4060,15 +3484,6 @@ dependencies = [
"rustls-pki-types",
]
-[[package]]
-name = "webpki-roots"
-version = "0.26.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9"
-dependencies = [
- "webpki-roots 1.0.6",
-]
-
[[package]]
name = "webpki-roots"
version = "1.0.6"
@@ -4078,12 +3493,6 @@ dependencies = [
"rustls-pki-types",
]
-[[package]]
-name = "weezl"
-version = "0.1.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
-
[[package]]
name = "winapi"
version = "0.3.9"
@@ -4194,15 +3603,6 @@ dependencies = [
"windows-targets 0.52.6",
]
-[[package]]
-name = "windows-sys"
-version = "0.59.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
-dependencies = [
- "windows-targets 0.52.6",
-]
-
[[package]]
name = "windows-sys"
version = "0.60.2"
@@ -4368,12 +3768,6 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9"
-[[package]]
-name = "y4m"
-version = "0.8.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
-
[[package]]
name = "yoke"
version = "0.8.1"
@@ -4510,42 +3904,3 @@ dependencies = [
"cc",
"pkg-config",
]
-
-[[package]]
-name = "zune-core"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
-
-[[package]]
-name = "zune-core"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9"
-
-[[package]]
-name = "zune-inflate"
-version = "0.2.54"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
-dependencies = [
- "simd-adler32",
-]
-
-[[package]]
-name = "zune-jpeg"
-version = "0.4.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713"
-dependencies = [
- "zune-core 0.4.12",
-]
-
-[[package]]
-name = "zune-jpeg"
-version = "0.5.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "410e9ecef634c709e3831c2cfdb8d9c32164fae1c67496d5b68fff728eec37fe"
-dependencies = [
- "zune-core 0.5.1",
-]
diff --git a/Cargo.toml b/Cargo.toml
index 2e12004..0a1aef2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -143,7 +143,7 @@ zstd = "0.13.3"
ureq = "3"
# Embeddings
-fastembed = "5.8.1"
+fastembed = { version = "5.13.3", default-features = false, features = ["ort-load-dynamic", "hf-hub-native-tls"] }
# TOON format
toon-format = { version = "0.4.1", default-features = false }
diff --git a/README.md b/README.md
index 4823aa3..8d7ee34 100644
--- a/README.md
+++ b/README.md
@@ -132,21 +132,24 @@ Seven Rust crates, one MCP server binary, one CLI binary:
| `rpg-nav` | Search, fetch, explore, snapshot, TOON serialization |
| `rpg-lift` | Autonomous LLM lifting (Anthropic, OpenAI, OpenRouter, Gemini) |
| `rpg-cli` | CLI binary (`rpg-encoder`) |
-| `rpg-mcp` | MCP server binary (`rpg-mcp-server`) with 27 tools |
+| `rpg-mcp` | MCP server binary (`rpg-mcp-server`) with 28 tools |
---
-## MCP Tools (27)
+## MCP Tools (28)
+
+Per-call `project_root` overrides target another repository without changing the active root. Semantic or embedding-backed queries may refresh the target root's `.rpg/embeddings.*` cache files to keep results up to date.
-Build & Maintain (4 tools)
+Build & Maintain (5 tools)
| Tool | Description |
|------|-------------|
+| `set_project_root` | Switch the active repository root at runtime without restarting the MCP server |
| `build_rpg` | Index the codebase (run once, instant) |
| `update_rpg` | Incremental update from git changes |
| `reload_rpg` | Reload graph from disk after external changes |
-| `rpg_info` | Graph statistics, hierarchy overview, per-area lifting coverage |
+| `rpg_info` | Graph statistics, hierarchy overview, per-area lifting coverage. Supports optional per-call `project_root` override |
@@ -155,11 +158,11 @@ Seven Rust crates, one MCP server binary, one CLI binary:
| Tool | Description |
|------|-------------|
-| `semantic_snapshot` | Whole-repo semantic understanding in one call (~25K tokens for 1000 entities) |
-| `search_node` | Search entities by intent or keywords (hybrid embedding + lexical scoring) |
-| `fetch_node` | Get entity metadata, source code, dependencies, and hierarchy context |
-| `explore_rpg` | Traverse dependency graph (upstream, downstream, or both) |
-| `context_pack` | Single-call search + fetch + explore with token budget |
+| `semantic_snapshot` | Whole-repo semantic understanding in one call (~25K tokens for 1000 entities). Supports optional per-call `project_root` override |
+| `search_node` | Search entities by intent or keywords (hybrid embedding + lexical scoring). Supports optional per-call `project_root` override |
+| `fetch_node` | Get entity metadata, source code, dependencies, and hierarchy context. Supports optional per-call `project_root` override |
+| `explore_rpg` | Traverse dependency graph (upstream, downstream, or both). Supports optional per-call `project_root` override |
+| `context_pack` | Single-call search + fetch + explore with token budget. Supports optional per-call `project_root` override |
@@ -168,13 +171,13 @@ Seven Rust crates, one MCP server binary, one CLI binary:
| Tool | Description |
|------|-------------|
-| `impact_radius` | BFS reachability analysis — "what depends on X?" |
-| `plan_change` | Change planning — find relevant entities, modification order, blast radius |
-| `find_paths` | K-shortest dependency paths between two entities |
-| `slice_between` | Extract minimal connecting subgraph between entities |
-| `analyze_health` | Code health: coupling, instability, god objects, clone detection |
-| `detect_cycles` | Find circular dependencies and architectural cycles |
-| `reconstruct_plan` | Dependency-safe reconstruction execution plan |
+| `impact_radius` | BFS reachability analysis — "what depends on X?" Supports optional per-call `project_root` override |
+| `plan_change` | Change planning — find relevant entities, modification order, blast radius. Supports optional per-call `project_root` override |
+| `find_paths` | K-shortest dependency paths between two entities. Supports optional per-call `project_root` override |
+| `slice_between` | Extract minimal connecting subgraph between entities. Supports optional per-call `project_root` override |
+| `analyze_health` | Code health: coupling, instability, god objects, clone detection. Supports optional per-call `project_root` override |
+| `detect_cycles` | Find circular dependencies and architectural cycles. Supports optional per-call `project_root` override |
+| `reconstruct_plan` | Dependency-safe reconstruction execution plan. Supports optional per-call `project_root` override |
@@ -183,17 +186,17 @@ Seven Rust crates, one MCP server binary, one CLI binary:
| Tool | Description |
|------|-------------|
-| `auto_lift` | One-call autonomous lifting via cheap LLM API (Haiku, GPT-4o-mini, OpenRouter, Gemini) |
-| `lifting_status` | Dashboard — coverage, per-area progress, NEXT STEP |
-| `get_entities_for_lifting` | Get entity source code for your agent to analyze |
-| `submit_lift_results` | Submit the agent's semantic features back to the graph |
-| `finalize_lifting` | Aggregate file-level features, rebuild hierarchy metadata |
-| `get_files_for_synthesis` | Get file-level entity features for holistic synthesis |
-| `submit_file_syntheses` | Submit holistic file-level summaries |
-| `build_semantic_hierarchy` | Get domain discovery + hierarchy assignment prompts |
-| `submit_hierarchy` | Apply hierarchy assignments to the graph |
-| `get_routing_candidates` | Get entities needing semantic routing (drifted or newly lifted) |
-| `submit_routing_decisions` | Submit routing decisions (hierarchy path or "keep") |
+| `auto_lift` | One-call autonomous lifting via cheap LLM API (Haiku, GPT-4o-mini, OpenRouter, Gemini). Supports optional per-call `project_root` override |
+| `lifting_status` | Dashboard — coverage, per-area progress, NEXT STEP. Supports optional per-call `project_root` override |
+| `get_entities_for_lifting` | Get entity source code for your agent to analyze. Supports optional per-call `project_root` override |
+| `submit_lift_results` | Submit the agent's semantic features back to the graph. Supports optional per-call `project_root` override |
+| `finalize_lifting` | Aggregate file-level features, rebuild hierarchy metadata. Supports optional per-call `project_root` override |
+| `get_files_for_synthesis` | Get file-level entity features for holistic synthesis. Supports optional per-call `project_root` override |
+| `submit_file_syntheses` | Submit holistic file-level summaries. Supports optional per-call `project_root` override |
+| `build_semantic_hierarchy` | Get domain discovery + hierarchy assignment prompts. Supports optional per-call `project_root` override |
+| `submit_hierarchy` | Apply hierarchy assignments to the graph. Supports optional per-call `project_root` override |
+| `get_routing_candidates` | Get entities needing semantic routing (drifted or newly lifted). Supports optional per-call `project_root` override |
+| `submit_routing_decisions` | Submit routing decisions (hierarchy path or "keep"). Supports optional per-call `project_root` override |
@@ -241,7 +244,7 @@ claude mcp add rpg -- npx -y -p rpg-encoder rpg-mcp-server
}
```
-The server auto-detects the project root from the current working directory — no path argument needed.
+The server auto-detects the project root from the startup directory by default. When no positional project path is provided, the binary falls back to its current working directory.
CLI
diff --git a/crates/rpg-core/src/graph.rs b/crates/rpg-core/src/graph.rs
index 0d40758..1d35d14 100644
--- a/crates/rpg-core/src/graph.rs
+++ b/crates/rpg-core/src/graph.rs
@@ -526,7 +526,7 @@ impl RPGraph {
}
}
let mut result: Vec<(String, Vec)> = by_file.into_iter().collect();
- result.sort_by(|a, b| b.1.len().cmp(&a.1.len()));
+ result.sort_by_key(|entry| std::cmp::Reverse(entry.1.len()));
result
}
diff --git a/crates/rpg-encoder/src/semantic_lifting.rs b/crates/rpg-encoder/src/semantic_lifting.rs
index 317e4a8..228f850 100644
--- a/crates/rpg-encoder/src/semantic_lifting.rs
+++ b/crates/rpg-encoder/src/semantic_lifting.rs
@@ -119,8 +119,8 @@ pub fn apply_features(
pub fn aggregate_module_features(graph: &mut RPGraph) -> usize {
let module_data: Vec<(String, Vec)> = graph
.file_index
- .iter()
- .filter_map(|(_, ids)| {
+ .values()
+ .filter_map(|ids| {
let module_id = ids.iter().find(|id| {
graph
.entities
diff --git a/crates/rpg-lift/src/provider.rs b/crates/rpg-lift/src/provider.rs
index 59ded06..0137c65 100644
--- a/crates/rpg-lift/src/provider.rs
+++ b/crates/rpg-lift/src/provider.rs
@@ -67,7 +67,7 @@ impl AnthropicProvider {
model: model.unwrap_or_else(|| Self::DEFAULT_MODEL.to_string()),
agent: ureq::Agent::new_with_config(
ureq::config::Config::builder()
- .timeout_global(Some(std::time::Duration::from_secs(120)))
+ .timeout_global(Some(std::time::Duration::from_mins(2)))
.build(),
),
}
@@ -193,7 +193,7 @@ impl OpenAiProvider {
base_url: base_url.unwrap_or_else(|| Self::DEFAULT_BASE_URL.to_string()),
agent: ureq::Agent::new_with_config(
ureq::config::Config::builder()
- .timeout_global(Some(std::time::Duration::from_secs(120)))
+ .timeout_global(Some(std::time::Duration::from_mins(2)))
.build(),
),
}
diff --git a/crates/rpg-mcp/src/main.rs b/crates/rpg-mcp/src/main.rs
index abd1a7b..a262952 100644
--- a/crates/rpg-mcp/src/main.rs
+++ b/crates/rpg-mcp/src/main.rs
@@ -32,16 +32,14 @@ pub(crate) const LARGE_SCOPE_BATCHES: usize = 10;
use anyhow::Result;
use rmcp::ServiceExt;
use rpg_core::storage;
-use std::path::PathBuf;
-
use server::RpgServer;
#[tokio::main]
async fn main() -> Result<()> {
- let project_root = std::env::args()
- .nth(1)
- .map(PathBuf::from)
- .unwrap_or_else(|| std::env::current_dir().expect("failed to get current directory"));
+ let cli_args: Vec = std::env::args().collect();
+ let cli_root = RpgServer::startup_project_root_arg(cli_args.iter().map(String::as_str));
+ let cwd = std::env::current_dir().expect("failed to get current directory");
+ let project_root = RpgServer::resolve_startup_project_root(cli_root.as_deref(), cwd);
eprintln!("RPG MCP server starting for: {}", project_root.display());
@@ -51,11 +49,13 @@ async fn main() -> Result<()> {
// Auto-update graph on startup if stale (structural-only, no LLM)
{
- let mut lock = server.graph.write().await;
- if let Some(ref mut graph) = *lock
+ let project_root = server.project_root().await;
+ let root_state = server.root_state(&project_root).await;
+ let mut root_state = root_state.write().await;
+ if let Some(ref mut graph) = root_state.graph
&& let (Some(base), Ok(head)) = (
&graph.base_commit.clone(),
- rpg_encoder::evolution::get_head_sha(&server.project_root().await),
+ rpg_encoder::evolution::get_head_sha(&project_root),
)
{
if *base != head {
@@ -71,7 +71,7 @@ async fn main() -> Result<()> {
let qcache_result =
rpg_parser::paradigms::query_engine::QueryCache::compile_all(¶digm_defs);
let active_defs = rpg_parser::paradigms::detect_paradigms_toml(
- &server.project_root().await,
+ &project_root,
&detected_langs,
¶digm_defs,
);
@@ -86,27 +86,21 @@ async fn main() -> Result<()> {
});
match rpg_encoder::evolution::run_update(
graph,
- &server.project_root().await,
+ &project_root,
None,
pipeline.as_ref(),
) {
Ok(s) => {
graph.metadata.paradigms = paradigm_names;
- let _ = storage::save(&server.project_root().await, graph);
- // Persist stale entity IDs from the startup sync so
- // lifting_status sees them on the first query. Every
- // other path that produces a summary feeds
- // `modified_entity_ids` into `stale_entity_ids`
- // (`auto_sync_if_stale`, `update_rpg`). The startup
- // path is the one exception — without this, modified
- // entities from between the last lift and this startup
- // are silently dropped across the session boundary.
+ let _ = storage::save(&project_root, graph);
+ let existing_ids: std::collections::HashSet =
+ graph.entities.keys().cloned().collect();
{
- let mut stale = server.stale_entity_ids.write().await;
+ let stale = &mut root_state.stale_entity_ids;
for id in &s.modified_entity_ids {
stale.insert(id.clone());
}
- stale.retain(|id| graph.entities.contains_key(id));
+ stale.retain(|id| existing_ids.contains(id));
}
eprintln!(
" Auto-update complete: +{} -{} ~{}",
@@ -123,12 +117,15 @@ async fn main() -> Result<()> {
// changeset) match instead of redundantly re-running the
// workdir diff. Must use the real empty-workdir changeset
// hash (not an empty string) for the match to fire.
- let project_root = server.project_root().await;
- *server.last_auto_sync_head.write().await =
+ root_state.last_auto_sync_head =
rpg_encoder::evolution::get_head_sha(&project_root).ok();
- *server.last_auto_sync_changeset.write().await =
+ root_state.last_auto_sync_changeset =
Some(RpgServer::compute_changeset_hash(&[], &project_root));
- *server.last_auto_sync_workdir_paths.write().await = std::collections::HashSet::new();
+ root_state.last_auto_sync_workdir_paths = std::collections::HashSet::new();
+ drop(root_state);
+ server
+ .sync_default_root_compat_from_state(&project_root)
+ .await;
}
}
diff --git a/crates/rpg-mcp/src/params.rs b/crates/rpg-mcp/src/params.rs
index c4a6b00..fdd9b9d 100644
--- a/crates/rpg-mcp/src/params.rs
+++ b/crates/rpg-mcp/src/params.rs
@@ -8,6 +8,8 @@ use serde::Deserialize;
pub(crate) struct SearchNodeParams {
/// The search query describing what you're looking for
pub(crate) query: String,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Search mode: 'features', 'snippets', or 'auto' (default: 'auto')
pub(crate) mode: Option,
/// Optional hierarchy scope to restrict search (e.g., 'Security/auth'). Comma-separated for multiple scopes.
@@ -26,7 +28,9 @@ pub(crate) struct SearchNodeParams {
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct FetchNodeParams {
/// The entity ID to fetch (e.g., 'src/auth.rs:validate_token')
- pub(crate) entity_id: String,
+ pub(crate) entity_id: Option,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Multiple entity IDs to fetch in batch (overrides entity_id when provided)
pub(crate) entity_ids: Option>,
/// Comma-separated fields to include: "features", "source", "deps", "hierarchy". Omit for all fields.
@@ -39,7 +43,9 @@ pub(crate) struct FetchNodeParams {
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct ExploreRpgParams {
/// The entity ID to start exploration from
- pub(crate) entity_id: String,
+ pub(crate) entity_id: Option,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Multiple entity IDs to explore from in batch (overrides entity_id when provided)
pub(crate) entity_ids: Option>,
/// Traversal direction: 'upstream', 'downstream', or 'both'
@@ -56,6 +62,12 @@ pub(crate) struct ExploreRpgParams {
pub(crate) max_results: Option,
}
+#[derive(Debug, Default, Deserialize, JsonSchema)]
+pub(crate) struct RpgInfoParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
+}
+
/// Parameters for the `set_project_root` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct SetProjectRootParams {
@@ -66,6 +78,8 @@ pub(crate) struct SetProjectRootParams {
/// Parameters for the `build_rpg` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct BuildRpgParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Primary language override (auto-detected if not specified)
pub(crate) language: Option,
/// Glob pattern to include files (e.g., "src/**/*.rs")
@@ -77,15 +91,33 @@ pub(crate) struct BuildRpgParams {
/// Parameters for the `update_rpg` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct UpdateRpgParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Base commit SHA to diff from (defaults to RPG's stored base_commit)
pub(crate) since: Option,
}
+/// Parameters for the `reload_rpg` tool.
+#[derive(Debug, Default, Deserialize, JsonSchema)]
+pub(crate) struct ReloadRpgParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
+}
+
+/// Parameters for the `lifting_status` tool.
+#[derive(Debug, Default, Deserialize, JsonSchema)]
+pub(crate) struct LiftingStatusParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
+}
+
/// Parameters for the `get_entities_for_lifting` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct GetEntitiesForLiftingParams {
/// Scope specifier: file glob ("src/auth/**"), hierarchy path, entity IDs, or "*"/"all".
pub(crate) scope: String,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Batch index to retrieve (0-based). Omit or 0 for first batch.
pub(crate) batch_index: Option,
}
@@ -93,6 +125,8 @@ pub(crate) struct GetEntitiesForLiftingParams {
/// Parameters for the `submit_lift_results` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct SubmitLiftResultsParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// JSON object mapping function names to feature arrays.
/// Example: {"my_func": ["validate input", "return result"], "other": ["compute hash"]}
pub(crate) features: String,
@@ -101,6 +135,8 @@ pub(crate) struct SubmitLiftResultsParams {
/// Parameters for the `submit_hierarchy` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct SubmitHierarchyParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// JSON object mapping file paths to 3-level hierarchy paths.
/// Example: {"src/auth/login.rs": "Authentication/manage sessions/handle login"}
pub(crate) assignments: String,
@@ -109,30 +145,51 @@ pub(crate) struct SubmitHierarchyParams {
/// Parameters for the `get_files_for_synthesis` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct GetFilesForSynthesisParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Batch index to retrieve (0-based). Omit or 0 for first batch.
pub(crate) batch_index: Option,
}
+/// Parameters for the `finalize_lifting` tool.
+#[derive(Debug, Default, Deserialize, JsonSchema)]
+pub(crate) struct FinalizeLiftingParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
+}
+
+/// Parameters for the `get_routing_candidates` tool.
+#[derive(Debug, Default, Deserialize, JsonSchema)]
+pub(crate) struct GetRoutingCandidatesParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
+ /// Batch index to retrieve (0-based). For large sets, returns paginated candidates.
+ pub(crate) batch_index: Option,
+}
+
/// Parameters for the `submit_file_syntheses` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct SubmitFileSynthesesParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// JSON object mapping file paths to comma-separated feature strings.
/// Example: {"src/auth/login.rs": "handle user authentication, manage session tokens",
/// "src/db/query.rs": "build SQL queries, execute database operations"}
pub(crate) syntheses: String,
}
-/// Parameters for the `get_routing_candidates` tool.
-#[derive(Debug, Deserialize, JsonSchema)]
-pub(crate) struct GetRoutingCandidatesParams {
- /// Batch index to retrieve (0-based). For large sets, returns paginated candidates.
- #[serde(default)]
- pub(crate) batch_index: Option,
+/// Parameters for the `build_semantic_hierarchy` tool.
+#[derive(Debug, Default, Deserialize, JsonSchema)]
+pub(crate) struct BuildSemanticHierarchyParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
}
/// Parameters for the `reconstruct_plan` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct ReconstructPlanParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Maximum number of entities per execution batch (default: 8).
pub(crate) max_batch_size: Option,
/// Include file-level Module entities in the schedule (default: false).
@@ -144,6 +201,8 @@ pub(crate) struct ReconstructPlanParams {
pub(crate) struct ContextPackParams {
/// The search query describing what context you need
pub(crate) query: String,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Optional hierarchy scope to restrict search (e.g., 'Security/auth')
pub(crate) scope: Option,
/// Target token budget for the packed context (default: 4000)
@@ -159,6 +218,8 @@ pub(crate) struct ContextPackParams {
pub(crate) struct ImpactRadiusParams {
/// The entity ID to compute impact from
pub(crate) entity_id: String,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Traversal direction: 'upstream' (what depends on this), 'downstream' (what this depends on), or 'both'
pub(crate) direction: Option,
/// Maximum traversal depth (default: 3). Use -1 for unlimited.
@@ -172,6 +233,8 @@ pub(crate) struct ImpactRadiusParams {
/// Parameters for the `submit_routing_decisions` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct SubmitRoutingDecisionsParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// JSON object mapping entity IDs to routing action.
/// Value is a hierarchy path to route there, or "keep" to confirm current position.
/// Example: {"src/auth.rs:validate_token": "Security/auth/validate", "src/db.rs:query": "keep"}
@@ -186,6 +249,8 @@ pub(crate) struct SubmitRoutingDecisionsParams {
pub(crate) struct PlanChangeParams {
/// The goal or intent of the change (e.g., "add rate limiting to API endpoints")
pub(crate) goal: String,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Optional hierarchy scope to restrict search (e.g., 'Security/auth')
pub(crate) scope: Option,
/// Maximum number of relevant entities to include (default: 15)
@@ -197,6 +262,8 @@ pub(crate) struct PlanChangeParams {
pub(crate) struct FindPathsParams {
/// Source entity ID
pub(crate) source: String,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Target entity ID
pub(crate) target: String,
/// Maximum path length (default: 5). Use -1 for unlimited.
@@ -212,6 +279,8 @@ pub(crate) struct FindPathsParams {
pub(crate) struct SliceBetweenParams {
/// Entity IDs to connect (minimum 2)
pub(crate) entity_ids: Vec,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Maximum path length when searching for connections (default: 3)
pub(crate) max_depth: Option,
/// Include entity metadata (name, file, features) in output
@@ -223,6 +292,8 @@ pub(crate) struct SliceBetweenParams {
pub(crate) struct AnalyzeHealthParams {
/// Instability threshold above which entities are flagged as highly unstable (default: 0.7).
pub(crate) instability_threshold: Option,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Minimum total degree for god object detection (default: 10).
pub(crate) god_object_threshold: Option,
/// Run Rabin-Karp token-based clone detection (reads source files from disk, slower). Default: false.
@@ -237,6 +308,8 @@ pub(crate) struct AnalyzeHealthParams {
/// Parameters for the `semantic_snapshot` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub(crate) struct SemanticSnapshotParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Target token budget (default: 30000). Controls how much detail is included.
pub(crate) token_budget: Option,
/// Include dependency skeleton (default: true). Set to false to save tokens.
@@ -248,6 +321,8 @@ pub(crate) struct SemanticSnapshotParams {
pub(crate) struct DetectCyclesParams {
/// Maximum number of cycles to return (default: all). Use to limit output.
pub(crate) max_cycles: Option,
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// Minimum cycle length to report (default: 2). Use 3+ to skip trivial 2-cycles.
pub(crate) min_cycle_length: Option,
/// Maximum cycle length to detect (default: 20, prevents exponential blowup)
@@ -273,6 +348,8 @@ pub(crate) struct DetectCyclesParams {
/// Parameters for the `auto_lift` tool.
#[derive(Deserialize, JsonSchema)]
pub(crate) struct AutoLiftParams {
+ /// Optional project root override for this call only.
+ pub(crate) project_root: Option,
/// LLM provider: "anthropic", "openai", or any OpenAI-compatible endpoint.
pub(crate) provider: String,
/// API key for the provider. Use this OR api_key_env (not both). Prefer api_key_env to avoid exposing keys in tool call transcripts.
diff --git a/crates/rpg-mcp/src/server.rs b/crates/rpg-mcp/src/server.rs
index 9132753..a7ec8c9 100644
--- a/crates/rpg-mcp/src/server.rs
+++ b/crates/rpg-mcp/src/server.rs
@@ -10,6 +10,34 @@ use tokio::sync::RwLock;
use crate::types::{HierarchySession, LiftingSession, PendingRouting, load_pending_routing};
+type RootKey = PathBuf;
+
+#[derive(Default)]
+#[allow(dead_code)]
+pub(crate) struct RootRuntimeState {
+ pub(crate) graph: Option,
+ pub(crate) config: RpgConfig,
+ pub(crate) lifting_session: Option,
+ pub(crate) hierarchy_session: Option,
+ pub(crate) pending_routing: Vec,
+ pub(crate) stale_entity_ids: std::collections::HashSet,
+ #[cfg(feature = "embeddings")]
+ pub(crate) embedding_index: Option,
+ #[cfg(feature = "embeddings")]
+ pub(crate) embedding_init_failed: bool,
+ pub(crate) last_auto_sync_head: Option,
+ pub(crate) last_auto_sync_changeset: Option,
+ pub(crate) last_auto_sync_workdir_paths: std::collections::HashSet,
+}
+
+#[derive(Clone)]
+#[allow(dead_code)]
+pub(crate) struct EffectiveContext {
+ pub(crate) root: RootKey,
+ pub(crate) state: Arc>,
+ pub(crate) cross_root_notice: String,
+}
+
/// Cached protocol prompt versions (SHA256 hashes) for deduplication.
#[derive(Clone)]
pub(crate) struct PromptVersions {
@@ -65,10 +93,15 @@ impl PromptVersions {
/// cycle on the inner locks.
#[derive(Clone)]
pub(crate) struct RpgServer {
- /// Active project root. Mutable at runtime via the `set_project_root` tool
- /// so a single long-lived session can switch between projects without
- /// restart. Tools acquire a snapshot via [`RpgServer::project_root`].
- pub(crate) project_root_cell: Arc>,
+ /// Default project root. Mutable at runtime via the `set_project_root` tool
+ /// so a single long-lived session can switch the default project without
+ /// restart. Tools may override it per-call via `project_root`.
+ pub(crate) default_root: Arc>,
+ /// Root-scoped runtime state keyed by canonical project root.
+ pub(crate) roots:
+ Arc>>>>,
+ /// Compatibility fields used by the current tool implementation while the
+ /// tool surface is migrated incrementally to root-scoped state.
pub(crate) graph: Arc>>,
pub(crate) config: Arc>,
pub(crate) lifting_session: Arc>>,
@@ -76,27 +109,14 @@ pub(crate) struct RpgServer {
pub(crate) pending_routing: Arc>>,
#[cfg(feature = "embeddings")]
pub(crate) embedding_index: Arc>>,
- /// Set to true after first failed init to avoid retrying every search.
#[cfg(feature = "embeddings")]
pub(crate) embedding_init_failed: Arc,
pub(crate) tool_router: rmcp::handler::server::router::tool::ToolRouter,
/// Protocol prompt versions for deduplication.
pub(crate) prompt_versions: PromptVersions,
- /// Last git HEAD SHA at which auto-sync ran. Prevents redundant updates.
pub(crate) last_auto_sync_head: Arc>>,
- /// Entity IDs whose source was modified after their features were lifted.
- /// Populated by `auto_sync_if_stale` (from `summary.modified_entity_ids`),
- /// drained by `submit_lift_results` as entities get re-lifted. Lets
- /// `lifting_status` surface stale-feature drift even though those entities
- /// still appear "lifted" in the coverage count.
pub(crate) stale_entity_ids: Arc>>,
- /// Hash of the last-synced workdir changeset (dirty files + their stat).
- /// Combined with `last_auto_sync_head` to detect when a re-sync is needed
- /// for uncommitted/staged/unstaged changes.
pub(crate) last_auto_sync_changeset: Arc>>,
- /// Paths that were dirty at the last successful auto-sync. Lets us detect
- /// reverts: when a previously-dirty file returns to clean, the workdir
- /// diff no longer lists it — we must re-parse it to restore HEAD content.
pub(crate) last_auto_sync_workdir_paths:
Arc>>,
/// Guard: true while auto_lift is running. Rejects concurrent lift calls.
@@ -106,16 +126,322 @@ pub(crate) struct RpgServer {
impl std::fmt::Debug for RpgServer {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RpgServer")
- .field("project_root", &"")
- .field("lifting_session", &"...")
+ .field("default_root", &"")
+ .field("roots", &"