tui lib for nicer cli
This commit is contained in:
281
Cargo.lock
generated
281
Cargo.lock
generated
@@ -436,7 +436,7 @@ dependencies = [
|
|||||||
"encode_unicode",
|
"encode_unicode",
|
||||||
"libc",
|
"libc",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"unicode-width",
|
"unicode-width 0.2.0",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -475,6 +475,24 @@ dependencies = [
|
|||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "convert_case"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bb402b8d4c85569410425650ce3eddc7d698ed96d39a73f941b08fb63082f1e7"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "coolor"
|
||||||
|
version = "1.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "980c2afde4af43d6a05c5be738f9eae595cff86dce1f38f88b95058a98c027f3"
|
||||||
|
dependencies = [
|
||||||
|
"crossterm",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "core-foundation"
|
name = "core-foundation"
|
||||||
version = "0.9.4"
|
version = "0.9.4"
|
||||||
@@ -510,6 +528,115 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crokey"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "51360853ebbeb3df20c76c82aecf43d387a62860f1a59ba65ab51f00eea85aad"
|
||||||
|
dependencies = [
|
||||||
|
"crokey-proc_macros",
|
||||||
|
"crossterm",
|
||||||
|
"once_cell",
|
||||||
|
"serde",
|
||||||
|
"strict",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crokey-proc_macros"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3bf1a727caeb5ee5e0a0826a97f205a9cf84ee964b0b48239fef5214a00ae439"
|
||||||
|
dependencies = [
|
||||||
|
"crossterm",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strict",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-deque"
|
||||||
|
version = "0.8.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-epoch"
|
||||||
|
version = "0.9.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-utils"
|
||||||
|
version = "0.8.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.29.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.9.4",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"derive_more 2.0.1",
|
||||||
|
"document-features",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"rustix 1.0.8",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@@ -539,6 +666,27 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "093242cf7570c207c83073cf82f79706fe7b8317e98620a47d5be7c3d8497678"
|
||||||
|
dependencies = [
|
||||||
|
"derive_more-impl",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_more-impl"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bda628edc44c4bb645fbe0f758797143e4e07926f7ebf4e9bdfbd3d2ce621df3"
|
||||||
|
dependencies = [
|
||||||
|
"convert_case 0.7.1",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.10.7"
|
version = "0.10.7"
|
||||||
@@ -611,6 +759,15 @@ dependencies = [
|
|||||||
"const-random",
|
"const-random",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "document-features"
|
||||||
|
version = "0.2.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "95249b50c6c185bee49034bcb378a49dc2b5dff0be90ff6616d31d64febab05d"
|
||||||
|
dependencies = [
|
||||||
|
"litrs",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -818,6 +975,7 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"chrono",
|
"chrono",
|
||||||
"clap",
|
"clap",
|
||||||
|
"crossterm",
|
||||||
"dirs 5.0.1",
|
"dirs 5.0.1",
|
||||||
"g3-config",
|
"g3-config",
|
||||||
"g3-core",
|
"g3-core",
|
||||||
@@ -825,6 +983,7 @@ dependencies = [
|
|||||||
"rustyline",
|
"rustyline",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"termimad",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tracing",
|
"tracing",
|
||||||
@@ -1310,7 +1469,7 @@ dependencies = [
|
|||||||
"console",
|
"console",
|
||||||
"number_prefix",
|
"number_prefix",
|
||||||
"portable-atomic",
|
"portable-atomic",
|
||||||
"unicode-width",
|
"unicode-width 0.2.0",
|
||||||
"web-time",
|
"web-time",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1405,6 +1564,29 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex"
|
||||||
|
version = "3.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "60c7310b93682b36b98fa7ea4de998d3463ccbebd94d935d6b48ba5b6ffa7126"
|
||||||
|
dependencies = [
|
||||||
|
"lazy-regex-proc_macros",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex-proc_macros"
|
||||||
|
version = "3.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ba01db5ef81e17eb10a5e0f2109d1b3a3e29bac3070fdbd7d156bf7dbd206a1"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -1470,13 +1652,19 @@ version = "0.8.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "litrs"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "llama_cpp"
|
name = "llama_cpp"
|
||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "7f126770a2ed5e0e4596119479dc56f56b99037246bf0e36c544f7581a9458fd"
|
checksum = "7f126770a2ed5e0e4596119479dc56f56b99037246bf0e36c544f7581a9458fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"derive_more",
|
"derive_more 0.99.20",
|
||||||
"futures",
|
"futures",
|
||||||
"llama_cpp_sys",
|
"llama_cpp_sys",
|
||||||
"num_cpus",
|
"num_cpus",
|
||||||
@@ -1540,6 +1728,15 @@ version = "0.3.17"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "minimad"
|
||||||
|
version = "0.13.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a9c5d708226d186590a7b6d4a9780e2bdda5f689e0d58cd17012a298efd745d2"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "minimal-lexical"
|
name = "minimal-lexical"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
@@ -1562,6 +1759,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
|
"log",
|
||||||
"wasi 0.11.1+wasi-snapshot-preview1",
|
"wasi 0.11.1+wasi-snapshot-preview1",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
@@ -2167,7 +2365,7 @@ dependencies = [
|
|||||||
"nix",
|
"nix",
|
||||||
"radix_trie",
|
"radix_trie",
|
||||||
"unicode-segmentation",
|
"unicode-segmentation",
|
||||||
"unicode-width",
|
"unicode-width 0.2.0",
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
@@ -2340,6 +2538,27 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.18"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.6"
|
version = "1.4.6"
|
||||||
@@ -2387,6 +2606,12 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strict"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f42444fea5b87a39db4218d9422087e66a85d0e7a0963a439b07bcdf91804006"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strsim"
|
name = "strsim"
|
||||||
version = "0.11.1"
|
version = "0.11.1"
|
||||||
@@ -2461,6 +2686,22 @@ dependencies = [
|
|||||||
"windows-sys 0.60.2",
|
"windows-sys 0.60.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "termimad"
|
||||||
|
version = "0.34.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "68ff5ca043d65d4ea43b65cdb4e3aba119657d0d12caf44f93212ec3168a8e20"
|
||||||
|
dependencies = [
|
||||||
|
"coolor",
|
||||||
|
"crokey",
|
||||||
|
"crossbeam",
|
||||||
|
"lazy-regex",
|
||||||
|
"minimad",
|
||||||
|
"serde",
|
||||||
|
"thiserror 2.0.16",
|
||||||
|
"unicode-width 0.1.14",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "1.0.69"
|
version = "1.0.69"
|
||||||
@@ -2757,9 +2998,15 @@ checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "unicode-width"
|
name = "unicode-width"
|
||||||
version = "0.2.1"
|
version = "0.1.14"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4a1a07cc7db3810833284e8d372ccdc6da29741639ecc70c9ec107df0fa6154c"
|
checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-width"
|
||||||
|
version = "0.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "url"
|
name = "url"
|
||||||
@@ -2981,6 +3228,22 @@ dependencies = [
|
|||||||
"rustix 0.38.44",
|
"rustix 0.38.44",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi"
|
||||||
|
version = "0.3.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||||
|
dependencies = [
|
||||||
|
"winapi-i686-pc-windows-gnu",
|
||||||
|
"winapi-x86_64-pc-windows-gnu",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-i686-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "winapi-util"
|
name = "winapi-util"
|
||||||
version = "0.1.11"
|
version = "0.1.11"
|
||||||
@@ -2990,6 +3253,12 @@ dependencies = [
|
|||||||
"windows-sys 0.61.0",
|
"windows-sys 0.61.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winapi-x86_64-pc-windows-gnu"
|
||||||
|
version = "0.4.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-core"
|
name = "windows-core"
|
||||||
version = "0.62.1"
|
version = "0.62.1"
|
||||||
|
|||||||
@@ -19,3 +19,5 @@ dirs = "5.0"
|
|||||||
tokio-util = "0.7"
|
tokio-util = "0.7"
|
||||||
indicatif = "0.17"
|
indicatif = "0.17"
|
||||||
chrono = { version = "0.4", features = ["serde"] }
|
chrono = { version = "0.4", features = ["serde"] }
|
||||||
|
crossterm = "0.29.0"
|
||||||
|
termimad = "0.34.0"
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ use std::path::PathBuf;
|
|||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
mod tui;
|
||||||
|
use tui::SimpleOutput;
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(name = "g3")]
|
#[command(name = "g3")]
|
||||||
#[command(about = "A modular, composable AI coding agent")]
|
#[command(about = "A modular, composable AI coding agent")]
|
||||||
@@ -122,14 +125,16 @@ pub async fn run() -> Result<()> {
|
|||||||
} else if let Some(task) = cli.task {
|
} else if let Some(task) = cli.task {
|
||||||
// Single-shot mode
|
// Single-shot mode
|
||||||
info!("Executing task: {}", task);
|
info!("Executing task: {}", task);
|
||||||
|
let output = SimpleOutput::new();
|
||||||
let result = agent
|
let result = agent
|
||||||
.execute_task_with_timing(&task, None, false, cli.show_prompt, cli.show_code, true)
|
.execute_task_with_timing(&task, None, false, cli.show_prompt, cli.show_code, true)
|
||||||
.await?;
|
.await?;
|
||||||
println!("{}", result);
|
output.print_markdown(&result);
|
||||||
} else {
|
} else {
|
||||||
|
let output = SimpleOutput::new();
|
||||||
// Interactive mode (default)
|
// Interactive mode (default)
|
||||||
info!("Starting interactive mode");
|
info!("Starting interactive mode");
|
||||||
println!("📁 Workspace: {}", project.workspace().display());
|
output.print(&format!("📁 Workspace: {}", project.workspace().display()));
|
||||||
run_interactive(agent, cli.show_prompt, cli.show_code).await?;
|
run_interactive(agent, cli.show_prompt, cli.show_code).await?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,29 +142,30 @@ pub async fn run() -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -> Result<()> {
|
async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -> Result<()> {
|
||||||
|
let output = SimpleOutput::new();
|
||||||
|
|
||||||
println!();
|
output.print("");
|
||||||
println!("🤖 G3 AI Coding Agent - Interactive Mode");
|
output.print("🤖 G3 AI Coding Agent - Interactive Mode");
|
||||||
println!(
|
output.print(
|
||||||
"I solve problems by writing and executing code. Tell me what you need to accomplish!"
|
"I solve problems by writing and executing code. Tell me what you need to accomplish!"
|
||||||
);
|
);
|
||||||
println!();
|
output.print("");
|
||||||
|
|
||||||
// Display provider and model information
|
// Display provider and model information
|
||||||
match agent.get_provider_info() {
|
match agent.get_provider_info() {
|
||||||
Ok((provider, model)) => {
|
Ok((provider, model)) => {
|
||||||
println!("🔧 Provider: {} | Model: {}", provider, model);
|
output.print(&format!("🔧 Provider: {} | Model: {}", provider, model));
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
error!("Failed to get provider info: {}", e);
|
error!("Failed to get provider info: {}", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
println!();
|
output.print("");
|
||||||
println!("Type 'exit' or 'quit' to exit, use Up/Down arrows for command history");
|
output.print("Type 'exit' or 'quit' to exit, use Up/Down arrows for command history");
|
||||||
println!("For multiline input: use \\ at the end of a line to continue");
|
output.print("For multiline input: use \\ at the end of a line to continue");
|
||||||
println!("Submit multiline with Enter (without backslash)");
|
output.print("Submit multiline with Enter (without backslash)");
|
||||||
println!();
|
output.print("");
|
||||||
|
|
||||||
// Initialize rustyline editor with history
|
// Initialize rustyline editor with history
|
||||||
let mut rl = DefaultEditor::new()?;
|
let mut rl = DefaultEditor::new()?;
|
||||||
@@ -180,7 +186,7 @@ async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -
|
|||||||
|
|
||||||
loop {
|
loop {
|
||||||
// Display context window progress bar before each prompt
|
// Display context window progress bar before each prompt
|
||||||
display_context_progress(&agent);
|
display_context_progress(&agent, &output);
|
||||||
|
|
||||||
// Adjust prompt based on whether we're in multi-line mode
|
// Adjust prompt based on whether we're in multi-line mode
|
||||||
let prompt = if in_multiline { "... > " } else { "g3> " };
|
let prompt = if in_multiline { "... > " } else { "g3> " };
|
||||||
@@ -220,7 +226,7 @@ async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Process the multiline input
|
// Process the multiline input
|
||||||
execute_task(&mut agent, &input, show_prompt, show_code).await;
|
execute_task(&mut agent, &input, show_prompt, show_code, &output).await;
|
||||||
} else {
|
} else {
|
||||||
// Single line input
|
// Single line input
|
||||||
let input = line.trim().to_string();
|
let input = line.trim().to_string();
|
||||||
@@ -237,23 +243,23 @@ async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -
|
|||||||
rl.add_history_entry(&input)?;
|
rl.add_history_entry(&input)?;
|
||||||
|
|
||||||
// Process the single line input
|
// Process the single line input
|
||||||
execute_task(&mut agent, &input, show_prompt, show_code).await;
|
execute_task(&mut agent, &input, show_prompt, show_code, &output).await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Interrupted) => {
|
Err(ReadlineError::Interrupted) => {
|
||||||
// Ctrl-C pressed
|
// Ctrl-C pressed
|
||||||
if in_multiline {
|
if in_multiline {
|
||||||
// Cancel multiline input
|
// Cancel multiline input
|
||||||
println!("Multi-line input cancelled");
|
output.print("Multi-line input cancelled");
|
||||||
multiline_buffer.clear();
|
multiline_buffer.clear();
|
||||||
in_multiline = false;
|
in_multiline = false;
|
||||||
} else {
|
} else {
|
||||||
println!("CTRL-C");
|
output.print("CTRL-C");
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
Err(ReadlineError::Eof) => {
|
Err(ReadlineError::Eof) => {
|
||||||
println!("CTRL-D");
|
output.print("CTRL-D");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -268,14 +274,14 @@ async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -
|
|||||||
let _ = rl.save_history(history_path);
|
let _ = rl.save_history(history_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("👋 Goodbye!");
|
output.print("👋 Goodbye!");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn execute_task(agent: &mut Agent, input: &str, show_prompt: bool, show_code: bool) {
|
async fn execute_task(agent: &mut Agent, input: &str, show_prompt: bool, show_code: bool, output: &SimpleOutput) {
|
||||||
// Show thinking indicator immediately
|
// Show thinking indicator immediately
|
||||||
print!("🤔 Thinking...");
|
output.print("🤔 Thinking...");
|
||||||
std::io::stdout().flush().unwrap();
|
// Note: flush is handled internally by println
|
||||||
|
|
||||||
// Create cancellation token for this request
|
// Create cancellation token for this request
|
||||||
let cancellation_token = CancellationToken::new();
|
let cancellation_token = CancellationToken::new();
|
||||||
@@ -290,16 +296,16 @@ async fn execute_task(agent: &mut Agent, input: &str, show_prompt: bool, show_co
|
|||||||
}
|
}
|
||||||
_ = tokio::signal::ctrl_c() => {
|
_ = tokio::signal::ctrl_c() => {
|
||||||
cancel_token_clone.cancel();
|
cancel_token_clone.cancel();
|
||||||
println!("\n⚠️ Operation cancelled by user (Ctrl+C)");
|
output.print("\n⚠️ Operation cancelled by user (Ctrl+C)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match execution_result {
|
match execution_result {
|
||||||
Ok(response) => println!("{}", response),
|
Ok(response) => output.print_markdown(&response),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if e.to_string().contains("cancelled") {
|
if e.to_string().contains("cancelled") {
|
||||||
println!("⚠️ Operation cancelled by user");
|
output.print("⚠️ Operation cancelled by user");
|
||||||
} else {
|
} else {
|
||||||
error!("Error: {}", e);
|
error!("Error: {}", e);
|
||||||
}
|
}
|
||||||
@@ -307,24 +313,9 @@ async fn execute_task(agent: &mut Agent, input: &str, show_prompt: bool, show_co
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn display_context_progress(agent: &Agent) {
|
fn display_context_progress(agent: &Agent, output: &SimpleOutput) {
|
||||||
let context = agent.get_context_window();
|
let context = agent.get_context_window();
|
||||||
let percentage = context.percentage_used();
|
output.print_context(context.used_tokens, context.total_tokens, context.percentage_used());
|
||||||
|
|
||||||
// Create a simple visual progress bar using the requested characters (10 dots max)
|
|
||||||
let bar_width = 10;
|
|
||||||
let filled_width = ((percentage / 100.0) * bar_width as f32) as usize;
|
|
||||||
let empty_width = bar_width - filled_width;
|
|
||||||
|
|
||||||
let filled_chars = "●".repeat(filled_width);
|
|
||||||
let empty_chars = "○".repeat(empty_width);
|
|
||||||
let progress_bar = format!("{}{}", filled_chars, empty_chars);
|
|
||||||
|
|
||||||
// Print context info with visual progress bar
|
|
||||||
println!(
|
|
||||||
"Context: {} {:.1}% | {}/{} tokens",
|
|
||||||
progress_bar, percentage, context.used_tokens, context.total_tokens
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set up the workspace directory for autonomous mode
|
/// Set up the workspace directory for autonomous mode
|
||||||
@@ -342,10 +333,11 @@ fn setup_workspace_directory() -> Result<PathBuf> {
|
|||||||
// Create the directory if it doesn't exist
|
// Create the directory if it doesn't exist
|
||||||
if !workspace_dir.exists() {
|
if !workspace_dir.exists() {
|
||||||
std::fs::create_dir_all(&workspace_dir)?;
|
std::fs::create_dir_all(&workspace_dir)?;
|
||||||
println!(
|
let output = SimpleOutput::new();
|
||||||
|
output.print(&format!(
|
||||||
"📁 Created workspace directory: {}",
|
"📁 Created workspace directory: {}",
|
||||||
workspace_dir.display()
|
workspace_dir.display()
|
||||||
);
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(workspace_dir)
|
Ok(workspace_dir)
|
||||||
@@ -359,14 +351,16 @@ async fn run_autonomous(
|
|||||||
show_code: bool,
|
show_code: bool,
|
||||||
max_turns: usize,
|
max_turns: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
println!("🤖 G3 AI Coding Agent - Autonomous Mode");
|
let output = SimpleOutput::new();
|
||||||
println!("📁 Using workspace: {}", project.workspace().display());
|
|
||||||
|
output.print("🤖 G3 AI Coding Agent - Autonomous Mode");
|
||||||
|
output.print(&format!("📁 Using workspace: {}", project.workspace().display()));
|
||||||
|
|
||||||
// Check if requirements exist
|
// Check if requirements exist
|
||||||
if !project.has_requirements() {
|
if !project.has_requirements() {
|
||||||
println!("❌ Error: requirements.md not found in workspace directory");
|
output.print("❌ Error: requirements.md not found in workspace directory");
|
||||||
println!(" Please create a requirements.md file with your project requirements at:");
|
output.print(" Please create a requirements.md file with your project requirements at:");
|
||||||
println!(" {}/requirements.md", project.workspace().display());
|
output.print(&format!(" {}/requirements.md", project.workspace().display()));
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,20 +368,20 @@ async fn run_autonomous(
|
|||||||
let requirements = match project.read_requirements()? {
|
let requirements = match project.read_requirements()? {
|
||||||
Some(content) => content,
|
Some(content) => content,
|
||||||
None => {
|
None => {
|
||||||
println!("❌ Error: Could not read requirements.md");
|
output.print("❌ Error: Could not read requirements.md");
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("📋 Requirements loaded from requirements.md");
|
output.print("📋 Requirements loaded from requirements.md");
|
||||||
println!("🔄 Starting coach-player feedback loop...");
|
output.print("🔄 Starting coach-player feedback loop...");
|
||||||
|
|
||||||
let mut turn = 1;
|
let mut turn = 1;
|
||||||
let mut coach_feedback = String::new();
|
let mut coach_feedback = String::new();
|
||||||
let mut implementation_approved = false;
|
let mut implementation_approved = false;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
println!("\n=== TURN {}/{} - PLAYER MODE ===", turn, max_turns);
|
output.print(&format!("\n=== TURN {}/{} - PLAYER MODE ===", turn, max_turns));
|
||||||
|
|
||||||
// Player mode: implement requirements (with coach feedback if available)
|
// Player mode: implement requirements (with coach feedback if available)
|
||||||
let player_prompt = if coach_feedback.is_empty() {
|
let player_prompt = if coach_feedback.is_empty() {
|
||||||
@@ -402,13 +396,13 @@ async fn run_autonomous(
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
println!("🎯 Starting player implementation...");
|
output.print("🎯 Starting player implementation...");
|
||||||
let player_result = agent
|
let player_result = agent
|
||||||
.execute_task_with_timing(&player_prompt, None, false, show_prompt, show_code, true)
|
.execute_task_with_timing(&player_prompt, None, false, show_prompt, show_code, true)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if let Err(e) = player_result {
|
if let Err(e) = player_result {
|
||||||
println!("❌ Player implementation failed: {}", e);
|
output.print(&format!("❌ Player implementation failed: {}", e));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new agent instance for coach mode to ensure fresh context
|
// Create a new agent instance for coach mode to ensure fresh context
|
||||||
@@ -418,7 +412,7 @@ async fn run_autonomous(
|
|||||||
// Ensure coach agent is also in the workspace directory
|
// Ensure coach agent is also in the workspace directory
|
||||||
project.enter_workspace()?;
|
project.enter_workspace()?;
|
||||||
|
|
||||||
println!("\n=== TURN {}/{} - COACH MODE ===", turn, max_turns);
|
output.print(&format!("\n=== TURN {}/{} - COACH MODE ===", turn, max_turns));
|
||||||
|
|
||||||
// Coach mode: critique the implementation
|
// Coach mode: critique the implementation
|
||||||
let coach_prompt = format!(
|
let coach_prompt = format!(
|
||||||
@@ -442,26 +436,26 @@ Keep your response concise and focused on actionable items.",
|
|||||||
requirements
|
requirements
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("🎓 Starting coach review...");
|
output.print("🎓 Starting coach review...");
|
||||||
let coach_result = coach_agent
|
let coach_result = coach_agent
|
||||||
.execute_task_with_timing(&coach_prompt, None, false, show_prompt, show_code, true)
|
.execute_task_with_timing(&coach_prompt, None, false, show_prompt, show_code, true)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
println!("🎓 Coach review completed");
|
output.print("🎓 Coach review completed");
|
||||||
println!("Coach feedback: {}", coach_result);
|
output.print(&format!("Coach feedback: {}", coach_result));
|
||||||
|
|
||||||
// Check if coach approved the implementation
|
// Check if coach approved the implementation
|
||||||
if coach_result.contains("IMPLEMENTATION_APPROVED") {
|
if coach_result.contains("IMPLEMENTATION_APPROVED") {
|
||||||
println!("\n=== SESSION COMPLETED - IMPLEMENTATION APPROVED ===");
|
output.print("\n=== SESSION COMPLETED - IMPLEMENTATION APPROVED ===");
|
||||||
println!("✅ Coach approved the implementation!");
|
output.print("✅ Coach approved the implementation!");
|
||||||
implementation_approved = true;
|
implementation_approved = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if we've reached max turns
|
// Check if we've reached max turns
|
||||||
if turn >= max_turns {
|
if turn >= max_turns {
|
||||||
println!("\n=== SESSION COMPLETED - MAX TURNS REACHED ===");
|
output.print("\n=== SESSION COMPLETED - MAX TURNS REACHED ===");
|
||||||
println!("⏰ Maximum turns ({}) reached", max_turns);
|
output.print(&format!("⏰ Maximum turns ({}) reached", max_turns));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,13 +463,13 @@ Keep your response concise and focused on actionable items.",
|
|||||||
coach_feedback = coach_result;
|
coach_feedback = coach_result;
|
||||||
turn += 1;
|
turn += 1;
|
||||||
|
|
||||||
println!("🔄 Coach provided feedback for next iteration");
|
output.print("🔄 Coach provided feedback for next iteration");
|
||||||
}
|
}
|
||||||
|
|
||||||
if implementation_approved {
|
if implementation_approved {
|
||||||
println!("\n🎉 Autonomous mode completed successfully");
|
output.print("\n🎉 Autonomous mode completed successfully");
|
||||||
} else {
|
} else {
|
||||||
println!("\n🔄 Autonomous mode completed (max iterations)");
|
output.print("\n🔄 Autonomous mode completed (max iterations)");
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
47
crates/g3-cli/src/tui.rs
Normal file
47
crates/g3-cli/src/tui.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use crossterm::style::Color;
|
||||||
|
use termimad::MadSkin;
|
||||||
|
|
||||||
|
/// Simple output handler with markdown support
|
||||||
|
pub struct SimpleOutput {
|
||||||
|
mad_skin: MadSkin,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleOutput {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let mut mad_skin = MadSkin::default();
|
||||||
|
// Configure termimad skin for better markdown rendering
|
||||||
|
mad_skin.set_headers_fg(Color::Cyan);
|
||||||
|
mad_skin.bold.set_fg(Color::Yellow);
|
||||||
|
mad_skin.italic.set_fg(Color::Magenta);
|
||||||
|
mad_skin.code_block.set_bg(Color::Rgb { r: 40, g: 40, b: 40 });
|
||||||
|
|
||||||
|
Self { mad_skin }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print(&self, text: &str) {
|
||||||
|
println!("{}", text);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_markdown(&self, markdown: &str) {
|
||||||
|
self.mad_skin.print_text(markdown);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_status(&self, status: &str) {
|
||||||
|
println!("📊 {}", status);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn print_context(&self, used: u32, total: u32, percentage: f32) {
|
||||||
|
let bar_width = 10;
|
||||||
|
let filled_width = ((percentage / 100.0) * bar_width as f32) as usize;
|
||||||
|
let empty_width = bar_width - filled_width;
|
||||||
|
|
||||||
|
let filled_chars = "●".repeat(filled_width);
|
||||||
|
let empty_chars = "○".repeat(empty_width);
|
||||||
|
let progress_bar = format!("{}{}", filled_chars, empty_chars);
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Context: {} {:.1}% | {}/{} tokens",
|
||||||
|
progress_bar, percentage, used, total
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user