diff --git a/.env.example b/.env.example index 43ce0a1..22dc12f 100644 --- a/.env.example +++ b/.env.example @@ -5,7 +5,7 @@ GITHUB_TOKEN=ghp_your_github_token_here # Repository Information # Format: owner/repo (e.g., YourUsername/my-project) -REPOSITORY=ShunsukeHayashi/miyabi-cli-standalone +REPOSITORY=ShunsukeHayashi/mergegate # Anthropic API Key (optional for local development) # Get key at: https://console.anthropic.com/ diff --git a/.gitignore b/.gitignore index 5cfa934..598ae75 100644 --- a/.gitignore +++ b/.gitignore @@ -176,3 +176,6 @@ yarn-error.log* .ai/ .env .gitnexus +project_memory/task-events.jsonl +project_memory/tasks.snapshot.json +project_memory/.tasks.lock diff --git a/AGENTS.md b/AGENTS.md index e2c99f2..7262233 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,7 +1,7 @@ # Repository Guidelines ## Project Structure & Module Organization -- Rust workspace lives under `crates/`: `miyabi-cli` (CLI entry), `miyabi-tui` (UI), and `miyabi-core` (shared logic, config, sessions, Polaris/DTP: `gate`, `store`, `lock`, `protocol`). Shared workspace config in `Cargo.toml`. +- Rust workspace lives under `crates/`: `mergegate-cli` (CLI entry), `mergegate-tui` (UI), and `mergegate-core` (shared logic, config, sessions, MergeGate/DTP: `gate`, `store`, `lock`, `protocol`). Shared workspace config in `Cargo.toml`. - ルートに `package.json` はない(過去テンプレート由来の記述は削除済み)。フロント系ツールが別ディレクトリにあれば、その README に従う。 - Supporting assets: `.claude/` agent configs, `docs/` for design notes, `project_memory/` for Polaris タスク台帳(`tasks.json`)、`.github/` for CI workflows。 diff --git a/CLAUDE.md b/CLAUDE.md index 6c5b302..b47797c 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -181,7 +181,7 @@ miyabi gate manual-complete --reason "理由" --operator "名前" # GitNexus — Code Intelligence -This project is indexed by GitNexus as **miyabi-cli-standalone** (5691 symbols, 12441 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. +This project is indexed by GitNexus as **miyabi-cli-standalone** (5866 symbols, 12893 relationships, 300 execution flows). Use the GitNexus MCP tools to understand code, assess impact, and navigate safely. > If any GitNexus tool warns the index is stale, run `npx gitnexus analyze` in terminal first. diff --git a/Cargo.lock b/Cargo.lock index fc320e8..60a6895 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -146,6 +146,21 @@ dependencies = [ "serde", ] +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + [[package]] name = "bitflags" version = "2.10.0" @@ -209,6 +224,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "chrono" version = "0.4.42" @@ -301,16 +322,6 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -498,15 +509,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "equivalent" version = "1.0.2" @@ -592,21 +594,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -741,8 +728,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -752,9 +741,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -778,25 +769,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "half" version = "2.7.1" @@ -881,7 +853,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -907,22 +878,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", + "webpki-roots", ] [[package]] @@ -944,11 +900,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -1302,6 +1256,12 @@ dependencies = [ "hashbrown 0.15.5", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "matchers" version = "0.2.0" @@ -1318,10 +1278,72 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +name = "mergegate-cli" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "clap", + "crossterm 0.29.0", + "mergegate-core", + "mergegate-tui", + "ratatui", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "mergegate-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "async-trait", + "chrono", + "dirs", + "fs2", + "futures", + "git2", + "glob", + "proptest", + "regex", + "reqwest", + "serde", + "serde_json", + "serde_yaml", + "tempfile", + "thiserror 2.0.17", + "tokio", + "toml", + "tracing", + "tracing-subscriber", + "uuid", +] + +[[package]] +name = "mergegate-tui" +version = "0.1.0" +dependencies = [ + "anyhow", + "arboard", + "chrono", + "crossterm 0.29.0", + "futures", + "mergegate-core", + "once_cell", + "pulldown-cmark", + "ratatui", + "serde", + "serde_json", + "syntect", + "textwrap", + "thiserror 2.0.17", + "tokio", + "unicode-width 0.2.0", + "uuid", +] [[package]] name = "miniz_oxide" @@ -1345,72 +1367,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "miyabi-cli" -version = "0.1.0" -dependencies = [ - "anyhow", - "chrono", - "clap", - "crossterm 0.29.0", - "miyabi-core", - "miyabi-tui", - "ratatui", - "serde_json", - "tokio", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "miyabi-core" -version = "0.1.0" -dependencies = [ - "anyhow", - "async-trait", - "chrono", - "dirs", - "fs2", - "futures", - "git2", - "glob", - "regex", - "reqwest", - "serde", - "serde_json", - "serde_yaml", - "tempfile", - "thiserror 2.0.17", - "tokio", - "toml", - "tracing", - "tracing-subscriber", - "uuid", -] - -[[package]] -name = "miyabi-tui" -version = "0.1.0" -dependencies = [ - "anyhow", - "arboard", - "chrono", - "crossterm 0.29.0", - "futures", - "miyabi-core", - "once_cell", - "pulldown-cmark", - "ratatui", - "serde", - "serde_json", - "syntect", - "textwrap", - "thiserror 2.0.17", - "tokio", - "unicode-width 0.2.0", - "uuid", -] - [[package]] name = "moxcms" version = "0.7.9" @@ -1421,23 +1377,6 @@ dependencies = [ "pxfm", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "nu-ansi-term" version = "0.50.3" @@ -1569,38 +1508,21 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "openssl-probe" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" +[[package]] +name = "openssl-src" +version = "300.5.4+3.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507b3792995dae9b0df8a1c1e3771e8418b7c2d9f0baeba32e6fe8b06c7cb72" +dependencies = [ + "cc", +] + [[package]] name = "openssl-sys" version = "0.9.111" @@ -1609,6 +1531,7 @@ checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" dependencies = [ "cc", "libc", + "openssl-src", "pkg-config", "vcpkg", ] @@ -1713,6 +1636,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.103" @@ -1722,6 +1654,25 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + [[package]] name = "pulldown-cmark" version = "0.12.2" @@ -1750,6 +1701,12 @@ dependencies = [ "num-traits", ] +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + [[package]] name = "quick-error" version = "2.0.1" @@ -1765,6 +1722,61 @@ dependencies = [ "memchr", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.42" @@ -1780,6 +1792,44 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core", +] + [[package]] name = "ratatui" version = "0.29.0" @@ -1858,30 +1908,27 @@ checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" dependencies = [ "base64", "bytes", - "encoding_rs", "futures-core", "futures-util", - "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", - "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tokio-util", "tower", "tower-http", @@ -1891,6 +1938,7 @@ dependencies = [ "wasm-bindgen-futures", "wasm-streams", "web-sys", + "webpki-roots", ] [[package]] @@ -1907,6 +1955,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustix" version = "0.38.44" @@ -1940,6 +1994,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1952,6 +2007,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94182ad936a0c91c324cd46c6511b9510ed16af436d7b5bab34beab0afd55f7a" dependencies = [ + "web-time", "zeroize", ] @@ -1972,6 +2028,18 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error 1.2.3", + "tempfile", + "wait-timeout", +] + [[package]] name = "ryu" version = "1.0.20" @@ -1987,44 +2055,12 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.228" @@ -2279,27 +2315,6 @@ dependencies = [ "yaml-rust", ] -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tempfile" version = "3.23.0" @@ -2382,7 +2397,7 @@ dependencies = [ "fax", "flate2", "half", - "quick-error", + "quick-error 2.0.1", "weezl", "zune-jpeg", ] @@ -2428,6 +2443,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -2455,16 +2485,6 @@ dependencies = [ "syn", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -2654,6 +2674,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + [[package]] name = "unicase" version = "2.8.1" @@ -2761,6 +2787,15 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + [[package]] name = "walkdir" version = "2.5.0" @@ -2876,6 +2911,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "weezl" version = "0.1.12" @@ -2954,17 +3008,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link", - "windows-result", - "windows-strings", -] - [[package]] name = "windows-result" version = "0.4.1" diff --git a/Cargo.toml b/Cargo.toml index 23a196d..e597be3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [workspace] resolver = "2" members = [ - "crates/miyabi-cli", - "crates/miyabi-tui", - "crates/miyabi-core", + "crates/mergegate-cli", + "crates/mergegate-tui", + "crates/mergegate-core", ] [workspace.package] @@ -12,9 +12,9 @@ edition = "2021" rust-version = "1.75" authors = ["Miyabi Team"] license = "BUSL-1.1" -repository = "https://github.com/ShunsukeHayashi/miyabi-cli-standalone" -homepage = "https://github.com/ShunsukeHayashi/miyabi-cli-standalone" -description = "Miyabi - Autonomous AI Development Framework" +repository = "https://github.com/ShunsukeHayashi/mergegate" +homepage = "https://github.com/ShunsukeHayashi/mergegate" +description = "MergeGate - Deterministic task execution and merge workflow for AI-assisted development" keywords = ["ai", "cli", "tui", "autonomous", "development"] categories = ["command-line-utilities", "development-tools"] diff --git a/MIYABI.md b/MIYABI.md index 6fa0a5d..a85f72f 100644 --- a/MIYABI.md +++ b/MIYABI.md @@ -282,7 +282,7 @@ cargo clippy --all-targets -- -D warnings cargo fmt --all --check # 実行 -cargo run -p miyabi-cli +cargo run -p mergegate-cli --bin mergegate ``` ## ライセンス diff --git a/README.github.md b/README.github.md index fa4740f..ba85080 100644 --- a/README.github.md +++ b/README.github.md @@ -1,2 +1,13 @@ -# miyabi-cli-standalone -Autonomous development powered by Agentic OS +# MergeGate +Deterministic execution protocol for AI-assisted development. + +Execution layer for GitNexus-style code intelligence: +- `GitNexus`: understand the codebase +- `MergeGate`: execute changes safely + +Current CLI entrypoints: `miyabi` and `mergegate` + +- `miyabi tui` / `mergegate tui`: terminal assistant +- `miyabi gate` / `mergegate gate`: deterministic task execution, file locks, and PR/merge workflow + +Start with `cargo build --release`, then run `./target/release/mergegate --help` or `./target/release/mergegate gate guide`. diff --git a/README.md b/README.md index 93ddf37..1b256a6 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,62 @@ -# Miyabi CLI +# MergeGate -A powerful terminal-based AI assistant with TUI (Terminal User Interface) built in Rust. +MergeGate is a deterministic execution protocol for AI-assisted development. +It is the execution layer for GitNexus-style code intelligence: GitNexus helps agents understand what will break, and MergeGate makes them execute that work in a safe, verifiable order. + +In short: + +- `GitNexus`: understand the codebase +- `MergeGate`: execute changes safely + +The project currently supports both `miyabi` and `mergegate` as CLI entrypoints. `miyabi` remains the legacy/default command, and `mergegate` is the product-aligned alias. + +This repository has two common entry points: + +- `miyabi tui` / `mergegate tui`: interactive terminal assistant +- `miyabi gate` / `mergegate gate`: deterministic task execution and file-lock workflow for repo work + +If you arrived here because of deterministic execution, task locks, PR gates, or agent-safe repo work, start with `mergegate gate` or `miyabi gate`, not the TUI. ## 60-Second Setup ```bash # 1. Clone and build -git clone https://github.com/ShunsukeHayashi/miyabi-cli-standalone.git -cd miyabi-cli-standalone +git clone https://github.com/ShunsukeHayashi/mergegate.git +cd mergegate cargo build --release # 2. Set API key export ANTHROPIC_API_KEY="sk-ant-..." # 3. Run -./target/release/miyabi tui +./target/release/mergegate tui ``` -That's it! Start chatting with Claude in your terminal. +That's it. Start with the TUI, or jump straight into `mergegate gate` for repo workflow control. --- -## Features +## Why MergeGate -- **Interactive TUI** - Beautiful terminal interface with markdown rendering -- **Chat Mode** - Conversational AI assistant -- **Agent Mode** - Autonomous task execution with tool approval -- **Session Management** - Persist and resume conversations +- **Execution layer for code intelligence** - Pair blast-radius understanding with deterministic execution, not chat-only automation +- **Deterministic task execution** - Register, analyze, lock, review, and merge in a verifiable order +- **Repo-safe agent workflow** - File locks and gate checks reduce accidental overlap and unsafe edits +- **Interactive TUI** - Terminal assistant for chat, prompts, and agent execution +- **Agent mode** - Autonomous execution with tool approval +- **Session management** - Persist and resume conversations - **Configurable** - TOML-based configuration -- **Extended Thinking** - Support for Claude 4.5+ extended thinking + +## Positioning + +MergeGate was designed for teams that already believe code understanding is not enough. +Knowing the blast radius of a change is only half of the problem. Agents also need a protocol for when work starts, which files are locked, what must be recorded before editing, and what counts as done. + +That is the role split: + +- `GitNexus`: query, context, impact, detect-changes, rename, wiki, graph exploration +- `MergeGate`: register, impact record, assign, file lock, branch, PR, merge, completion discipline + +If GitNexus answers "what is this change connected to?", MergeGate answers "how do we carry it through safely?" ## Installation @@ -40,16 +68,22 @@ That's it! Start chatting with Claude in your terminal. ### Build from Source ```bash -git clone https://github.com/ShunsukeHayashi/miyabi-cli-standalone.git -cd miyabi-cli-standalone +git clone https://github.com/ShunsukeHayashi/mergegate.git +cd mergegate cargo build --release ``` -Binary will be at `target/release/miyabi`. +Binary will be at `target/release/miyabi` and `target/release/mergegate`. ## Quick Start -### 1. Generate Config +Choose the path that matches what you want to do. + +### A. Use MergeGate as a terminal assistant + +Run the TUI after configuring your API key. + +#### 1. Generate Config ```bash ./target/release/miyabi init @@ -57,7 +91,7 @@ Binary will be at `target/release/miyabi`. This creates `~/.miyabi/config.toml`. -### 2. Set API Key +#### 2. Set API Key Edit `~/.miyabi/config.toml`: @@ -72,7 +106,7 @@ Or use environment variable: export ANTHROPIC_API_KEY="sk-ant-..." ``` -### 3. Launch TUI +#### 3. Launch TUI ```bash ./target/release/miyabi tui @@ -80,8 +114,55 @@ export ANTHROPIC_API_KEY="sk-ant-..." ./target/release/miyabi ``` +### B. Use MergeGate as a task execution gate + +If you are working inside a repository and want task registration, impact tracking, file locks, and PR/merge recording, use `mergegate gate` or `miyabi gate`. + +#### 1. Check whether the repo is already initialized + +```bash +./target/release/miyabi gate status +``` + +If you see `tasks: 0`, that means the ledger exists but is currently empty. + +#### 2. Initialize MergeGate for this repo if needed + +```bash +./target/release/miyabi gate init +``` + +This creates `project_memory/tasks.json` and prepares the repo for gate-managed work. + +#### 3. Read the built-in workflow guide + +```bash +./target/release/miyabi gate guide +``` + +#### 4. Start a task + +```bash +./target/release/miyabi gate register --issue 123 --title "Fix login redirect" +./target/release/miyabi gate impact issue-123 --risk medium --symbols 3 +./target/release/miyabi gate assign issue-123 --agent codex --node macbook --files "src/auth.rs" +``` + +Typical flow: + +1. `register` +2. `impact` +3. `assign` +4. implement +5. `branch` +6. `pr` +7. `merge` or `manual-complete` + ## Usage +`MergeGate` is the product and documentation name. +Both `miyabi` and `mergegate` work today. + ### CLI Commands ```bash @@ -94,6 +175,11 @@ miyabi sessions -d # Delete session miyabi sessions -e # Export session to JSON miyabi version # Show version info miyabi agent # Run autonomous agent +miyabi gate status # Show task ledger status +miyabi gate init # Initialize MergeGate in the current repo +miyabi gate guide # Show the full gate workflow guide +mergegate gate status # Same gate workflow with product-aligned command +mergegate tui # Same TUI with product-aligned command ``` ### CLI Options @@ -187,7 +273,7 @@ Agent mode features: ## Core Library Features -The `miyabi-core` crate provides powerful utilities for building AI applications. +The `mergegate-core` crate provides powerful utilities for building AI applications. ### Git Utilities @@ -373,10 +459,10 @@ flags.load_from_map(config); ## Project Structure ``` -miyabi-cli-standalone/ +mergegate/ ├── crates/ -│ ├── miyabi-cli/ # CLI entry point -│ ├── miyabi-core/ # Core library +│ ├── mergegate-cli/ # CLI entry point +│ ├── mergegate-core/ # Core library │ │ ├── agent.rs # Agent system │ │ ├── anthropic.rs # Anthropic API client │ │ ├── cache.rs # TTL cache system @@ -391,7 +477,7 @@ miyabi-cli-standalone/ │ │ ├── session.rs # Session management │ │ ├── tools.rs # Built-in tools │ │ └── ... -│ └── miyabi-tui/ # TUI implementation +│ └── mergegate-tui/ # TUI implementation │ ├── app.rs # Main application │ ├── views.rs # UI views │ └── ... @@ -482,7 +568,7 @@ RUST_LOG=debug ./target/release/miyabi tui - Check `miyabi --help` for CLI options - Press `F1` in TUI for keybindings -- Open an issue: https://github.com/ShunsukeHayashi/miyabi-cli-standalone/issues +- Open an issue: https://github.com/ShunsukeHayashi/mergegate/issues ## License diff --git a/autorun/sprint-1-today/TASKS.md b/autorun/sprint-1-today/TASKS.md index 2f5b2da..beec19a 100644 --- a/autorun/sprint-1-today/TASKS.md +++ b/autorun/sprint-1-today/TASKS.md @@ -4,7 +4,7 @@ ## 1.1 verify_merge ラッパー (~50行) -- [ ] `crates/miyabi-core/src/protocol.rs` に `verify_merge()` を追加 +- [x] `crates/miyabi-core/src/protocol.rs` に `verify_merge()` を追加 - 既存の `get_pull_request()` (github.rs) を呼ぶ - `PullRequest.merge_commit_sha` を取得 - `pr.state == "merged"` を検証 @@ -12,59 +12,53 @@ - tasks.json 更新: merge_commit + state → done - ロック解放 - 後続タスク blocked → pending -- [ ] CLI に `miyabi gate verify-merge ` サブコマンド追加 -- [ ] テスト: mock PullRequest で merged → done 遷移 +- [x] CLI に `miyabi gate verify-merge ` サブコマンド追加 +- [x] テスト: mock PullRequest で merged → done 遷移 ## 1.2 escape hatch (~50行) -- [ ] `protocol.rs` に `force_unlock()` 追加 +- [x] `protocol.rs` に `force_unlock()` 追加 ``` fn force_unlock(&self, task_id: &str, reason: &str, operator: &str) -> Result<()> ``` - ロック即解放 - event log に reason + operator を記録 - state は変更しない(implementing のまま) -- [ ] `protocol.rs` に `manual_complete()` 追加 +- [x] `protocol.rs` に `manual_complete()` 追加 ``` fn manual_complete(&self, task_id: &str, reason: &str, operator: &str) -> Result<()> ``` - PR/merge なしで done に遷移 - event log に reason + operator + "manual" を記録 - - CompletionMode::Manual として区別 -- [ ] CLI に `miyabi gate force-unlock --reason R --operator O` 追加 -- [ ] CLI に `miyabi gate manual-complete --reason R --operator O` 追加 -- [ ] テスト: force_unlock → ロック解放確認 -- [ ] テスト: manual_complete → done 遷移 + event 記録 + - `CompletionMode::Manual` として区別(2026-04-10 実装反映) +- [x] CLI に `miyabi gate force-unlock --reason R --operator O` 追加 +- [x] CLI に `miyabi gate manual-complete --reason R --operator O` 追加 +- [x] テスト: force_unlock → ロック解放確認 +- [x] テスト: manual_complete → done 遷移 + event 記録 ## 1.3 E2E テスト (~100行) -- [ ] `crates/miyabi-core/src/protocol.rs` の tests モジュールに E2E 追加 +- [x] `crates/miyabi-core/src/protocol.rs` の tests モジュールに E2E 追加(`full_lifecycle_register_to_merged_releases_lock` — マージ完了時は `TaskState::Merged`) ``` #[test] - fn full_lifecycle_register_to_done() { - // 1. register(issue=1, title="test") - // 2. check_dependencies → ready - // 3. record_impact(risk=LOW) - // 4. assign_and_lock(agent="test", files=["src/test.rs"]) - // 5. record_branch("feature/issue-1-test") - // 6. record_pr(42) - // 7. record_merge("a1b2c3d4...40hex") - // 8. assert: state == Done, lock == None + fn full_lifecycle_register_to_merged_releases_lock() { + // register → impact → assign → branch → pr → merge(40hex) + // assert: Merged, lock == None } ``` -- [ ] GATE 拒否テスト: issue=0 → GateError -- [ ] GATE 拒否テスト: impact なしで assign → GateError -- [ ] GATE 拒否テスト: HIGH risk + 承認なし → GateError -- [ ] GATE 拒否テスト: ロック競合 → LockError -- [ ] GATE 拒否テスト: 不正 SHA → GateError -- [ ] escape hatch テスト: force_unlock + manual_complete +- [x] GATE 拒否テスト: issue=0 → GateError(既存テスト群でカバー) +- [x] GATE 拒否テスト: impact なしで assign → GateError(既存) +- [x] GATE 拒否テスト: HIGH risk + 承認なし → GateError(既存) +- [x] GATE 拒否テスト: ロック競合 → LockError(既存) +- [x] GATE 拒否テスト: 不正 SHA → GateError(既存) +- [x] escape hatch テスト: force_unlock + manual_complete ## 1.4 最終確認 -- [ ] `cargo test --all` → 全 GREEN -- [ ] `cargo clippy --all-targets --all-features -- -D warnings` → ゼロエラー -- [ ] `cargo build --release` → リリースビルド成功 -- [ ] `npx gitnexus analyze --force` → 再インデックス -- [ ] `git tag v1.0-dtp-complete` +- [x] `cargo test --all` → 全 GREEN +- [x] `cargo clippy --all-targets --all-features -- -D warnings` → ゼロエラー +- [x] `cargo build --release` → リリースビルド成功 +- [ ] `npx gitnexus analyze --force` → 再インデックス(必要時に手動) +- [ ] `git tag v1.0-dtp-complete`(リリース判断後) - [ ] `git push origin v1.0-dtp-complete` - [ ] `~/bin/announce "Polaris v1.0 完成。全 GATE 実装済み。テスト GREEN。"` diff --git a/crates/miyabi-cli/Cargo.toml b/crates/mergegate-cli/Cargo.toml similarity index 63% rename from crates/miyabi-cli/Cargo.toml rename to crates/mergegate-cli/Cargo.toml index 40bbebc..8a37459 100644 --- a/crates/miyabi-cli/Cargo.toml +++ b/crates/mergegate-cli/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "miyabi-cli" +name = "mergegate-cli" version.workspace = true edition.workspace = true rust-version.workspace = true @@ -7,16 +7,20 @@ authors.workspace = true license.workspace = true repository.workspace = true homepage.workspace = true -description = "Miyabi CLI - Main command-line interface" +description = "MergeGate CLI - Main command-line interface for deterministic task execution and agent-assisted development" [[bin]] name = "miyabi" path = "src/main.rs" +[[bin]] +name = "mergegate" +path = "src/bin/mergegate.rs" + [dependencies] # Workspace crates -miyabi-core = { path = "../miyabi-core" } -miyabi-tui = { path = "../miyabi-tui" } +miyabi-core = { package = "mergegate-core", path = "../mergegate-core" } +miyabi-tui = { package = "mergegate-tui", path = "../mergegate-tui" } # CLI clap = { workspace = true } @@ -32,6 +36,7 @@ tokio = { workspace = true } anyhow = { workspace = true } # Serialization +serde = { workspace = true } serde_json = { workspace = true } # Utilities diff --git a/crates/mergegate-cli/src/bin/mergegate.rs b/crates/mergegate-cli/src/bin/mergegate.rs new file mode 100644 index 0000000..2bbd8f8 --- /dev/null +++ b/crates/mergegate-cli/src/bin/mergegate.rs @@ -0,0 +1 @@ +include!("../main.rs"); diff --git a/crates/miyabi-cli/src/main.rs b/crates/mergegate-cli/src/main.rs similarity index 84% rename from crates/miyabi-cli/src/main.rs rename to crates/mergegate-cli/src/main.rs index 9c9c103..f177262 100644 --- a/crates/miyabi-cli/src/main.rs +++ b/crates/mergegate-cli/src/main.rs @@ -1,8 +1,9 @@ -//! Miyabi CLI - Main entry point +/// MergeGate CLI - Main entry point use chrono::Duration as ChronoDuration; -use clap::{Parser, Subcommand, ValueEnum}; +use clap::{CommandFactory, FromArgMatches, Parser, Subcommand, ValueEnum}; use miyabi_core::{FeatureFlagManager, RulesLoader}; +use serde::Serialize; use std::collections::HashMap; use std::fs; use std::io::{self, BufRead, BufReader, Write}; @@ -13,6 +14,7 @@ use tracing_subscriber::EnvFilter; /// Global feature flags manager static FEATURE_FLAGS: std::sync::OnceLock = std::sync::OnceLock::new(); +static INVOKED_BINARY_NAME: std::sync::OnceLock = std::sync::OnceLock::new(); /// Get the global feature flags manager pub fn feature_flags() -> &'static FeatureFlagManager { @@ -27,9 +29,40 @@ pub fn feature_flags() -> &'static FeatureFlagManager { }) } +fn detect_invoked_binary_name() -> String { + std::env::args() + .next() + .and_then(|path| { + std::path::Path::new(&path) + .file_name() + .map(|name| name.to_string_lossy().into_owned()) + }) + .filter(|name| !name.is_empty()) + .unwrap_or_else(|| "miyabi".to_string()) +} + +fn current_binary_name() -> &'static str { + INVOKED_BINARY_NAME + .get_or_init(detect_invoked_binary_name) + .as_str() +} + +fn gate_command(command: &str) -> String { + if command.is_empty() { + format!("{} gate", current_binary_name()) + } else { + format!("{} gate {}", current_binary_name(), command) + } +} + +fn agent_guide() -> String { + AGENT_GUIDE_TEMPLATE + .replace("{{GATE}}", &gate_command("")) + .replace("{{BINARY}}", current_binary_name()) +} + #[derive(Parser)] -#[command(name = "miyabi")] -#[command(author, version, about = "Miyabi - Autonomous AI Development Framework", long_about = None)] +#[command(author, version, about = "MergeGate - Deterministic task execution and merge workflow for AI-assisted development", long_about = None)] struct Cli { /// Model to use (overrides config) #[arg(short, long)] @@ -102,12 +135,15 @@ enum Commands { }, #[command( about = "Deterministic Task Protocol gate controls", - long_about = "Run miyabi gate init to set up a new project.\n\nDeterministic Task Protocol gate controls" + long_about = "Run the gate init command to set up a new project.\n\nDeterministic Task Protocol gate controls" )] Gate { /// Output format #[arg(long, value_enum, default_value_t = OutputFormat::Text)] format: OutputFormat, + /// Emit a hook-friendly JSON event envelope to stdout + #[arg(long)] + emit_event: bool, /// Path to the task ledger JSON file #[arg(long, default_value = "project_memory/tasks.json")] store_path: PathBuf, @@ -278,7 +314,7 @@ enum GateCommand { }, } -#[derive(Debug)] +#[derive(Debug, Serialize)] struct AssignPlanAttachment { attachment_type: String, source: String, @@ -286,7 +322,7 @@ struct AssignPlanAttachment { content: String, } -#[derive(Debug)] +#[derive(Debug, Serialize)] struct AssignExecutionPlan { task_title: String, risk_level: Option, @@ -296,7 +332,7 @@ struct AssignExecutionPlan { next_steps: Vec, } -#[derive(Debug)] +#[derive(Debug, Serialize)] struct InitStatus { initialized: bool, current_dir: String, @@ -409,7 +445,11 @@ async fn main() -> anyhow::Result<()> { .with_env_filter(EnvFilter::from_default_env()) .init(); - let cli = Cli::parse(); + let invoked_binary_name = detect_invoked_binary_name(); + let _ = INVOKED_BINARY_NAME.set(invoked_binary_name.clone()); + let clap_binary_name: &'static str = Box::leak(invoked_binary_name.into_boxed_str()); + let matches = Cli::command().name(clap_binary_name).get_matches(); + let cli = Cli::from_arg_matches(&matches).unwrap_or_else(|error| error.exit()); match cli.command { Some(Commands::Tui) | None => { @@ -874,10 +914,11 @@ async fn main() -> anyhow::Result<()> { } Some(Commands::Gate { format, + emit_event, store_path, command, }) => { - let code = handle_gate_command(&format, &store_path, command)?; + let code = handle_gate_command(&format, emit_event, &store_path, command)?; std::process::exit(code); } Some(Commands::Openclaw { command }) => { @@ -1245,6 +1286,7 @@ async fn main() -> anyhow::Result<()> { fn handle_gate_command( format: &OutputFormat, + emit_event: bool, store_path: &std::path::Path, command: GateCommand, ) -> anyhow::Result { @@ -1263,7 +1305,10 @@ fn handle_gate_command( let result = match command { GateCommand::Init => { - initialize_gate_project(format, store_path)?; + let status = initialize_gate_project(format, emit_event, store_path)?; + if emit_event { + emit_gate_event("gate_initialized", None, &status); + } Ok(()) } GateCommand::Register { @@ -1297,7 +1342,9 @@ fn handle_gate_command( &node, ) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("task_registered", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("registered: {} ({})", task.id, task.title); @@ -1312,20 +1359,22 @@ fn handle_gate_command( .status(task_id.as_deref()) .map(|status| match status { StatusReport::Task(task) => { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("task_status", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { - println!("{}: {:?} - {}", task.id, task.current_state, task.title); + print_gate_task_status(&task); } } StatusReport::Snapshot(snapshot) => { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("status_snapshot", None, &snapshot); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&snapshot).unwrap()); } else { - println!("tasks: {}", snapshot.tasks.len()); - for task in snapshot.tasks { - println!(" {} [{:?}] {}", task.id, task.current_state, task.title); - } + let dispatchable = protocol.dispatchable().ok(); + print_gate_snapshot_status(&snapshot, dispatchable.as_ref()); } } }) @@ -1340,15 +1389,14 @@ fn handle_gate_command( .and_then(|result| { let attachments = protocol.attach_context(&task_id, actor, &node)?; let plan = build_assign_execution_plan(&result.task, attachments); - if matches!(format, OutputFormat::Json) { - println!( - "{}", - serde_json::to_string_pretty(&serde_json::json!({ - "assignment": result, - "plan": assign_plan_to_json(&plan), - })) - .unwrap() - ); + let output = serde_json::json!({ + "assignment": result, + "plan": assign_plan_to_json(&plan), + }); + if emit_event { + emit_gate_event("lock_acquired", Some(&task_id), &output); + } else if matches!(format, OutputFormat::Json) { + println!("{}", serde_json::to_string_pretty(&output).unwrap()); } else { println!("assigned: {} -> {}@{}", result.task.id, agent, agent_node); print_assign_execution_plan(&result, &plan); @@ -1383,7 +1431,9 @@ fn handle_gate_command( &node, ) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("impact_recorded", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("impact recorded: {}", task.id); @@ -1392,7 +1442,9 @@ fn handle_gate_command( GateCommand::Branch { task_id, name } => protocol .record_branch(&task_id, &name, actor, &node) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("branch_created", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("branch recorded: {} -> {}", task.id, name); @@ -1402,7 +1454,9 @@ fn handle_gate_command( protocol .attach_context(&task_id, actor, &node) .map(|attachments| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("context_attached", Some(&task_id), &attachments); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&attachments).unwrap()); } else if attachments.is_empty() { println!("no context attachments: {}", task_id); @@ -1427,7 +1481,9 @@ fn handle_gate_command( protocol .refresh_context(&task_id, actor, &node) .map(|attachments| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("context_refreshed", Some(&task_id), &attachments); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&attachments).unwrap()); } else if attachments.is_empty() { println!("no context attachments: {}", task_id); @@ -1448,7 +1504,9 @@ fn handle_gate_command( GateCommand::Pr { task_id, number } => protocol .record_pr(&task_id, number, actor, &node) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("pr_created", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("pr recorded: {} -> #{}", task.id, number); @@ -1457,7 +1515,9 @@ fn handle_gate_command( GateCommand::Merge { task_id, sha } => protocol .record_merge(&task_id, &sha, actor, &node) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("task_completed", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("merge recorded: {} -> {}", task.id, sha); @@ -1466,7 +1526,9 @@ fn handle_gate_command( GateCommand::VerifyMerge { task_id, repo } => protocol .verify_merge(&task_id, &repo, actor, &node) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("task_completed", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { let sha = task @@ -1484,7 +1546,9 @@ fn handle_gate_command( } => protocol .force_unlock(&task_id, &reason, &operator) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("lock_released", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("lock released: {} by {}", task.id, operator); @@ -1497,14 +1561,18 @@ fn handle_gate_command( } => protocol .manual_complete(&task_id, &reason, &operator) .map(|task| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("task_completed", Some(&task.id), &task); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&task).unwrap()); } else { println!("task completed manually: {} by {}", task.id, operator); } }), GateCommand::Locks => protocol.locks().map(|locks| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("locks_reported", None, &locks); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&locks).unwrap()); } else if locks.is_empty() { println!("no active locks"); @@ -1515,7 +1583,9 @@ fn handle_gate_command( } }), GateCommand::Dag => protocol.dag().map(|report| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("dag_reported", None, &report); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&report).unwrap()); } else { for (index, level) in report.levels.iter().enumerate() { @@ -1524,10 +1594,16 @@ fn handle_gate_command( } }), GateCommand::Dispatchable => protocol.dispatchable().map(|report| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("dispatchable_reported", None, &report); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&report).unwrap()); } else if report.tasks.is_empty() { println!("no dispatchable tasks"); + println!("next:"); + println!(" {}", gate_command("status")); + println!(" {}", gate_command("dag")); + println!(" {}", gate_command("guide")); } else { for task in report.tasks { println!("{} [{}] {}", task.id, task.priority, task.title); @@ -1536,6 +1612,13 @@ fn handle_gate_command( }), GateCommand::Serve { port } => { serve_dashboard(store_path, port)?; + if emit_event { + emit_gate_event( + "dashboard_started", + None, + serde_json::json!({ "port": port }), + ); + } Ok(()) } GateCommand::Dream { @@ -1553,7 +1636,9 @@ fn handle_gate_command( protocol .dream(since, auto, &repo_root, actor, &node) .and_then(|report| { - if matches!(format, OutputFormat::Json) { + if emit_event { + emit_gate_event("dream_recorded", None, &report); + } else if matches!(format, OutputFormat::Json) { println!("{}", serde_json::to_string_pretty(&report).unwrap()); } else { print_dream_report(&report); @@ -1586,15 +1671,14 @@ fn handle_gate_command( Err(ProtocolError::input("heartbeat currently requires --all")) } else { protocol.heartbeat_all().map(|renewed| { - if matches!(format, OutputFormat::Json) { - println!( - "{}", - serde_json::to_string_pretty(&serde_json::json!({ - "renewed": renewed, - "count": renewed.len(), - })) - .unwrap() - ); + let output = serde_json::json!({ + "renewed": renewed, + "count": renewed.len(), + }); + if emit_event { + emit_gate_event("heartbeat_renewed", None, &output); + } else if matches!(format, OutputFormat::Json) { + println!("{}", serde_json::to_string_pretty(&output).unwrap()); } else { println!("renewed leases: {}", renewed.len()); for task_id in renewed { @@ -1605,7 +1689,7 @@ fn handle_gate_command( } } GateCommand::Guide => { - print!("{AGENT_GUIDE}"); + print!("{}", agent_guide()); Ok(()) } }; @@ -1613,19 +1697,19 @@ fn handle_gate_command( Ok(match result { Ok(()) => 0, Err(ProtocolError::GateRejected(message)) => { - emit_gate_error(format, "gate_rejected", &message); + emit_gate_error(format, emit_event, "gate_rejected", &message); 1 } Err(ProtocolError::DependencyBlocked(message)) => { - emit_gate_error(format, "gate_rejected", &message); + emit_gate_error(format, emit_event, "gate_rejected", &message); 1 } Err(ProtocolError::Input(message)) => { - emit_gate_error(format, "input_error", &message); + emit_gate_error(format, emit_event, "input_error", &message); 2 } Err(ProtocolError::Internal(error)) => { - emit_gate_error(format, "internal_error", &error.to_string()); + emit_gate_error(format, emit_event, "internal_error", &error.to_string()); 1 } }) @@ -1678,17 +1762,23 @@ fn assign_next_steps( miyabi_core::store::CompletionMode::GithubPr => vec![ "1. Create branch".to_string(), "2. Make changes".to_string(), - format!("3. miyabi gate branch {task_id} ..."), - format!("4. miyabi gate pr {task_id} ..."), - format!("5. miyabi gate merge {task_id} ..."), + format!("3. {} {task_id} ...", gate_command("branch")), + format!("4. {} {task_id} ...", gate_command("pr")), + format!("5. {} {task_id} ...", gate_command("merge")), ], miyabi_core::store::CompletionMode::Manual => vec![ "1. Complete the work".to_string(), - format!("2. miyabi gate manual-complete {task_id} --reason ... --operator ..."), + format!( + "2. {} {task_id} --reason ... --operator ...", + gate_command("manual-complete") + ), ], miyabi_core::store::CompletionMode::ExternalOp => vec![ "1. Complete external operation".to_string(), - format!("2. miyabi gate manual-complete {task_id} --reason ... --operator ..."), + format!( + "2. {} {task_id} --reason ... --operator ...", + gate_command("manual-complete") + ), ], } } @@ -1737,10 +1827,145 @@ fn print_assign_execution_plan( } } +fn print_gate_task_status(task: &miyabi_core::store::ExecutionTask) { + println!("task: {}", task.id); + println!("title: {}", task.title); + println!("state: {:?}", task.current_state); + + if task.issue_number > 0 { + println!("issue: #{}", task.issue_number); + } + println!("completion mode: {:?}", task.completion_mode); + + if let Some(impact) = &task.impact { + println!( + "impact: {:?} (symbols: {})", + impact.risk_level, impact.affected_symbols + ); + } else { + println!("impact: not recorded"); + } + + match task.current_state { + miyabi_core::store::TaskState::Pending | miyabi_core::store::TaskState::Draft => { + if task.impact.is_none() { + println!("next:"); + println!(" {} {} --risk low --symbols 0", gate_command("impact"), task.id); + println!( + " {} {} --agent --node --files \"path/to/file\"", + gate_command("assign"), + task.id + ); + } else { + println!("next:"); + println!( + " {} {} --agent --node --files \"path/to/file\"", + gate_command("assign"), + task.id + ); + } + } + miyabi_core::store::TaskState::Implementing => { + println!("next:"); + println!(" {} {} ", gate_command("branch"), task.id); + println!(" {} {}", gate_command("attach"), task.id); + } + miyabi_core::store::TaskState::Reviewing => { + println!("next:"); + println!(" {} {} ", gate_command("pr"), task.id); + println!(" {} {} <40-char-sha>", gate_command("merge"), task.id); + } + miyabi_core::store::TaskState::Merged | miyabi_core::store::TaskState::Done => { + println!("next:"); + println!(" {}", gate_command("dispatchable")); + } + _ => {} + } +} + +fn print_gate_snapshot_status( + snapshot: &miyabi_core::store::TasksSnapshot, + dispatchable: Option<&miyabi_core::protocol::DispatchableReport>, +) { + if snapshot.tasks.is_empty() { + println!("tasks: 0"); + println!("status: ready to initialize or register your first task"); + println!("this is not an error. it means the ledger is empty."); + println!("next:"); + println!(" {}", gate_command("init")); + println!(" {} --issue --title \"Your task\"", gate_command("register")); + println!(" {}", gate_command("guide")); + return; + } + + let total = snapshot.tasks.len(); + let completed = snapshot + .tasks + .iter() + .filter(|task| { + matches!( + task.current_state, + miyabi_core::store::TaskState::Done | miyabi_core::store::TaskState::Merged + ) + }) + .count(); + let active = snapshot + .tasks + .iter() + .filter(|task| { + matches!( + task.current_state, + miyabi_core::store::TaskState::Implementing + | miyabi_core::store::TaskState::Reviewing + | miyabi_core::store::TaskState::Analyzing + ) + }) + .count(); + let waiting = total.saturating_sub(completed + active); + let dispatchable_count = dispatchable.map(|report| report.count).unwrap_or(0); + + println!( + "tasks: {} (dispatchable: {}, locks: {})", + total, + dispatchable_count, + snapshot.file_locks.len() + ); + println!( + "summary: {} completed, {} active, {} waiting", + completed, active, waiting + ); + + if let Some(report) = dispatchable { + if !report.tasks.is_empty() { + println!("next tasks:"); + for task in report.tasks.iter().take(3) { + println!(" {} [{}] {}", task.id, task.priority, task.title); + } + println!("next:"); + println!(" {} ", gate_command("status")); + println!( + " {} --agent --node --files \"path/to/file\"", + gate_command("assign") + ); + } else { + println!("next:"); + println!(" {}", gate_command("dag")); + println!(" {}", gate_command("locks")); + println!(" {}", gate_command("guide")); + } + } + + println!("all tasks:"); + for task in &snapshot.tasks { + println!(" {} [{:?}] {}", task.id, task.current_state, task.title); + } +} + fn initialize_gate_project( format: &OutputFormat, + emit_event: bool, store_path: &std::path::Path, -) -> anyhow::Result<()> { +) -> anyhow::Result { let current_dir = std::env::current_dir()?; let created_path = store_path.display().to_string(); let initialized = if store_path.exists() { @@ -1773,6 +1998,10 @@ fn initialize_gate_project( github_project_detected, }; + if emit_event { + return Ok(status); + } + if matches!(format, OutputFormat::Json) { println!( "{}", @@ -1785,15 +2014,15 @@ fn initialize_gate_project( "gitignore_updated": status.gitignore_updated, "github_project_detected": status.github_project_detected, "next_steps": [ - "miyabi gate register --issue --title ...", - "miyabi gate status", - "miyabi gate --help" + gate_command("status"), + gate_command("guide"), + format!("{} --issue --title ...", gate_command("register")) ], }))? ); } else { if status.initialized { - println!("Polaris initialized in {}", status.current_dir); + println!("MergeGate initialized in {}", status.current_dir); println!("Created: {}", status.created_path); } else { println!("Already initialized"); @@ -1805,13 +2034,13 @@ fn initialize_gate_project( println!("⚠️ No GitHub remote. Run: gh repo create --private"); } println!("Next steps:"); - println!(" miyabi gate register --issue --title ..."); - println!(" miyabi gate status"); - println!(" miyabi gate --help"); + println!(" {}", gate_command("status")); + println!(" {}", gate_command("guide")); + println!(" {} --issue --title ...", gate_command("register")); print_init_checklist(&status); } - Ok(()) + Ok(status) } fn print_init_checklist(status: &InitStatus) { @@ -2011,8 +2240,24 @@ fn print_dream_report(report: &miyabi_core::DreamReport) { } } -fn emit_gate_error(format: &OutputFormat, kind: &str, message: &str) { - if matches!(format, OutputFormat::Json) { +fn emit_gate_event(event: &str, task_id: Option<&str>, payload: impl serde::Serialize) { + println!( + "{}", + serde_json::to_string_pretty(&serde_json::json!({ + "event": event, + "task_id": task_id, + "source": "miyabi-gate", + "ts": chrono::Utc::now(), + "payload": payload, + })) + .unwrap() + ); +} + +fn emit_gate_error(format: &OutputFormat, emit_event: bool, kind: &str, message: &str) { + if emit_event { + emit_gate_event(kind, None, serde_json::json!({ "message": message })); + } else if matches!(format, OutputFormat::Json) { println!( "{}", serde_json::to_string_pretty(&serde_json::json!({ @@ -2026,12 +2271,12 @@ fn emit_gate_error(format: &OutputFormat, kind: &str, message: &str) { } } -const AGENT_GUIDE: &str = r#" -# Polaris (miyabi-gate) — Agent Guide +const AGENT_GUIDE_TEMPLATE: &str = r#" +# MergeGate ({{GATE}}) — Agent Guide ## What is this? -Polaris is a deterministic task execution protocol. It enforces a strict +MergeGate is a deterministic task execution protocol. It enforces a strict workflow so that any agent on any machine produces the same verifiable result. Tasks are tracked in project_memory/tasks.json, not in conversation memory. @@ -2047,53 +2292,53 @@ Tasks are tracked in project_memory/tasks.json, not in conversation memory. ``` Step 1: Register - miyabi-gate gate register --issue --title "Task description" + {{GATE}} register --issue --title "Task description" Step 2: Impact analysis - miyabi-gate gate impact --risk --symbols + {{GATE}} impact --risk --symbols # Add --approve if risk is high or critical Step 3: Assign (acquires file locks) - miyabi-gate gate assign --agent --node --files "file1.rs,file2.rs" + {{GATE}} assign --agent --node --files "file1.rs,file2.rs" # This prints an execution plan and context attachments. Read them. Step 4: Work # Edit ONLY the locked files. Pre-commit hook blocks unlocked files. Step 5: Branch - miyabi-gate gate branch feature/issue-- + {{GATE}} branch feature/issue-- Step 6: PR - miyabi-gate gate pr + {{GATE}} pr Step 7: Merge - miyabi-gate gate merge + {{GATE}} merge ``` ## For document-only tasks (no PR needed) ``` -miyabi-gate gate register --issue --title "Doc task" --completion-mode manual -miyabi-gate gate impact --risk low --symbols 0 -miyabi-gate gate assign --agent --node --files "docs/file.md" +{{GATE}} register --issue --title "Doc task" --completion-mode manual +{{GATE}} impact --risk low --symbols 0 +{{GATE}} assign --agent --node --files "docs/file.md" # ... do the work ... -miyabi-gate gate manual-complete --reason "reason" --operator +{{GATE}} manual-complete --reason "reason" --operator ``` ## Checking state ``` -miyabi-gate gate status # All tasks -miyabi-gate gate status # One task -miyabi-gate gate locks # Active file locks -miyabi-gate gate dag # Dependency graph -miyabi-gate gate dispatchable # Tasks ready to work on -miyabi-gate gate attach # View context attachments +{{GATE}} status # All tasks +{{GATE}} status # One task +{{GATE}} locks # Active file locks +{{GATE}} dag # Dependency graph +{{GATE}} dispatchable # Tasks ready to work on +{{GATE}} attach # View context attachments ``` ## Context attachments (auto-injected on assign) -When you run `assign`, Polaris automatically attaches: +When you run `assign`, MergeGate automatically attaches: - GitHub Issue body - Impact analysis result - Obsidian vault notes matching the task title (if OBSIDIAN_VAULT_PATH is set) @@ -2101,15 +2346,17 @@ When you run `assign`, Polaris automatically attaches: - First 30 lines of each locked file - First 30 lines of depth-1 impact files (direct callers) -Use `miyabi-gate gate attach --format json` to get this as JSON +Use `{{GATE}} attach ` to inspect the attachments. +Use `{{GATE}} --format json status` or `{{GATE}} --format json locks` +when you need machine-readable output from supported commands. for programmatic injection into prompts. ## Emergency commands ``` -miyabi-gate gate force-unlock --reason "why" --operator -miyabi-gate gate manual-complete --reason "why" --operator -miyabi-gate gate heartbeat --all # Renew all lease heartbeats +{{GATE}} force-unlock --reason "why" --operator +{{GATE}} manual-complete --reason "why" --operator +{{GATE}} heartbeat --all # Renew all lease heartbeats ``` ## Exit codes @@ -2128,23 +2375,23 @@ cargo clippy --all-targets --all-features -- -D warnings ## Self-improvement ``` -miyabi-gate gate dream # Extract learnings from event log -miyabi-gate gate dream --auto # Also write High learnings to docs/ and update SKILL.md -miyabi-gate gate serve # Web dashboard at localhost:4848 +{{GATE}} dream # Extract learnings from event log +{{GATE}} dream --auto # Also write High learnings to docs/ and update SKILL.md +{{GATE}} serve # Web dashboard at localhost:4848 ``` ## Command Reference ### init Initialize project memory in the current repo. - miyabi-gate gate init + {{GATE}} init ### register Register a new task. Creates an entry in tasks.json. - miyabi-gate gate register --issue --title "Title" - miyabi-gate gate register --issue --title "Title" --completion-mode manual - miyabi-gate gate register --issue --title "Title" --dependencies dep-1,dep-2 - miyabi-gate gate register --issue --title "Title" --no-bus + {{GATE}} register --issue --title "Title" + {{GATE}} register --issue --title "Title" --completion-mode manual + {{GATE}} register --issue --title "Title" --dependencies dep-1,dep-2 + {{GATE}} register --issue --title "Title" --no-bus Options: --issue GitHub issue number (required, 0 = auto-create) --title Task title (required) @@ -2157,9 +2404,9 @@ miyabi-gate gate serve # Web dashboard at localhost:4848 ### impact Record impact analysis for a task. - miyabi-gate gate impact --risk low --symbols 3 - miyabi-gate gate impact --risk high --symbols 12 --approve - miyabi-gate gate impact --risk medium --symbols 5 --depth1 "src/a.rs,src/b.rs" + {{GATE}} impact --risk low --symbols 3 + {{GATE}} impact --risk high --symbols 12 --approve + {{GATE}} impact --risk medium --symbols 5 --depth1 "src/a.rs,src/b.rs" Options: --risk low | medium | high | critical --symbols Number of affected symbols @@ -2170,7 +2417,7 @@ miyabi-gate gate serve # Web dashboard at localhost:4848 ### assign Acquire file locks and start implementation. - miyabi-gate gate assign --agent codex --node macbook --files "src/main.rs,src/lib.rs" + {{GATE}} assign --agent codex --node macbook --files "src/main.rs,src/lib.rs" Options: --agent Agent name (required) --node Machine name (required) @@ -2178,63 +2425,61 @@ miyabi-gate gate serve # Web dashboard at localhost:4848 ### branch Record branch creation. - miyabi-gate gate branch feature/issue-45-auth + {{GATE}} branch feature/issue-45-auth ### pr Record PR creation. - miyabi-gate gate pr 88 + {{GATE}} pr 88 ### merge Record merge verification. Releases locks and unblocks dependents. - miyabi-gate gate merge <40-char-SHA> + {{GATE}} merge <40-char-SHA> ### status Show task status. - miyabi-gate gate status # All tasks - miyabi-gate gate status # One task - miyabi-gate gate status --format json + {{GATE}} status # All tasks + {{GATE}} status # One task + {{GATE}} --format json status ### locks List active file locks. - miyabi-gate gate locks - miyabi-gate gate locks --format json + {{GATE}} locks + {{GATE}} --format json locks ### dag Show DAG dependency levels. - miyabi-gate gate dag + {{GATE}} dag ### dispatchable Show tasks ready to be worked on (dependencies resolved, no lock). - miyabi-gate gate dispatchable - miyabi-gate gate dispatchable --format json + {{GATE}} dispatchable ### attach View context attachments for a task. - miyabi-gate gate attach - miyabi-gate gate attach --format json + {{GATE}} attach ### refresh Force-refresh context attachments (clears cache). - miyabi-gate gate refresh + {{GATE}} refresh ### verify-merge Verify merge state via GitHub API. - miyabi-gate gate verify-merge --repo owner/repo + {{GATE}} verify-merge --repo owner/repo ### force-unlock Emergency: release a lock without completing the task. - miyabi-gate gate force-unlock --reason "why" --operator "name" + {{GATE}} force-unlock --reason "why" --operator "name" ### manual-complete Complete a task without merge verification (for doc/ops tasks). - miyabi-gate gate manual-complete --reason "why" --operator "name" + {{GATE}} manual-complete --reason "why" --operator "name" ### dream Analyze event logs and extract learnings. - miyabi-gate gate dream - miyabi-gate gate dream --since 24h - miyabi-gate gate dream --auto - miyabi-gate gate dream --auto --vault-path /path/to/obsidian + {{GATE}} dream + {{GATE}} dream --since 24h + {{GATE}} dream --auto + {{GATE}} dream --auto --vault-path /path/to/obsidian Options: --since Filter events (e.g. 24h, 7d, 30m) --auto Write High learnings to docs/ + update SKILL.md @@ -2242,18 +2487,18 @@ miyabi-gate gate serve # Web dashboard at localhost:4848 ### heartbeat Renew lock lease heartbeats. - miyabi-gate gate heartbeat --all + {{GATE}} heartbeat --all ### serve Start web dashboard. - miyabi-gate gate serve - miyabi-gate gate serve --port 8080 + {{GATE}} serve + {{GATE}} serve --port 8080 Options: --port Port number (default: 4848) ### guide Print this guide. - miyabi-gate gate guide + {{GATE}} guide ### Global options (apply to all gate commands) --format Output format (default: text) @@ -2265,7 +2510,7 @@ const POLARIS_DASHBOARD_HTML: &str = r##" - Polaris Dashboard + MergeGate Dashboard