mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-23 00:01:24 -07:00
Compare commits
1735 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| eb9e7eb10e | |||
| 7630190038 | |||
| 35419b51f2 | |||
| 7a687ce858 | |||
| 1182168bd9 | |||
| e34f0b4a98 | |||
| 4920ad2694 | |||
| 52fadba9f2 | |||
| ec7413456e | |||
| 451e0b49cb | |||
| 155d9aafe2 | |||
| 05c0a8eac3 | |||
| 53994b2ff0 | |||
| 0bebc664c1 | |||
| 1b6b6d40d5 | |||
| a90bcf749d | |||
| e2901f3f7e | |||
| 4b4bdd10b6 | |||
| 0a6f2e089f | |||
| d8a8b434f6 | |||
| 49769152d6 | |||
| 4cfbe4c716 | |||
| a4eb04b7d4 | |||
| d079b7a216 | |||
| d87a3acdef | |||
| 08c32f725e | |||
| 203f5209ba | |||
| 9d9e3d1555 | |||
| 20580d754d | |||
| 41e01c2321 | |||
| 6900253cb8 | |||
| 86dbf2013e | |||
| a76946189a | |||
| e03042657b | |||
| 1998a713e2 | |||
| ee8d425603 | |||
| 272570cc18 | |||
| 1681ae1842 | |||
| 608da23393 | |||
| f2d3b76389 | |||
| d8d4d87e29 | |||
| 93224e1813 | |||
| 9722ec9b6a | |||
| 063f0d0dcd | |||
| 42fd647373 | |||
| 59da61602f | |||
| 5241174827 | |||
| 013a4e02ff | |||
| be37c26c88 | |||
| fcd860e1b0 | |||
| ce35d84113 | |||
| cfdc4cfca8 | |||
| a15978593a | |||
| 4bb817de22 | |||
| c6cf3a4234 | |||
| 420a419f5e | |||
| 8dde66c0dd | |||
| f367b959cd | |||
| 655ab21d8b | |||
| 1e8f87fbdf | |||
| 8a627d6c9a | |||
| b0d2ec55cc | |||
| e77d7b2e1e | |||
| 48fdb9872f | |||
| c8670f8696 | |||
| 5ba6e243b5 | |||
| 9e13a43225 | |||
| 6740886e22 | |||
| f7f38e2b9e | |||
| 2b6bfe4097 | |||
| f909c9ef90 | |||
| fa3981990c | |||
| d545a3b614 | |||
| a8631a109e | |||
| b0c9db7b33 | |||
| 448fd3ca66 | |||
| 53f54436c9 | |||
| 409f9c825b | |||
| 222b739501 | |||
| d0bbc7fa59 | |||
| 4848f42486 | |||
| 467e869326 | |||
| 4f324b548e | |||
| a81500a929 | |||
| 5bdfe1a1fa | |||
| ae198029bc | |||
| 4b2e9f7954 | |||
| 42c26d1e1b | |||
| e58fca68ce | |||
| b3527dc9e4 | |||
| a2dab146b9 | |||
| fb7640886b | |||
| 5ed275ce39 | |||
| 6021e4c3ba | |||
| 7e6817da5b | |||
| b14cf1dc30 | |||
| 94d5ae541e | |||
| 41369f67eb | |||
| c8c7b57a79 | |||
| b3eecc3a50 | |||
| 16b35910e2 | |||
| 1212161d1d | |||
| 4db00b8f2a | |||
| 09a7301d80 | |||
| a3234fb534 | |||
| 764016bca7 | |||
| dfb7dc7069 | |||
| 3b55581aaf | |||
| f6c2d61906 | |||
| c04af6c3e9 | |||
| a1cbe85da3 | |||
| c8d7c09ca4 | |||
| bb6c574144 | |||
| 66e7b479ae | |||
| 8030404b08 | |||
| 933bc5774f | |||
| 4afd3741df | |||
| 428e602882 | |||
| 04f65d7b4e | |||
| eda47f587c | |||
| 8dbaa2bcea | |||
| 778de55fd8 | |||
| c7c409c68f | |||
| d66ec38f82 | |||
| 63c918fe7d | |||
| b2e866585d | |||
| b518125c46 | |||
| e8be252b75 | |||
| 92e31e3c4a | |||
| e931ebe581 | |||
| 91fcca3b1c | |||
| aa52462550 | |||
| 0f7a136612 | |||
| 8faa23cea6 | |||
| a6dca02344 | |||
| 304caa4e43 | |||
| 7bbfaabffa | |||
| 6adae9f775 | |||
| b81fe68325 | |||
| 2fc61685a3 | |||
| c572b9e9ac | |||
| 8d3e93cdb0 | |||
| 548641c952 | |||
| 6ef2a92233 | |||
| e9c9dd1d67 | |||
| ca6786a28b | |||
| 2e8c6cfdbb | |||
| 95d9a33996 | |||
| b8cc414d5b | |||
| d7bff8610f | |||
| e049d5e4e8 | |||
| 8437ce940a | |||
| 7d92242011 | |||
| d65eab01d2 | |||
| 2306e60be4 | |||
| 3b678a4da0 | |||
| 64cde8d439 | |||
| 0167392f22 | |||
| 15891721ad | |||
| 8a2e0fac0d | |||
| 8656ce8a27 | |||
| ed7bcf9968 | |||
| 465ec9759d | |||
| 950244f6b0 | |||
| 64c75cb767 | |||
| 7b7f2fc69e | |||
| d315f4d3da | |||
| 9703fe73cf | |||
| 93b57b82c1 | |||
| 0e955da171 | |||
| 39b3f20a22 | |||
| b9762a3ee1 | |||
| 33e3ed0f6c | |||
| 446058cb1c | |||
| d130d99ff0 | |||
| 72dae7e0ee | |||
| 6f7d798189 | |||
| 3090008b1c | |||
| 461c277bf2 | |||
| b54e688c75 | |||
| b08b0d715b | |||
| c87d1aed4c | |||
| 356f76e545 | |||
| 1fb55dcb2e | |||
| d74bf9ef2f | |||
| e04a5f0cb0 | |||
| ea7393f7fd | |||
| c7d17dda49 | |||
| 9d187e041c | |||
| 14f0cb4538 | |||
| ca48661423 | |||
| 041463d112 | |||
| c1401682ed | |||
| 8bc3cfe29a | |||
| 77e226c55f | |||
| b9f8858bfb | |||
| f7b97ef55e | |||
| 88f1ec8d0a | |||
| aa480e5fbb | |||
| 41e627a7ee | |||
| e1e3efc9d0 | |||
| 18dd399cb5 | |||
| f1ca7fa40a | |||
| 6166d7f6ec | |||
| ffb80c2426 | |||
| 4ab1b9895a | |||
| 84710b1953 | |||
| a7f758eb3a | |||
| 26505b580c | |||
| e51f3e11f1 | |||
| d75792703a | |||
| e5f7a9c424 | |||
| 9062a943e7 | |||
| 7e02ef697d | |||
| 41a8809280 | |||
| 01d2d43737 | |||
| 16da6918cb | |||
| fbfad06307 | |||
| f8138262fa | |||
| 1a4ae41397 | |||
| 75bc41fc20 | |||
| ced5110dab | |||
| 76d020511f | |||
| 02cf264ee1 | |||
| eb3f3cfdb8 | |||
| 660368f249 | |||
| 97ad3d97cb | |||
| cf021ccae4 | |||
| eb75f59a96 | |||
| 030847a80a | |||
| 722c4933dc | |||
| 3e2f4eb8ba | |||
| aca6bf6aa0 | |||
| d48c934357 | |||
| 41cc6cf105 | |||
| dd04b46e86 | |||
| 1bd4f9d8b6 | |||
| 1aa35c8796 | |||
| 51d3f44d51 | |||
| d4b418ba01 | |||
| bd77515fd9 | |||
| 1c77bac146 | |||
| 0be8b5b1ed | |||
| d5996fea99 | |||
| a1dd19738e | |||
| 17b3eb730a | |||
| 4c961df313 | |||
| 19bdd95eab | |||
| 143bb63483 | |||
| c64b5ec4a3 | |||
| 57012ae5b3 | |||
| db99beda36 | |||
| 97b31c4eef | |||
| d1eb87c81f | |||
| 8f9bb6bccc | |||
| 416d243027 | |||
| 5fe5d1da46 | |||
| 2d683bb6f8 | |||
| 7956eb239e | |||
| a26463b056 | |||
| 982eee63b6 | |||
| b54215f0a5 | |||
| 521dc7f26c | |||
| da85e3f8f2 | |||
| 59a18e710d | |||
| 5f027cb63a | |||
| 4086abf375 | |||
| 8f5bf33eac | |||
| 7eeb7bd74c | |||
| 96b9be3ec4 | |||
| 1e31427da8 | |||
| 7feb2f8f42 | |||
| 2122604b32 | |||
| d4b4aede2f | |||
| 4b5c044272 | |||
| 2fe45834dd | |||
| 56092bd782 | |||
| c31f05356a | |||
| 61dbab03e0 | |||
| 86b5995f12 | |||
| d2849fda8a | |||
| a61fb058b7 | |||
| 7edd803034 | |||
| 0c54136244 | |||
| cce4574143 | |||
| 9172e28315 | |||
| 6f4b2ad0b9 | |||
| 9a3ff6510f | |||
| 4c67eef0f2 | |||
| 7d4f97de7a | |||
| cbd2eee9c4 | |||
| 384fb6a465 | |||
| 2cb33b2f76 | |||
| 8f0324d868 | |||
| fd7b6bf40a | |||
| ed8bad8c26 | |||
| 6d1e27633a | |||
| 2da911e4a0 | |||
| e5d183031a | |||
| b13c6b57ae | |||
| 3c92666ec2 | |||
| dc6dda5c37 | |||
| 3997c7ff80 | |||
| b4b49e7029 | |||
| 615b218ff7 | |||
| f3625aab13 | |||
| d3563e2f0e | |||
| dd84c2fb83 | |||
| bdb349e7f6 | |||
| 12c7c9cc42 | |||
| 90be9c3587 | |||
| eec5d5ebf8 | |||
| d3c206c677 | |||
| f0a039f7c0 | |||
| 30f5c4af4a | |||
| e78c3fe4f0 | |||
| c0ccb22460 | |||
| 764b195977 | |||
| 0f3555a4d2 | |||
| 006de1dd31 | |||
| 18fef0db31 | |||
| 958284dc24 | |||
| 788bb04f5c | |||
| 8a0190ca3b | |||
| c29a8c12b3 | |||
| 05049b5abf | |||
| 3ebe4e6a8f | |||
| 0eb84f5133 | |||
| de1233b8ca | |||
| ec11b8afbf | |||
| ec79fe1ab2 | |||
| 90eb1e0281 | |||
| 15c9f88da6 | |||
| 4e6fee7fcd | |||
| 1e08b150f7 | |||
| fb22f5b8ee | |||
| 848e8485cd | |||
| 07e597de40 | |||
| 10ae84869a | |||
| dcd2449b1a | |||
| a3d214f8d7 | |||
| 37be162435 | |||
| 5566292cc8 | |||
| 65e2144b3d | |||
| a26d195404 | |||
| 21388a0a40 | |||
| acecd80afa | |||
| 69fc75c0b2 | |||
| 9cdb267ba5 | |||
| 56b050422d | |||
| 546baf9934 | |||
| e6344a8c24 | |||
| 873d10df42 | |||
| 02a36afc38 | |||
| 563d81e08e | |||
| 5f28614760 | |||
| b6b0727e28 | |||
| e9a601c1fe | |||
| 308aa70718 | |||
| 6be034392f | |||
| 3b1dbcd42d | |||
| 2ac9fe08f7 | |||
| 24c722454b | |||
| 0a216b28f3 | |||
| 9c48cd849b | |||
| 0843d9af58 | |||
| b0d5c4c058 | |||
| d18c96d6a1 | |||
| dced409ac4 | |||
| d6a2f1d670 | |||
| 58fd00a3df | |||
| b923604602 | |||
| 8643d60b88 | |||
| 8feeffb29b | |||
| e64146914a | |||
| 6084708cc2 | |||
| 8ed0f8981f | |||
| 41a1a3eed1 | |||
| 10ba348a3a | |||
| db67bb106a | |||
| b828b4754b | |||
| 9383b54d50 | |||
| 181da07dd9 | |||
| 419464a8c2 | |||
| 2e229d3bb6 | |||
| 3c92bdb1ad | |||
| e0f159085e | |||
| 402148dbc4 | |||
| 70696e364b | |||
| 1e10492e55 | |||
| 9e6914d641 | |||
| 0c4fb6afd2 | |||
| edab97997a | |||
| 2515b89e2b | |||
| 60f0f19d76 | |||
| 322232e514 | |||
| 54466a3ea8 | |||
| 7da060c149 | |||
| 088f48d60f | |||
| 2b426c1d91 | |||
| bc168bbae4 | |||
| 124a6da743 | |||
| 739c02bd6d | |||
| 5d13145995 | |||
| c28ff3d5a5 | |||
| 6ddd5abd7b | |||
| bf6d0485ce | |||
| 3d486ec1e9 | |||
| a6d1245a54 | |||
| de7e1937f6 | |||
| b465e12747 | |||
| 948401a450 | |||
| bb8f181ef1 | |||
| bf90b59935 | |||
| 18698d6929 | |||
| 80c4225286 | |||
| d02f3f6809 | |||
| da85aed5aa | |||
| 7f2d33458a | |||
| 3e9a0a7628 | |||
| 26c115a4fb | |||
| db643e9166 | |||
| ba100642e3 | |||
| cc52839f19 | |||
| e79b149985 | |||
| 5e21c8c03c | |||
| 5ea5107d05 | |||
| 2995af6a21 | |||
| 0c3eb82671 | |||
| 79f664d593 | |||
| bb0c0d8ee3 | |||
| d236df5b21 | |||
| 13944b9bb1 | |||
| ec665ef405 | |||
| 217e2b0eb4 | |||
| d030a1f62f | |||
| fc2a5a42ce | |||
| 57c7b9ccce | |||
| 942bcfc61e | |||
| fcc3b2b5ec | |||
| d9f94103cd | |||
| ad60cbfc2c | |||
| 126c32aca4 | |||
| 977248e095 | |||
| a47af8e261 | |||
| 5b56920f0a | |||
| 5f298c17d7 | |||
| d2a1a45646 | |||
| 6dea66f1f5 | |||
| 20164ebcda | |||
| edbe5480ca | |||
| e84c4bfb81 | |||
| 3b2a4ba27a | |||
| 12cbe320e4 | |||
| 1e734d7e60 | |||
| 299cc9bebf | |||
| a02abcf578 | |||
| bb33e281c0 | |||
| 86134e9970 | |||
| 54de67536d | |||
| d818fb1d91 | |||
| 927102ea1b | |||
| 2d3db97067 | |||
| 24fca1b745 | |||
| 8c83e1ea9a | |||
| 91b15fc9dc | |||
| d4506e0fc0 | |||
| 648041c62a | |||
| 22e6af414a | |||
| c8b688655c | |||
| 68ebf5d655 | |||
| 1954f45c19 | |||
| ee6556cbd2 | |||
| d2a6b30398 | |||
| d90356e8a3 | |||
| aced401071 | |||
| 2800187355 | |||
| 3f5f030d01 | |||
| 364b12e2fa | |||
| 6877dd0de7 | |||
| 6be32e9383 | |||
| f919783173 | |||
| 674494e80b | |||
| 1f813f6a06 | |||
| afd4829f10 | |||
| 03be923e05 | |||
| bc542cbca8 | |||
| 560550f5df | |||
| 720b31cb8b | |||
| d35a1fdec7 | |||
| 8f43d4851d | |||
| 171103aedc | |||
| ec9a8c7a72 | |||
| 91c46311c8 | |||
| 89570aef06 | |||
| 17b5b40765 | |||
| cdc74a742a | |||
| 4322137cee | |||
| 8651c53923 | |||
| b2a821c81a | |||
| 3888a592cf | |||
| 84c07c8fa1 | |||
| 389cadb06a | |||
| 6e51bbc215 | |||
| 8f4f8baa81 | |||
| 7a72037572 | |||
| 025e450ac2 | |||
| 3cf44acc08 | |||
| bdd15e8911 | |||
| 04cbae5b5f | |||
| 2c4ec31ed1 | |||
| 996cbcb680 | |||
| dd3fd73ffe | |||
| 738354ff65 | |||
| 616d6f6667 | |||
| 8341256d1e | |||
| 934b309b4c | |||
| d284fa66c0 | |||
| bedce8aacf | |||
| 7db5abdecf | |||
| 290ede93e2 | |||
| f4f2bcbd98 | |||
| 1c5213788c | |||
| 0d5424fb79 | |||
| ae8694b303 | |||
| 5f60281d25 | |||
| 0c7ae22f5d | |||
| 2d1c1ac567 | |||
| 205d0f456e | |||
| 8b0a8f47c1 | |||
| 84f521b1c6 | |||
| 1040c246f5 | |||
| b27cf0b0a8 | |||
| 0a2971f9d3 | |||
| 48e8c12476 | |||
| 6f3b56c5b6 | |||
| b745d46395 | |||
| 46bb07e4b7 | |||
| d5e5f58737 | |||
| 9bc5a4d64f | |||
| 7b811a38a6 | |||
| e0a2227faf | |||
| 470f3b057f | |||
| 518e73ac9f | |||
| 3da4fd5f7d | |||
| 1e6243045e | |||
| a28be4a4e0 | |||
| 153d01a01e | |||
| eb3312e7ba | |||
| 939cb67621 | |||
| 00705b14bd | |||
| 8d4082ef2e | |||
| f588219bb9 | |||
| b8c038f41f | |||
| 08067acc71 | |||
| 1c12da1fad | |||
| 7a6d3067c6 | |||
| 54c62d580c | |||
| 0857345945 | |||
| b9b3b8050d | |||
| 92e95ed806 | |||
| 035bea3699 | |||
| bdbbe9232d | |||
| 145fb246a6 | |||
| 533a3fb312 | |||
| 344f2f26e7 | |||
| ed10edbf0d | |||
| 828afe113e | |||
| 2d935b3798 | |||
| aa544c40de | |||
| c78f2574a2 | |||
| 2b1a791a0b | |||
| 752a521423 | |||
| 71b0e7ab0d | |||
| f7f047936b | |||
| 2a3c0eddb9 | |||
| 1689e9b671 | |||
| 98d7238ed6 | |||
| d24e5cf353 | |||
| 70a48a3dd6 | |||
| 5fa6d87c25 | |||
| 5729642556 | |||
| f4babf172b | |||
| 0c463e664e | |||
| 4df43c802b | |||
| 784d3c6c9a | |||
| fcb85e612f | |||
| b4df7e351b | |||
| 806cd112ac | |||
| 26f050ff10 | |||
| bde8b78a88 | |||
| 62f890b5aa | |||
| db027dd95b | |||
| 613fb2fed6 | |||
| f918af82fe | |||
| 844d3a4dfa | |||
| 4228a75186 | |||
| 2fe609cb62 | |||
| f2466e5224 | |||
| f98e84f03f | |||
| bbd61f375f | |||
| 576fda18eb | |||
| 7a4280a482 | |||
| 6a43b31218 | |||
| 450734e333 | |||
| cf7f6b49ed | |||
| b9dc8eb14d | |||
| 69188c8538 | |||
| 5949d56370 | |||
| 5bed97064a | |||
| b2bdfcf1b5 | |||
| 0d29385e1b | |||
| 87edeb4e32 | |||
| 0f12d6c426 | |||
| 3406dc5b2e | |||
| 4a82b0d891 | |||
| bc365f1eaa | |||
| 558c8ece2c | |||
| 1187c7fdac | |||
| e1d2653a7a | |||
| 36a0a3d37b | |||
| d97bbd5324 | |||
| d2a6cff4df | |||
| acf5ed595f | |||
| 03845198ce | |||
| d8a3d08f8e | |||
| 6f9118dca6 | |||
| d0b6701fba | |||
| 94c3eecb99 | |||
| 098e5c281c | |||
| f2c52f777c | |||
| 2b41263aa7 | |||
| 404a4468d8 | |||
| 7f67c7f952 | |||
| c8540b5744 | |||
| f6d97d4488 | |||
| 87712a0a7c | |||
| ba0e053ffc | |||
| d14779b264 | |||
| 5411f4a667 | |||
| 2034098780 | |||
| d53a5c4fb5 | |||
| f6ee025c46 | |||
| e50bf6adad | |||
| c21b6899e1 | |||
| 95693e265e | |||
| 569c6f1dd0 | |||
| 7350399a50 | |||
| c2a741ee36 | |||
| e177314a4a | |||
| dadd606c0d | |||
| b3fcddde11 | |||
| e205a468d9 | |||
| bdf80ea7c0 | |||
| 5e218a5630 | |||
| 8c36b10682 | |||
| 42c2e1b217 | |||
| 1e715d1e5c | |||
| 9f9a2fa844 | |||
| 64eb14ab11 | |||
| 0713c86dec | |||
| 99c5bf2e97 | |||
| fe67ef63c1 | |||
| 19d4384f16 | |||
| d351f07758 | |||
| d72f35c2e7 | |||
| aeffa2a460 | |||
| 030a5ace97 | |||
| b97661553f | |||
| 3370644ffe | |||
| 0f0b463a2f | |||
| 8d082a904d | |||
| 61582678bf | |||
| 613b8a4527 | |||
| 5982abeffb | |||
| 78b10dccf1 | |||
| 85bc25a765 | |||
| fec0eba07e | |||
| f92e79eba0 | |||
| 3e50be1658 | |||
| 9937fb2220 | |||
| 8e531dc029 | |||
| 83d0bdc32e | |||
| d15970e12c | |||
| c7b5dcd28f | |||
| 8c07ad2ab9 | |||
| ade9dfeebb | |||
| d1e35f8660 | |||
| e20d282088 | |||
| 4adfdad47f | |||
| 6c126b9e58 | |||
| 1d2e27a698 | |||
| 049a299b92 | |||
| b1258dd52c | |||
| 98cdaa01b8 | |||
| 3476a97acc | |||
| 9ebf321717 | |||
| ff725dea41 | |||
| c3f1b29c1e | |||
| 257cd07a3a | |||
| 43d6dc3668 | |||
| 2231497b1f | |||
| d0a845b6e6 | |||
| e4c4bb265e | |||
| 0f845407f1 | |||
| b2a2ea3633 | |||
| f8a862738d | |||
| 25f8452127 | |||
| 84573992b4 | |||
| 3f8d636501 | |||
| 300205b07c | |||
| e1c711f5ba | |||
| 282654e7b8 | |||
| 0d89ac7406 | |||
| 5c47592159 | |||
| 61f0f3c243 | |||
| 1e8ae5b9d7 | |||
| e8d0e0d342 | |||
| b644f037a3 | |||
| c5498bbb07 | |||
| 90c764ce13 | |||
| 10003a6490 | |||
| b916d79fe2 | |||
| 36b0a86c69 | |||
| 7cc5234b9c | |||
| 379f09d92f | |||
| fd9d3e19a9 | |||
| 86828bb561 | |||
| 78075c8a37 | |||
| 7d33baabe4 | |||
| ecf8fba10e | |||
| 2f8b68dffd | |||
| 8877c85278 | |||
| 1d1bdc57ce | |||
| ba88707b1e | |||
| 8c78fe4f10 | |||
| 394a7ea019 | |||
| 78a28bfc01 | |||
| cf8de02c68 | |||
| e650a4ee59 | |||
| c6b6dcbe95 | |||
| 9786c4dcff | |||
| d03496b710 | |||
| ab6b2293e1 | |||
| 472e775a13 | |||
| ce56b4ee1b | |||
| ba15eeb55f | |||
| d683e1c0db | |||
| 6d83d3440c | |||
| 9d74b7c0e8 | |||
| 016b5b42e2 | |||
| a591505bf9 | |||
| 638dd2f6c0 | |||
| 555e25e633 | |||
| 3174573b64 | |||
| 2c5e09e1c3 | |||
| ab11b2c27f | |||
| 0fcbff506e | |||
| 48e3932f65 | |||
| d304216300 | |||
| 2b8adf8cf3 | |||
| fb99b95372 | |||
| 60fe5acd60 | |||
| eb9ff72b5a | |||
| fb03242950 | |||
| 54c1e13853 | |||
| 102905bbc7 | |||
| fe1bfc64f7 | |||
| 0f9ec2735c | |||
| 13d8d9477c | |||
| 43916b98aa | |||
| 5d27a62bec | |||
| a05e0ea3a4 | |||
| 2987b473dd | |||
| ee7065f665 | |||
| d26b828ab3 | |||
| a810ca80bc | |||
| ad1f0d995d | |||
| 1ed163a666 | |||
| aa9922bc98 | |||
| 0075b4f118 | |||
| 4d85ce40be | |||
| 7ec78452ec | |||
| 540f60696a | |||
| 1ffb9c4188 | |||
| 570ccc7da0 | |||
| 1c8fe92d0f | |||
| 396b427cc9 | |||
| c961f27401 | |||
| 1c87e7cd25 | |||
| 408b885689 | |||
| cab9b1f370 | |||
| d3cf28eb4b | |||
| 11a0a9b911 | |||
| e8038c727f | |||
| 2abc288c52 | |||
| 2e2b066713 | |||
| c1076512d1 | |||
| 3154c06dc3 | |||
| b248ec6dfb | |||
| cc2c48d59e | |||
| ac733d40b3 | |||
| a4415f15d3 | |||
| cbbf565121 | |||
| 7bb13d1c41 | |||
| e79f62694a | |||
| 4fbeac8b32 | |||
| 22b0550520 | |||
| 5ba6bc7139 | |||
| 6893d27441 | |||
| 51f952e700 | |||
| 4af4f8644d | |||
| 37ca643a64 | |||
| 8f4b1b582d | |||
| 3c9052a751 | |||
| 2136598e87 | |||
| 3f90001f83 | |||
| 4ef4bd6f09 | |||
| 331dbd5638 | |||
| 4ab94dec5e | |||
| c0b766ad74 | |||
| fd59d9dd92 | |||
| 9e4ae214a8 | |||
| f649948713 | |||
| 43b8731241 | |||
| 9116cf2bab | |||
| bafbcbbe8b | |||
| fdb6088603 | |||
| f3a8b73717 | |||
| c883403147 | |||
| 2077521f84 | |||
| 6d90b7ddb3 | |||
| 6cf1c98524 | |||
| 5ff7cdc9e8 | |||
| 47603ef8e1 | |||
| ef4030331a | |||
| a0a6828265 | |||
| 69339f08a6 | |||
| cd27cae848 | |||
| 475e92da5d | |||
| 054497c7a6 | |||
| c81a02f8d2 | |||
| 445a5eac33 | |||
| f05d937f39 | |||
| 5f1208ad81 | |||
| 1e42fdf6c2 | |||
| 98055d0989 | |||
| c13ec85d7d | |||
| 36feb73bfd | |||
| 77614eff5b | |||
| 31b34b11ab | |||
| c585470a71 | |||
| 9ba1cd0336 | |||
| 5f6453a1e0 | |||
| 956ab94452 | |||
| 0f5dd2229c | |||
| 224a33db2e | |||
| fb0768f007 | |||
| 9787108532 | |||
| 44b8c62db9 | |||
| 1d2f90c7e7 | |||
| c951f9fdcd | |||
| fa93b56243 | |||
| ca6cfaaf4e | |||
| 400da30a8d | |||
| f5bd474e51 | |||
| 16113647de | |||
| f51d74586c | |||
| c743631148 | |||
| b445db3d46 | |||
| 21dd9bbf7d | |||
| 3937461272 | |||
| da3da19844 | |||
| 53c7646ed7 | |||
| b6524e410a | |||
| 75c2769b32 | |||
| f796650122 | |||
| 460c3debf5 | |||
| b8b6620365 | |||
| 96d7eb2966 | |||
| da4fa5ad75 | |||
| 2b77c1ded4 | |||
| 6ab1b239ca | |||
| ab73051298 | |||
| cb2880cb93 | |||
| b5315bfc20 | |||
| b259153403 | |||
| f0c3c81e94 | |||
| fc42c4613f | |||
| bd06e5b161 | |||
| ad33c22374 | |||
| f3759381b1 | |||
| 80673a0c0c | |||
| 1671bf77fb | |||
| ffc5e4d048 | |||
| 60d2c2cc90 | |||
| 2144d25885 | |||
| 7a515339ea | |||
| be1dc13bb1 | |||
| 1c044ba8af | |||
| 4fc9b1cde2 | |||
| 60973aacd9 | |||
| 1c18552426 | |||
| c4377c1b1a | |||
| 265f24e5d7 | |||
| 59e0b10e6c | |||
| 9d642f3bb1 | |||
| 19ea68b838 | |||
| 93f14ce626 | |||
| 1ef34261e0 | |||
| 5062fadf87 | |||
| 462c7d3502 | |||
| 9187f6f6d1 | |||
| c0495ce2f9 | |||
| 02518d2927 | |||
| d7243fb81f | |||
| e3262f8766 | |||
| caf2ca1438 | |||
| f8ce3585eb | |||
| 61207fc2cb | |||
| b3cc397a31 | |||
| f9df415392 | |||
| fd2cbacae9 | |||
| 8b93a5f27d | |||
| 9478bca67d | |||
| 4b53b3a6e6 | |||
| d72f8453cb | |||
| 6ee7165e39 | |||
| adddafe6d0 | |||
| 35f091bb01 | |||
| e9c7a80b12 | |||
| ab013fb7e9 | |||
| c158923b27 | |||
| 31c5761e06 | |||
| f4ee245bf9 | |||
| d13482e802 | |||
| 11e1e98022 | |||
| b31f6804fe | |||
| 01ad74a870 | |||
| f875911af7 | |||
| f8ff921c42 | |||
| ab8c24f5ea | |||
| 9da3cb7e43 | |||
| 6f69cdcc31 | |||
| 236334d015 | |||
| 12472ce9b2 | |||
| e762cda55d | |||
| f566df91c8 | |||
| db37c715c0 | |||
| c89bc30d66 | |||
| 59e00eeda6 | |||
| 3332703fca | |||
| 643f2c0958 | |||
| c6a7107f78 | |||
| 322feaafa6 | |||
| 68afb7200e | |||
| b382ae6803 | |||
| 135d981e3c | |||
| 54fa26ef0e | |||
| cc081337b7 | |||
| d4cad0cdcc | |||
| b8969cceff | |||
| a3370ac86b | |||
| 7d03151cd5 | |||
| 054b4307ed | |||
| b8330b626e | |||
| 2a3244b1bb | |||
| 4d2a2de557 | |||
| c583b510e0 | |||
| 42c79c64d7 | |||
| 1d9e6870be | |||
| 167b6ff8a1 | |||
| 77df6d48e2 | |||
| 06035d5d43 | |||
| 6c8a48db13 | |||
| 523274dbf3 | |||
| 36207abec2 | |||
| 99f75f3218 | |||
| 82c10421a0 | |||
| 2e003ad8cf | |||
| 42a265d290 | |||
| 121732dde6 | |||
| 3e9701861e | |||
| b31b786db7 | |||
| 66e981ed1e | |||
| c60d8ef5a8 | |||
| 6e026bd950 | |||
| 372b588778 | |||
| 5d87a7f9c7 | |||
| 706834ecd3 | |||
| cca41edc6e | |||
| 601a639f95 | |||
| f6423ea45a | |||
| 70996bfdee | |||
| 44bdd3ad11 | |||
| ab1f195508 | |||
| 39eb6ed9cc | |||
| e750da987d | |||
| 7e987113a2 | |||
| 7a238bd938 | |||
| c2d60d61ac | |||
| 13aa0148e7 | |||
| 5d61adf804 | |||
| 4e6eef5882 | |||
| 064edc52f5 | |||
| 1b302deeff | |||
| 25f27509c0 | |||
| d465a26e30 | |||
| 034ca93907 | |||
| cca5a12839 | |||
| ecf0a24873 | |||
| cb208f53d8 | |||
| 85f3a8c210 | |||
| e9f8ccd51a | |||
| a9cb8f4958 | |||
| 2dfb813c90 | |||
| cb0947c501 | |||
| 2a87d663d2 | |||
| 6db64aab2b | |||
| abd22a753d | |||
| 9e8f7c074c | |||
| 44c62c8e5d | |||
| 4ef3c09332 | |||
| 29efebe38f | |||
| 0e4dce23b2 | |||
| e115083fac | |||
| 5ded674ad6 | |||
| 541eeb7a50 | |||
| 23c906b085 | |||
| c7817aee30 | |||
| 2fa13420ae | |||
| ee66732ad2 | |||
| 8352980f01 | |||
| a2d7f82b49 | |||
| 73570f1c86 | |||
| 51578397a5 | |||
| ef70e63230 | |||
| ce26b58f09 | |||
| bcd9735a73 | |||
| b1059f891f | |||
| 145e099ca5 | |||
| 81006605c8 | |||
| f934f01881 | |||
| cdff69b7b2 | |||
| d91484eb4d | |||
| c20b88cee2 | |||
| 810d940e57 | |||
| 7e2642b9f1 | |||
| 40057b55f0 | |||
| b188a51c32 | |||
| 63a90836fe | |||
| ca94dabd4f | |||
| 31b7c010d0 | |||
| 4960c47257 | |||
| ee92db7533 | |||
| c2104a14fb | |||
| 25996ae037 | |||
| a123a813b2 | |||
| 8bdef8754e | |||
| 978fbcf95e | |||
| d915525c8e | |||
| c079084ca4 | |||
| a889c15e38 | |||
| 884d838a1e | |||
| 0fe82a2f4e | |||
| b16fe7b646 | |||
| b77381750c | |||
| 9814f86a25 | |||
| aa6ae954ef | |||
| 5e70a7dd46 | |||
| 48ff9e1555 | |||
| c4c0c0d182 | |||
| 3a501196f0 | |||
| 7787a31f81 | |||
| 9e91aafe40 | |||
| 8ad72ec1ae | |||
| d3e4ff2cfc | |||
| 750c0e366f | |||
| bde5d61812 | |||
| 5ae9fe6949 | |||
| 3f38f95b1d | |||
| 445ef4fbed | |||
| 5ebe40e919 | |||
| 8e9f71b7a3 | |||
| 1202dced73 | |||
| 047bc44032 | |||
| 92d412e542 | |||
| d9f0b9c668 | |||
| 8f8a689722 | |||
| a90b9fe977 | |||
| eee3452948 | |||
| 2ede47d5ee | |||
| 4f220e945a | |||
| 30dd2f1dfe | |||
| b40f67b76a | |||
| a7faa2080f | |||
| 6d75005afc | |||
| 5bb9cd1a13 | |||
| 0bf2a0353d | |||
| ce655436ef | |||
| 59985138f7 | |||
| 0542de95eb | |||
| dc90c8fec7 | |||
| 0d7da7ecb1 | |||
| 73b1afb106 | |||
| d1c913ed5c | |||
| 2940b50811 | |||
| c7243997f4 | |||
| 306e12c23c | |||
| cc3904f026 | |||
| ed9f714fbb | |||
| 5b750f519b | |||
| 465f97a528 | |||
| ccf8d0ca31 | |||
| 519bd57e55 | |||
| 16f5f767b2 | |||
| c6a59896f3 | |||
| e9e80b054d | |||
| b364f37655 | |||
| f5e07d94bd | |||
| dd3b1cb653 | |||
| cf16d1678d | |||
| fb44f5ba0d | |||
| e72c00cf9a | |||
| 62dc9683bd | |||
| 3443946087 | |||
| e49f4673f2 | |||
| af833c5eff | |||
| 74a7771943 | |||
| 193b4bba91 | |||
| bf80263bd6 | |||
| 0658b4aa31 | |||
| a74a04d12d | |||
| 2c93542e5c | |||
| cb8f93bad7 | |||
| 9d0177e0ce | |||
| f0eed9b221 | |||
| a2013f34e5 | |||
| 0ed4f98087 | |||
| 14867c7c14 | |||
| f4080b6037 | |||
| 0e7b3951c2 | |||
| 81772c4252 | |||
| fc4e10b537 | |||
| 654c555099 | |||
| 7dd2d8f794 | |||
| 8aace3aff2 | |||
| 72b16b3a24 | |||
| 723b8d33c2 | |||
| 70a99af1d3 | |||
| 31f58a1f04 | |||
| cc7e1472f9 | |||
| 995ae717cc | |||
| d5a06d3cd2 | |||
| 61a71c4fcd | |||
| 3c57e76c45 | |||
| 30d9a336bc | |||
| 49bde9fca0 | |||
| 36de686224 | |||
| 085e5b1f4d | |||
| a96f065903 | |||
| 9a4c045518 | |||
| 397e52dac7 | |||
| d0ab6e9982 | |||
| 35afab31e1 | |||
| 71ecc401c3 | |||
| 518a9ca314 | |||
| d52ec522f1 | |||
| 8731309d7c | |||
| a788a6df48 | |||
| c8518d6a63 | |||
| 0fd9ff0f53 | |||
| 23e52f0ff3 | |||
| 98eef9ba0c | |||
| f425bd765b | |||
| d065c3ca53 | |||
| f22aa72c62 | |||
| aa46eb4fd7 | |||
| ff31a2224e | |||
| dd42893df7 | |||
| 725b312032 | |||
| cd76b0b22d | |||
| 2ef38065c7 | |||
| cedf0235a1 | |||
| f4330c9f5e | |||
| 9b9ab60985 | |||
| 0b20f88fc0 | |||
| 21163a1636 | |||
| 08e87a59d5 | |||
| d3bdbc6978 | |||
| b4a405c684 | |||
| 426d36146a | |||
| 4d2a1111e0 | |||
| e4226b8a3e | |||
| 7c086fe55b | |||
| 6786684962 | |||
| 8da47db1a4 | |||
| 2b61ac532d | |||
| 4a0fcd054b | |||
| 659b0557be | |||
| 795e5134c7 | |||
| 0ded546a09 | |||
| be25e2cb96 | |||
| b2ef66263f | |||
| ca3d260a2b | |||
| f3ffaf09c7 | |||
| 3a1d3769ae | |||
| d42da87161 | |||
| 406f0baaf2 | |||
| cd0f9fe22f | |||
| a67deae8ba | |||
| dcf362bcf9 | |||
| 22f725eb08 | |||
| 9049f8f8ae | |||
| a440333990 | |||
| 991bd37362 | |||
| 9a4211b610 | |||
| c71b749185 | |||
| 02241e9126 | |||
| 01c577c371 | |||
| 2aa1d74286 | |||
| 6b866d1282 | |||
| de3632afc3 | |||
| 937c15c66e | |||
| bec2bfcadb | |||
| 39cc07de30 | |||
| d2c9c5b35e | |||
| 6ded45e5d2 | |||
| 05930d5e25 | |||
| ffa547ce8d | |||
| 155242af34 | |||
| c96fd828df | |||
| b4f6c7c4b9 | |||
| 872d2eaf60 | |||
| b734723d95 | |||
| 5aaa0e66f5 | |||
| a9083b9d48 | |||
| 60420e52db | |||
| 3acb014ed7 | |||
| c9c633be62 | |||
| 130f0a0253 | |||
| 8c74be79c5 | |||
| e2fef41fe2 | |||
| 72b2cc54b9 | |||
| cfaa95a2b9 | |||
| 8c1656bf56 | |||
| 1fc3fc0a22 | |||
| ccaa7009a7 | |||
| 47f693173a | |||
| 7bed302f21 | |||
| 2e6d69c9c8 | |||
| c80352a7fb | |||
| 47f5e73be6 | |||
| d38ab07939 | |||
| 4f17eae5cc | |||
| b8df8b2ab8 | |||
| 8a937ebf8b | |||
| 0dea354457 | |||
| cb1ec755f6 | |||
| 984415f6c7 | |||
| 203bad7c06 | |||
| 4f5b335792 | |||
| dabe161a6f | |||
| a2f3339a0e | |||
| 996c9f5955 | |||
| bd5c158a62 | |||
| 6f0107e7b7 | |||
| 769fe8b161 | |||
| ef3186d441 | |||
| 8c78b62b75 | |||
| 0a3e492e6b | |||
| 99c7108bb0 | |||
| 49b66733c2 | |||
| a6720d6007 | |||
| 92dbdbb93b | |||
| 061a89fc2b | |||
| 7c1a90244a | |||
| c86ee4cc83 | |||
| 7b06a0bebd | |||
| 9e8c767694 | |||
| 3ba4ba79fa | |||
| 1e838393a9 | |||
| 481ba01c97 | |||
| b2ba67f337 | |||
| 249a193c00 | |||
| a3fe9279d8 | |||
| 0f8199dde3 | |||
| 6787d42de4 | |||
| 9185f68e52 | |||
| f56a561f02 | |||
| dd01af609e | |||
| ada179f572 | |||
| 20fc7abc8c | |||
| f3424844dc | |||
| c4bd759467 | |||
| 771627505d | |||
| a73b81452d | |||
| 4a5ef4d9f7 | |||
| 518caae62e | |||
| 7beaa368a9 | |||
| 19c1d73405 | |||
| 28e667bd97 | |||
| f68f27e7f1 | |||
| 90de8416cc | |||
| c23eb84b04 | |||
| cfb71b9d60 | |||
| 87f175bb2c | |||
| 907e51ac01 | |||
| 5dc7059ba3 | |||
| 09ef33ec3a | |||
| cd9193466e | |||
| ead8928c39 | |||
| 265d39f337 | |||
| 37678acb1a | |||
| a64bb433b0 | |||
| a6e00d9183 | |||
| bf0f61e656 | |||
| ae48e964f0 | |||
| ab3804d823 | |||
| 0a7ee67707 | |||
| a5e47c62e4 | |||
| 2a7c71667d | |||
| c6af4eaa00 | |||
| 32db4ff66d | |||
| 849cd1f9ed | |||
| 249ea55942 | |||
| affd3cae9a | |||
| 8dc397c0a5 | |||
| 38bc856212 | |||
| 971eb64e98 | |||
| 65b9e367f0 | |||
| 112790cbaa | |||
| 558be87311 | |||
| c82c2c2b15 | |||
| 83075b2800 | |||
| cce245738e | |||
| fda3b54357 | |||
| d190188aaf | |||
| 21062dd30e | |||
| ed37b7c5e7 | |||
| 0b6c02000f | |||
| 1f6716f98a | |||
| 5aab793cfd | |||
| 5e688b8113 | |||
| 5f96eba54a | |||
| a8379d1f4b | |||
| 6d84d4dc9c | |||
| ae02236c63 | |||
| 433ca84ce0 | |||
| bd6bba8d07 | |||
| cd354aebed | |||
| b60c8858af | |||
| 70610c740e | |||
| a08938010c | |||
| 0cd490a9b8 | |||
| a044c25981 | |||
| 3d24575239 | |||
| 3ea5581ad7 | |||
| 95268b266d | |||
| 06920402f8 | |||
| bcbcaeb82c | |||
| 29aabd7bfc | |||
| 56ca62cf3c | |||
| 741b57ed06 | |||
| 8d8a2ab64e | |||
| 3d106186f4 | |||
| 118aade84c | |||
| 76b1deec25 | |||
| f2852056a1 | |||
| 1962b51d8d | |||
| b92e3bca50 | |||
| 467a305f26 | |||
| b0b1be0c2a | |||
| c0552ceb22 | |||
| b45bd5ff7b | |||
| 603ec2b21b | |||
| 1af3fef33a | |||
| 8aa730082e | |||
| 8ac2c6842d | |||
| 8980276b20 | |||
| 5d09ab7eb3 | |||
| 8cd2ec7c9b | |||
| c195a9aa3b | |||
| ed0d1a0ba3 | |||
| cf7debbac1 | |||
| 34ba8be821 | |||
| 6bb99806f0 | |||
| d93e987f24 | |||
| 343be47fa9 | |||
| c4656fb063 | |||
| 69f93f852e | |||
| 5a0b21b1d1 | |||
| dc0e0b4165 | |||
| 4f53919a2b | |||
| e705f45c5f | |||
| 4cd3728039 | |||
| 974ab66b7a | |||
| abe4045c63 | |||
| d9fdff339a | |||
| b6d3c56b35 | |||
| 0cf01df48c | |||
| 7f8537a130 | |||
| 1a06282061 | |||
| 7db79e145e | |||
| 319f43fa23 | |||
| f2308dba28 | |||
| c9eb58e122 | |||
| 0c61653b34 | |||
| 43b3f79d60 | |||
| 8149a4546e | |||
| ee3e4017c3 | |||
| 3f79d7e5bb | |||
| 3b92f127a9 | |||
| 667ca6d241 | |||
| d8570e4d64 | |||
| 505e88656a | |||
| f76adec8a6 | |||
| 93c7378d3b | |||
| 2ab61dd16a | |||
| 16d4701822 | |||
| fcdfa8609a | |||
| 4af89e944d | |||
| 43bac6a038 | |||
| 0713dd4d2d | |||
| 69e1239698 | |||
| f63561dce4 | |||
| 0c6f9d2898 | |||
| 63efcb6b66 | |||
| 452d0e21b6 | |||
| 0f465e88e8 | |||
| a6af7bbb46 | |||
| 4a70d6f22f | |||
| 460ec60212 | |||
| 12d4ec2e07 | |||
| e7a13aa031 | |||
| eae8b8b1a1 | |||
| 99958c68e7 | |||
| 332e392aee | |||
| c987e6a623 | |||
| ebbfcda762 | |||
| aa8b2abe3d | |||
| 14dbda9145 | |||
| 33269bdbc9 | |||
| 6553e64431 | |||
| 8174e1d5b8 | |||
| a404fb8d2e | |||
| ef76a801c4 | |||
| 6eca199c39 | |||
| 331ae7dbdf | |||
| 5b16771567 | |||
| 7e493f4a9e | |||
| f3152fa7fb | |||
| 163dba7e4b | |||
| 5ceae177d5 | |||
| 65e7ccd1d4 | |||
| ed1b5fe5e1 | |||
| 1ee161f3c1 | |||
| 5c6f006634 | |||
| a80cd28d4c | |||
| 46c884de51 | |||
| 4c5ab80b7e | |||
| f207ea94d6 | |||
| c913ce3c0b | |||
| f2aa9d283a | |||
| 794d92a79d | |||
| 6695c32aa2 | |||
| c0400a4414 | |||
| 0fec673bfb | |||
| 178e89a914 | |||
| d991c4607d | |||
| 62e9691373 | |||
| 3d1b0df0fa | |||
| 953935d67c | |||
| 6c54746e24 | |||
| ec08129fba | |||
| 42436d2ed4 | |||
| 1067df1872 | |||
| 6ef78cbbe9 | |||
| 1d24f95a3f | |||
| ddcbd0c2bd | |||
| ae51bbdae8 | |||
| ae387b61a9 | |||
| 6f6e004f82 | |||
| d37fff7fd6 | |||
| cea1a867b6 | |||
| d6933c77ba | |||
| 0c3fcb7030 | |||
| 94f43c79d0 | |||
| 5478b58166 | |||
| a49a09f13c | |||
| ac4a79223a | |||
| 042288e72c | |||
| 8a2c2dc73f | |||
| bf32492da9 | |||
| e8a0249e6b | |||
| ea061f52b0 | |||
| d1485d4672 | |||
| 62ba330612 | |||
| 1bd75f060d | |||
| 331e2ce45d | |||
| 0b2d79a2ea | |||
| ffcd996366 | |||
| 93694c6a65 | |||
| db51e3f4cd | |||
| 19400ba8c7 | |||
| eb1a6a6091 | |||
| 80a414be97 | |||
| 8d17d09488 | |||
| 38dccf32c1 | |||
| 24c15b9d43 | |||
| 2aa2ab878b | |||
| 969833e6e3 | |||
| 94b377049b | |||
| 0d22b22c82 | |||
| e909993dd1 | |||
| 3d7cb3fb8a | |||
| e8a065cb9f | |||
| 8a16165a9b | |||
| a4516665d5 | |||
| 11c995e9fa | |||
| 53434d860a | |||
| 2e4e53c3ee | |||
| 7e2ffd7a80 | |||
| 463e5d5b7e | |||
| 809b933d81 | |||
| 8bf8707668 | |||
| 18e5113754 | |||
| 70bc2933cc | |||
| 6535b71c35 | |||
| ed309096b1 | |||
| f885375c72 | |||
| d2d9ae3f91 | |||
| 5f080aa52a | |||
| c334f02d51 | |||
| 2d76cdf2c6 | |||
| defda3a97d | |||
| a0c8e3bf2b | |||
| e209724789 | |||
| 4caaa2a8e8 | |||
| c463d47fa8 | |||
| f80eb71068 | |||
| 135d3401cd | |||
| fab279f0fd | |||
| b1da8c210e | |||
| 9ba1640c07 | |||
| 422eb78b41 | |||
| 275a12fd45 | |||
| 8abe7e151c | |||
| 05c962af1f | |||
| 74447fcff2 | |||
| ee36307495 | |||
| 4c6da1eaf9 | |||
| 86e45c9aa5 | |||
| e0ba7e4ffb | |||
| 3660d4ecc0 | |||
| 22740ddceb | |||
| ad59be0c81 | |||
| 66c2184fe5 | |||
| e0ef5beae4 | |||
| 9d70649b75 | |||
| 4f49341ce9 | |||
| d8b895a2f1 | |||
| 1753c71bfa | |||
| b540137820 | |||
| 5cadd37eb2 | |||
| cc47e475af | |||
| 3667ecf107 | |||
| ce92ed3f68 | |||
| c6f8ecc227 | |||
| 38e053b7bb | |||
| 03bd6876d2 | |||
| 4bd4cd697b | |||
| 28c3901513 | |||
| c12b87ed03 | |||
| f795255e58 | |||
| 39b0948417 | |||
| 16278fd849 | |||
| 98461ff667 | |||
| 31c609daec | |||
| 7f9975cf1c | |||
| bfd904bfc8 | |||
| f46e50b271 | |||
| d8393a06dd | |||
| 5151bedf06 | |||
| c96f8259c1 | |||
| 82625fc07a | |||
| c93eed6384 | |||
| 5218323831 | |||
| 7e1705274c | |||
| 40db029887 | |||
| 8d637a434c | |||
| 712dc245ac | |||
| 47948e3712 | |||
| 570b0086b6 | |||
| 4cdf9207f3 | |||
| 6328ca512f | |||
| d7a0dbc6b1 | |||
| 525ced295c | |||
| 9c4d1594ac | |||
| fcffcfbacb | |||
| 59df649349 | |||
| 93e022cee5 | |||
| 710e00e02d | |||
| 4ef46e441c | |||
| bcc4d81d19 | |||
| 6c559e2338 | |||
| c0c7ad10ca | |||
| 10392ad344 | |||
| 34c14b7dd2 | |||
| 57272f84b1 | |||
| 9abb165f7b | |||
| 92543da28c | |||
| 81d03cb524 | |||
| edd988be60 | |||
| 6869dbe695 | |||
| d9828e2571 | |||
| 8fdb61aabf | |||
| b4455af306 | |||
| 56f394cefd | |||
| a1dc7a8f55 | |||
| 62b49ab14a | |||
| 375b8522fc | |||
| 2216856e3c | |||
| 468db8730a | |||
| aba23feddd | |||
| 44691a4ce6 | |||
| 22a560a990 | |||
| 0152759dfb | |||
| 37c5397319 | |||
| c564464e6b | |||
| 532497b3d1 | |||
| ab982b8e32 | |||
| 5be2a9d5e2 | |||
| f028913b0e | |||
| c8ad831039 | |||
| 3bf8fec2f1 | |||
| b9f6283a8e | |||
| 4c43c69d35 | |||
| fd64d34082 | |||
| d0da1f5fc9 | |||
| 7681c595b5 | |||
| 2bf226e918 | |||
| dd91b0a68b | |||
| fa8cea170a | |||
| 6e4236bf76 | |||
| 2c4f61eca5 | |||
| 23467cdbdb | |||
| f371ecb948 | |||
| 9f597ab7c0 | |||
| c9b39c379b | |||
| b08633daa3 | |||
| 5f5e07e6cc | |||
| 8af1439697 | |||
| 0fbc13aa4b | |||
| e232c1d5ba | |||
| e7a88f225d | |||
| 47e8822467 | |||
| 44805f4d58 | |||
| bba0f2e557 | |||
| b1fb68bb06 | |||
| fd2bfc384d | |||
| bcc1f25c53 | |||
| c442f8ae8c | |||
| 951596ffe4 | |||
| 1bfd575473 | |||
| e48f61bdc7 | |||
| 938c850ed8 | |||
| 55b6f848fc | |||
| 889a6c21ac | |||
| ecd95ed6b3 | |||
| 4e6ca95db9 | |||
| 52c1599240 | |||
| 9c392b2d44 | |||
| 42bac72be2 | |||
| 1195072aae | |||
| 09f0672734 | |||
| 00cd4b6a19 | |||
| c434a7e682 | |||
| 3f16ed5a9a | |||
| d5abb46fad | |||
| c56d0ce6d2 | |||
| cf95b631cd | |||
| 2993800e07 | |||
| e678b231a3 | |||
| a815fa37e7 | |||
| 81dcef070f | |||
| 690867e433 | |||
| 9954b03ced | |||
| ec01b1f29f | |||
| bfa4140593 | |||
| b14a4f5ebc | |||
| 29852e9b08 | |||
| 509444d059 | |||
| bbcc906133 | |||
| 22b7d86574 | |||
| e94ce7e2fd | |||
| afba59a953 | |||
| ec0acc486e | |||
| 0fcda10082 | |||
| 1f31443ed1 | |||
| a34e375193 | |||
| 899b6f72cb | |||
| db5b49b2ca | |||
| f2a47dec54 | |||
| 92c99d7873 | |||
| 2c754d71e3 | |||
| bcfd50b45b | |||
| 853ae56e7e | |||
| f41db212ec | |||
| b376df7691 | |||
| b88d9c6353 | |||
| ec9e548bd6 | |||
| 4f468a9f19 | |||
| 99b43e1302 | |||
| 6b34f38ce0 | |||
| 143e5c3140 | |||
| c9cc05bc3d | |||
| 6be59628c3 | |||
| 56b909c8e1 | |||
| 2f64307c19 | |||
| 7a68997b18 | |||
| 2d406ffc75 | |||
| 0534ca74d4 | |||
| e0fcbc39f6 | |||
| 6dca8270bc | |||
| d746eb7b22 | |||
| 930f39a0cd | |||
| 8f0306f499 | |||
| f3abfb8e66 | |||
| f8678f9e9d | |||
| 72114464b8 | |||
| 407373dcd6 | |||
| 6756a8b8a9 | |||
| d54cdd8802 | |||
| eddd13d70e |
@@ -0,0 +1,39 @@
|
||||
description = "Answer questions about the Gemini CLI codebase with explanations and code snippets."
|
||||
|
||||
prompt = """
|
||||
## Mission: Explain the Gemini CLI Codebase
|
||||
|
||||
Your primary task is to help a new engineer understand the Gemini CLI codebase by answering their questions about architecture, specific functions, and project structure.
|
||||
|
||||
|
||||
### Objective:
|
||||
|
||||
Your primary task is to help a new engineer understand the Gemini CLI codebase. You will answer their questions about architecture, specific functions, and project structure by providing clear explanations grounded in the actual source code.
|
||||
|
||||
|
||||
### Instructions:
|
||||
|
||||
1. **Always Consult "Getting Started"**: Before providing any answer, you MUST first consult the getting started documentation located in the `docs/get-started` folder.
|
||||
|
||||
2. **Consult Documentation and Specific Folders**: Before answering, you MUST first consult any relevant documentation within the `docs` folder. Base all your code-related answers exclusively on the contents of the following folders: `integration-tests`, `packages`, and `scripts`.
|
||||
|
||||
3. **Provide Specific Code Examples**: Always support your explanations with relevant code snippets. You MUST include the full file path (e.g., `packages/gemini/core.py`) so the user can easily locate the code.
|
||||
|
||||
4. **Explain the "Why"**: Go beyond simply showing the code. Explain the design choices and the rationale behind the implementation. Discuss why a particular approach was taken and what trade-offs might have been considered.
|
||||
|
||||
5. **Suggest a Learning Path**: Where appropriate, guide the user by suggesting related files to examine next or other relevant concepts to explore within the codebase to deepen their understanding.
|
||||
|
||||
6. **Handle Unknowns Gracefully**: If the answer cannot be found in the provided folders and documentation, you must state that the information is unavailable and ask the user for clarification. Do not invent answers or speculate.
|
||||
|
||||
|
||||
### Constraints:
|
||||
|
||||
|
||||
1. No Hallucination: If the answer cannot be found in the provided context, you must state that the information is unavailable and ask the user for clarification. Do not invent answers or speculate.
|
||||
|
||||
2. Stay Focused: Only answer questions directly related to the Gemini CLI project within the specified folders.
|
||||
|
||||
### QUESTION:
|
||||
|
||||
{{args}}
|
||||
"""
|
||||
@@ -0,0 +1,29 @@
|
||||
description="Injects context of all relevant cli files"
|
||||
prompt = """
|
||||
The following output contains the complete
|
||||
source code of packages/core.
|
||||
**Pay extremely close attention to these files.** They define the project's
|
||||
core architecture, component patterns, and testing standards.
|
||||
|
||||
The source code contains the content of absolutely every source code file in
|
||||
packages/core.
|
||||
You should very rarely need to read any other files from packages/core to resolve
|
||||
prompts.
|
||||
|
||||
!{find packages/core \\( -path packages/cli/dist -o -path packages/core/dist -o -name node_modules \\) -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\;}
|
||||
|
||||
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines when writing code in packages/core.
|
||||
|
||||
## Configuration & Settings
|
||||
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
|
||||
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
|
||||
* **Documentation**:
|
||||
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
|
||||
* Ensure `requiresRestart` is correctly set.
|
||||
|
||||
## General
|
||||
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
|
||||
* **TypeScript**: Avoid the non-null assertion operator (`!`).
|
||||
|
||||
{{args}}.
|
||||
"""
|
||||
@@ -0,0 +1,54 @@
|
||||
description="Injects context of all relevant cli files"
|
||||
prompt = """
|
||||
The source code contains the content of absolutely every source code file in
|
||||
packages/cli and key files from packages/core. In addition to the source code, the following test files are
|
||||
included as they are test files that conform to the project's testing standards:
|
||||
`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`.
|
||||
You should very rarely need to read any other files from packages/cli to resolve
|
||||
prompts.
|
||||
|
||||
!{find packages/cli -path packages/cli/dist -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\; && echo "--- packages/cli/src/ui/components/InputPrompt.test.tsx ---" && cat packages/cli/src/ui/components/InputPrompt.test.tsx && echo "--- packages/cli/src/ui/App.test.tsx ---" && cat packages/cli/src/ui/App.test.tsx && echo "--- packages/core/src/core/coreToolScheduler.ts ---" && cat packages/core/src/core/coreToolScheduler.ts && echo "--- packages/core/src/core/nonInteractiveToolExecutor.ts ---" && cat packages/core/src/core/nonInteractiveToolExecutor.ts && echo "--- packages/core/src/confirmation-bus/message-bus.ts ---" && cat packages/core/src/confirmation-bus/message-bus.ts && echo "--- packages/core/src/confirmation-bus/types.ts ---" && cat packages/core/src/confirmation-bus/types.ts && echo "--- packages/core/src/utils/events.ts ---" && cat packages/core/src/utils/events.ts && echo "--- packages/core/src/core/client.ts ---" && cat packages/core/src/core/client.ts && echo "--- packages/core/src/core/geminiChat.ts ---" && cat packages/core/src/core/geminiChat.ts && echo "--- packages/core/src/core/turn.ts ---" && cat packages/core/src/core/turn.ts && echo "--- packages/core/src/agents/executor.ts ---" && cat packages/core/src/agents/executor.ts && echo "--- packages/core/src/telemetry/types.ts ---" && cat packages/core/src/telemetry/types.ts && echo "--- packages/core/src/telemetry/loggers.ts ---" && cat packages/core/src/telemetry/loggers.ts && echo "--- packages/core/src/telemetry/uiTelemetry.ts ---" && cat packages/core/src/telemetry/uiTelemetry.ts}
|
||||
|
||||
**Pay extremely close attention to these files.** They define the project's
|
||||
core architecture, component patterns, and testing standards.
|
||||
|
||||
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines while adding code to packages/cli.
|
||||
|
||||
## Testing Standards
|
||||
* **Async Testing**: ALWAYS use `waitFor` from `packages/cli/src/test-utils/async.ts` instead of `vi.waitFor` to prevent flakiness and `act` warnings. NEVER use fixed waits (e.g., `await delay(100)`).
|
||||
* **State Changes**: Wrap all blocks that change component state in `act`.
|
||||
* **Snapshots**: Use `toMatchSnapshot` to verify rendering.
|
||||
* **Rendering**: Use `render` or `renderWithProviders` from `packages/cli/src/test-utils/render.tsx` instead of `ink-testing-library` directly.
|
||||
* **Mocking**:
|
||||
* Reuse existing mocks/fakes where possible.
|
||||
* **Parameterized Tests**: Use parameterized tests with explicit types to reduce duplication and ensure type safety.
|
||||
* Avoid mocking the file system, os, or child_process if at all possible; if you have to mock the Mock critical dependencies (`fs`, `os`, `child_process`) do so ONLY at the top of the file.
|
||||
|
||||
## React & Ink Architecture
|
||||
* **Keyboard Handling**: You MUST use `useKeyPress.ts` from Gemini CLI for keyboard handling, NOT the standard ink library. This is critical for supporting slow terminals and multiple events per frame.
|
||||
* Handle multiple events gracefully (often requires a reducer pattern, see `text-buffer.ts`).
|
||||
* **State Management**:
|
||||
* NEVER trigger side effects from within the body of a `setState` callback. Use a reducer or `useRef` if necessary.
|
||||
* Initialize state explicitly (e.g., use `undefined` rather than `true` if unknown).
|
||||
* **Performance**:
|
||||
* Avoid synchronous file I/O in components.
|
||||
* Do not introduce excessive property drilling; leverage or extend existing providers.
|
||||
|
||||
## Configuration & Settings
|
||||
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
|
||||
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
|
||||
* **Documentation**:
|
||||
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
|
||||
* Ensure `requiresRestart` is correctly set.
|
||||
|
||||
## Keyboard Shortcuts
|
||||
* **Registration**: Define new shortcuts in `packages/cli/src/config/keyBindings.ts`.
|
||||
* **Documentation**: Document all new shortcuts in `docs/cli/keyboard-shortcuts.md`.
|
||||
* **Compatibility**: Avoid function keys and common VSCode shortcuts. Be cautious with `Meta` key (Mac-limited support).
|
||||
|
||||
## General
|
||||
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
|
||||
* **TypeScript**: Avoid the non-null assertion operator (`!`).
|
||||
|
||||
{{args}}.
|
||||
"""
|
||||
@@ -0,0 +1,57 @@
|
||||
description="Injects context of all relevant cli files"
|
||||
prompt = """
|
||||
The following output contains the complete
|
||||
source code of the gemini cli library (`packages/cli`), and {packages/core} and representative test files
|
||||
(`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`) that best conform to the project's testing standards.
|
||||
**Pay extremely close attention to these files.** They define the project's
|
||||
core architecture, component patterns, and testing standards.
|
||||
|
||||
The source code contains the content of absolutely every source code file in
|
||||
packages/cli and packages/core. In addition to the source code, the following test files are
|
||||
included as they are test files that conform to the project's testing standards:
|
||||
`packages/cli/src/ui/components/InputPrompt.test.tsx` and `packages/cli/src/ui/App.test.tsx`.
|
||||
You should very rarely need to read any other files from packages/cli to resolve
|
||||
prompts.
|
||||
|
||||
!{find packages/cli packages/core \\( -path packages/cli/dist -o -path packages/core/dist -o -name node_modules \\) -prune -o -type f \\( -name "*.ts" -o -name "*.tsx" \\) ! -name "*.test.ts" ! -name "*.test.tsx" ! -name "*.d.ts" -exec echo "--- {} ---" \\; -exec cat {} \\; && echo "--- packages/cli/src/ui/components/InputPrompt.test.tsx ---" && cat packages/cli/src/ui/components/InputPrompt.test.tsx && echo "--- packages/cli/src/ui/App.test.tsx ---" && cat packages/cli/src/ui/App.test.tsx}
|
||||
|
||||
In addition to the code context, you MUST strictly adhere to the following frontend-specific development guidelines when writing code in packages/cli.
|
||||
|
||||
## Testing Standards
|
||||
* **Async Testing**: ALWAYS use `waitFor` from `packages/cli/src/test-utils/async.ts` instead of `vi.waitFor` to prevent flakiness and `act` warnings. NEVER use fixed waits (e.g., `await delay(100)`).
|
||||
* **State Changes**: Wrap all blocks that change component state in `act`.
|
||||
* **Snapshots**: Use `toMatchSnapshot` to verify rendering.
|
||||
* **Rendering**: Use `render` or `renderWithProviders` from `packages/cli/src/test-utils/render.tsx` instead of `ink-testing-library` directly.
|
||||
* **Mocking**:
|
||||
* Reuse existing mocks/fakes where possible.
|
||||
* **Parameterized Tests**: Use parameterized tests with explicit types to reduce duplication and ensure type safety.
|
||||
* Avoid mocking the file system, os, or child_process if at all possible; if you have to mock the Mock critical dependencies (`fs`, `os`, `child_process`) do so ONLY at the top of the file.
|
||||
|
||||
## React & Ink Architecture
|
||||
* **Keyboard Handling**: You MUST use `useKeyPress.ts` from Gemini CLI for keyboard handling, NOT the standard ink library. This is critical for supporting slow terminals and multiple events per frame.
|
||||
* Handle multiple events gracefully (often requires a reducer pattern, see `text-buffer.ts`).
|
||||
* **State Management**:
|
||||
* NEVER trigger side effects from within the body of a `setState` callback. Use a reducer or `useRef` if necessary.
|
||||
* Initialize state explicitly (e.g., use `undefined` rather than `true` if unknown).
|
||||
* **Performance**:
|
||||
* Avoid synchronous file I/O in components.
|
||||
* Do not introduce excessive property drilling; leverage or extend existing providers.
|
||||
|
||||
## Configuration & Settings
|
||||
* **Settings vs Args**: Use settings for user-configurable options; do not add new CLI arguments.
|
||||
* **Schema**: Add new settings to `packages/cli/src/config/settingsSchema.ts`.
|
||||
* **Documentation**:
|
||||
* If `showInDialog: true`, document in `docs/get-started/configuration.md`.
|
||||
* Ensure `requiresRestart` is correctly set.
|
||||
|
||||
## Keyboard Shortcuts
|
||||
* **Registration**: Define new shortcuts in `packages/cli/src/config/keyBindings.ts`.
|
||||
* **Documentation**: Document all new shortcuts in `docs/cli/keyboard-shortcuts.md`.
|
||||
* **Compatibility**: Avoid function keys and common VSCode shortcuts. Be cautious with `Meta` key (Mac-limited support).
|
||||
|
||||
## General
|
||||
* **Logging**: Use `debugLogger` for errors. NEVER leave `console.log/warn/error` in the code.
|
||||
* **TypeScript**: Avoid the non-null assertion operator (`!`).
|
||||
|
||||
{{args}}.
|
||||
"""
|
||||
@@ -0,0 +1,16 @@
|
||||
description = "Analyze the influence of system instructions on a specific action."
|
||||
prompt = """
|
||||
# Introspection Task
|
||||
|
||||
Take a step back and analyze your own system instructions and internal logic.
|
||||
The user is curious about the reasoning behind a specific action or decision you've made.
|
||||
|
||||
**Specific point of interest:** {{args}}
|
||||
|
||||
Please provide a detailed breakdown of:
|
||||
1. Which parts of your system instructions (global, workspace-specific, or provided via GEMINI.md) influenced this behavior?
|
||||
2. What was your internal thought process leading up to this action?
|
||||
3. Are there any ambiguities or conflicting instructions that played a role?
|
||||
|
||||
Your goal is to provide transparency into your underlying logic so the user can potentially improve the instructions in the future.
|
||||
"""
|
||||
@@ -0,0 +1,140 @@
|
||||
description="Reviews a pull request based on issue number."
|
||||
prompt = """
|
||||
Please provide a detailed pull request review on GitHub issue: {{args}}.
|
||||
|
||||
Follow these steps:
|
||||
|
||||
1. Use `gh pr view {{args}}` to pull the information of the PR.
|
||||
2. Use `gh pr diff {{args}}` to view the diff of the PR.
|
||||
3. Understand the intent of the PR using the PR description.
|
||||
4. If PR description is not detailed enough to understand the intent of the PR,
|
||||
make sure to note it in your review.
|
||||
5. Make sure the PR title follows Conventional Commits, here are the last five
|
||||
commits to the repo as examples: !{git log --pretty=format:"%s" -n 5}
|
||||
6. Search the codebase if required.
|
||||
7. Write a concise review of the PR, keeping in mind to encourage strong code
|
||||
quality and best practices. Pay particular attention to the Gemini MD file
|
||||
in the repo.
|
||||
8. Consider ways the code may not be consistent with existing code in the repo.
|
||||
In particular it is critical that the react code uses patterns consistent
|
||||
with existing code in the repo.
|
||||
9. Evaluate all tests on the PR and make sure that they are doing the following:
|
||||
* Using `waitFor` from @{packages/cli/src/test-utils/async.ts} rather than
|
||||
using `vi.waitFor` for all `waitFor` calls within `packages/cli`. Even if
|
||||
tests pass, using the wrong `waitFor` could result in flaky tests as `act`
|
||||
warnings could show up if timing is slightly different.
|
||||
* Using `act` to wrap all blocks in tests that change component state.
|
||||
* Using `toMatchSnapshot` to verify that rendering works as expected rather
|
||||
than matching against the raw content of the output.
|
||||
* If snapshots were changed as part of the pull request, review the snapshots
|
||||
changes to ensure they are intentional and comment if any look at all
|
||||
suspicious. Too many snapshot changes that indicate bugs have been approved
|
||||
in the past.
|
||||
* Use `render` or `renderWithProviders` from
|
||||
@{packages/cli/src/test-utils/render.tsx} rather than using `render` from
|
||||
`ink-testing-library` directly. This is needed to ensure that we do not get
|
||||
warnings about spurious `act` calls. If test cases specify providers
|
||||
directly, consider whether the existing `renderWithProviders` should be
|
||||
modified to support that use case.
|
||||
* Ensure the test cases are using parameterized tests where that might reduce
|
||||
the number of duplicated lines significantly.
|
||||
* NEVER use fixed waits (e.g. 'await delay(100)'). Always use 'waitFor' with
|
||||
a predicate to ensure tests are stable and fast.
|
||||
* Ensure mocks are properly managed:
|
||||
* Critical dependencies (fs, os, child_process) should only be mocked at
|
||||
the top of the file. Ideally avoid mocking these dependencies altogether.
|
||||
* Check to see if there are existing mocks or fakes that can be used rather
|
||||
than creating new ones for the new tests added.
|
||||
* Try to avoid mocking the file system whenever possible. If using the real
|
||||
file system is difficult consider whether the test should be an
|
||||
integration test rather than a unit test.
|
||||
* `vi.restoreAllMocks()` should be called in `afterEach` to prevent test
|
||||
pollution.
|
||||
* Use `vi.useFakeTimers()` for tests involving time-based logic to avoid
|
||||
flakiness.
|
||||
* Avoid using `any` in tests; prefer proper types or `unknown` with
|
||||
narrowing.
|
||||
* When creating parameterized tests, give the parameters types to ensure
|
||||
that the tests are type-safe.
|
||||
10. Evaluate all react logic carefully keeping in mind that the author of the PR
|
||||
is not likely an expert on React. Key areas to audit carefully are:
|
||||
* Whether `setState` calls trigger side effects from within the body of the
|
||||
`setState` callback. If so, you *must* propose an alternate design using
|
||||
reducers or other ways the code might be modified to not have to modify
|
||||
state from within a `setState`. Make sure to comment about absolutely
|
||||
every case like this as these cases have introduced multiple bugs in the
|
||||
past. Typically these cases should be resolved using a reducer although
|
||||
occassionally other techniques such as useRef are appropriate. Consider
|
||||
suggesting that jacob314@ be tagged on the code review if the solution is
|
||||
not 100% obvious.
|
||||
* Whether code might introduce an infinite rendering loop in React.
|
||||
* Whether keyboard handling is robust. Keyboard handling must go through
|
||||
`useKeyPress.ts` from the Gemini CLI package rather than using the
|
||||
standard ink library used by most keyboard handling. Unlike the standard
|
||||
ink library, the keyboard handling library in Gemini CLI may report
|
||||
multiple keyboard events one after another in the same React frame. This
|
||||
is needed to support slow terminals but introduces complexity in all our
|
||||
code that handles keyboard events. Handling this correctly often means
|
||||
that reducers must be used or other mechanisms to ensure that multiple
|
||||
state updates one after another are handled gracefully rather than
|
||||
overriding values from the first update with the second update. Refer to
|
||||
text-buffer.ts as a canonical example of using a reducer for this sort of
|
||||
case.
|
||||
* Ensure code does not use `console.log`, `console.warn`, or `console.error`
|
||||
as these indicate debug logging that was accidentally left in the code.
|
||||
* Avoid synchronous file I/O in React components as it will hang the UI.
|
||||
* Ensure state initialization is explicit (e.g., use 'undefined' rather than
|
||||
'true' as a default if the state is truly unknown initially).
|
||||
* Carefully manage 'useEffect' dependencies. Prefer to use a reducer
|
||||
whenever practical to resolve the issues. If that is not practical it is
|
||||
ok to use 'useRef' to access the latest value of a prop or state inside an
|
||||
effect without adding it to the dependency array if re-running the effect
|
||||
is undesirable (common in event listeners).
|
||||
* NEVER disable 'react-hooks/exhaustive-deps'. Fix the code to correctly
|
||||
declare dependencies. Disabling this lint rule will almost always lead to
|
||||
hard to detect bugs.
|
||||
* Avoid making types nullable unless strictly necessary, as it hurts
|
||||
readability.
|
||||
* Do not introduce excessive property drilling. There are multiple providers
|
||||
that can be leveraged to avoid property drilling. Make sure one of them
|
||||
cannot be used. Do suggest a provider that might make sense to be extended
|
||||
to include the new property or propose a new provider to add if the
|
||||
property drilling is excessive. Only use providers for properties that are
|
||||
consistent for the entire application.
|
||||
11. General Gemini CLI design principles:
|
||||
* Make sure that settings are only used for options that a user might
|
||||
consider changing.
|
||||
* Do not add new command line arguments and suggest settings instead.
|
||||
* New settings must be added to packages/cli/src/config/settingsSchema.ts.
|
||||
* If a setting has 'showInDialog: true', it MUST be documented in
|
||||
docs/get-started/configuration.md.
|
||||
* Ensure 'requiresRestart' is correctly set for new settings.
|
||||
* Use 'debugLogger' for rethrown errors to avoid duplicate logging.
|
||||
* All new keyboard shortcuts MUST be documented in
|
||||
docs/cli/keyboard-shortcuts.md.
|
||||
* Ensure new keyboard shortcuts are defined in
|
||||
packages/cli/src/config/keyBindings.ts.
|
||||
* If new keyboard shortcuts are added, remind the user to test them in
|
||||
VSCode, iTerm2, Ghostty, and Windows to ensure they work for all
|
||||
users.
|
||||
* Be careful of keybindings that require the meta key as only certain
|
||||
meta key shortcuts are supported on Mac.
|
||||
* Be skeptical of function keys and keyboard shortcuts that are commonly
|
||||
bound in VSCode as they may conflict.
|
||||
12. TypeScript Best Practices:
|
||||
* Use 'checkExhaustive' in the 'default' clause of 'switch' statements to
|
||||
ensure all cases are handled.
|
||||
* Avoid using the non-null assertion operator ('!') unless absolutely
|
||||
necessary and you are confident the value is not null.
|
||||
13. If the change might at all impact the prompts sent to Gemini CLI, flagged
|
||||
that the change could impact Gemini CLI quality and make sure anj-s has been
|
||||
tagged on the code review.
|
||||
14. Discuss with me before making any comments on the issue. I will clarify
|
||||
which possible issues you identified are problems, which ones you need to
|
||||
investigate further, and which ones I do not care about.
|
||||
15. If I request you to add comments to the issue, use
|
||||
`gh pr comment {{args}} --body {{review}}` to post the review to the PR.
|
||||
|
||||
Remember to use the GitHub CLI (`gh`) with the Shell tool for all
|
||||
GitHub-related tasks.
|
||||
"""
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"experimental": {
|
||||
"skills": true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: pr-creator
|
||||
description:
|
||||
Use this skill when asked to create a pull request (PR). It ensures all PRs
|
||||
follow the repository's established templates and standards.
|
||||
---
|
||||
|
||||
# Pull Request Creator
|
||||
|
||||
This skill guides the creation of high-quality Pull Requests that adhere to the
|
||||
repository's standards.
|
||||
|
||||
## Workflow
|
||||
|
||||
Follow these steps to create a Pull Request:
|
||||
|
||||
1. **Locate Template**: Search for a pull request template in the repository.
|
||||
- Check `.github/pull_request_template.md`
|
||||
- Check `.github/PULL_REQUEST_TEMPLATE.md`
|
||||
- If multiple templates exist (e.g., in `.github/PULL_REQUEST_TEMPLATE/`),
|
||||
ask the user which one to use or select the most appropriate one based on
|
||||
the context (e.g., `bug_fix.md` vs `feature.md`).
|
||||
|
||||
2. **Read Template**: Read the content of the identified template file.
|
||||
|
||||
3. **Draft Description**: Create a PR description that strictly follows the
|
||||
template's structure.
|
||||
- **Headings**: Keep all headings from the template.
|
||||
- **Checklists**: Review each item. Mark with `[x]` if completed. If an item
|
||||
is not applicable, leave it unchecked or mark as `[ ]` (depending on the
|
||||
template's instructions) or remove it if the template allows flexibility
|
||||
(but prefer keeping it unchecked for transparency).
|
||||
- **Content**: Fill in the sections with clear, concise summaries of your
|
||||
changes.
|
||||
- **Related Issues**: Link any issues fixed or related to this PR (e.g.,
|
||||
"Fixes #123").
|
||||
|
||||
4. **Create PR**: Use the `gh` CLI to create the PR. To avoid shell escaping
|
||||
issues with multi-line Markdown, write the description to a temporary file
|
||||
first.
|
||||
```bash
|
||||
# 1. Write the drafted description to a temporary file
|
||||
# 2. Create the PR using the --body-file flag
|
||||
gh pr create --title "type(scope): succinct description" --body-file <temp_file_path>
|
||||
# 3. Remove the temporary file
|
||||
rm <temp_file_path>
|
||||
```
|
||||
- **Title**: Ensure the title follows the
|
||||
[Conventional Commits](https://www.conventionalcommits.org/) format if the
|
||||
repository uses it (e.g., `feat(ui): add new button`,
|
||||
`fix(core): resolve crash`).
|
||||
|
||||
## Principles
|
||||
|
||||
- **Compliance**: Never ignore the PR template. It exists for a reason.
|
||||
- **Completeness**: Fill out all relevant sections.
|
||||
- **Accuracy**: Don't check boxes for tasks you haven't done.
|
||||
+5
-2
@@ -4,10 +4,13 @@
|
||||
# Require reviews from the release approvers for critical files.
|
||||
# These patterns override the rule above.
|
||||
/package.json @google-gemini/gemini-cli-askmode-approvers
|
||||
/npm-shrinkwrap.json @google-gemini/gemini-cli-askmode-approvers
|
||||
/package-lock.json @google-gemini/gemini-cli-askmode-approvers
|
||||
/GEMINI.md @google-gemini/gemini-cli-askmode-approvers
|
||||
/SECURITY.md @google-gemini/gemini-cli-askmode-approvers
|
||||
/LICENSE @google-gemini/gemini-cli-askmode-approvers
|
||||
/.github/workflows/ @google-gemini/gemini-cli-askmode-approvers
|
||||
/packages/cli/package.json @google-gemini/gemini-cli-askmode-approvers
|
||||
/packages/core/package.json @google-gemini/gemini-cli-askmode-approvers
|
||||
/packages/core/package.json @google-gemini/gemini-cli-askmode-approvers
|
||||
|
||||
# Docs have a dedicated approver group in addition to maintainers
|
||||
/docs/ @google-gemini/gemini-cli-maintainers @google-gemini/gemini-cli-docs
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
name: 'Bug Report'
|
||||
description: 'Report a bug to help us improve Gemini CLI'
|
||||
labels:
|
||||
- 'kind/bug'
|
||||
- 'status/need-triage'
|
||||
body:
|
||||
- type: 'markdown'
|
||||
attributes:
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
name: 'Feature Request'
|
||||
description: 'Suggest an idea for this project'
|
||||
labels:
|
||||
- 'kind/enhancement'
|
||||
- 'status/need-triage'
|
||||
type: 'Feature'
|
||||
body:
|
||||
- type: 'markdown'
|
||||
attributes:
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
name: 'Website issue'
|
||||
description: 'Report an issue with the Gemini CLI Website and Gemini CLI Extensions Gallery'
|
||||
labels:
|
||||
- 'area/extensions'
|
||||
body:
|
||||
- type: 'markdown'
|
||||
attributes:
|
||||
value: |-
|
||||
> [!IMPORTANT]
|
||||
> Thanks for taking the time to report an issue with the Gemini CLI Website
|
||||
>
|
||||
> Please search **[existing issues](https://github.com/google-gemini/gemini-cli/issues?q=is%3Aissue+is%3Aopen+label%3Aarea%2Fwebsite)** to see if a similar feature has already been requested.
|
||||
- type: 'input'
|
||||
id: 'url'
|
||||
attributes:
|
||||
label: 'URL of the page with the issue'
|
||||
description: 'Please provide the URL where the issue occurs.'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: 'textarea'
|
||||
id: 'problem'
|
||||
attributes:
|
||||
label: 'What is the problem?'
|
||||
description: 'A clear and concise description of what the bug or issue is.'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: 'textarea'
|
||||
id: 'expected'
|
||||
attributes:
|
||||
label: 'What did you expect to happen?'
|
||||
validations:
|
||||
required: true
|
||||
|
||||
- type: 'textarea'
|
||||
id: 'additional-context'
|
||||
attributes:
|
||||
label: 'Additional context'
|
||||
description: 'Add any other context or screenshots about the issue here.'
|
||||
@@ -0,0 +1,33 @@
|
||||
name: 'Calculate vars'
|
||||
description: 'Calculate commonly used var in our release process'
|
||||
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Whether or not this is a dry run'
|
||||
type: 'boolean'
|
||||
|
||||
outputs:
|
||||
is_dry_run:
|
||||
description: 'Boolean flag indicating if the current run is a dry-run or a production release.'
|
||||
value: '${{ steps.set_vars.outputs.is_dry_run }}'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: 'Print inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'Set vars for simplified logic'
|
||||
id: 'set_vars'
|
||||
shell: 'bash'
|
||||
env:
|
||||
DRY_RUN_INPUT: '${{ inputs.dry_run }}'
|
||||
run: |-
|
||||
is_dry_run="true"
|
||||
if [[ "${DRY_RUN_INPUT}" == "" || "${DRY_RUN_INPUT}" == "false" ]]; then
|
||||
is_dry_run="false"
|
||||
fi
|
||||
echo "is_dry_run=${is_dry_run}" >> "${GITHUB_OUTPUT}"
|
||||
@@ -0,0 +1,56 @@
|
||||
name: 'Create Pull Request'
|
||||
description: 'Creates a pull request.'
|
||||
|
||||
inputs:
|
||||
branch-name:
|
||||
description: 'The name of the branch to create the PR from.'
|
||||
required: true
|
||||
pr-title:
|
||||
description: 'The title of the pull request.'
|
||||
required: true
|
||||
pr-body:
|
||||
description: 'The body of the pull request.'
|
||||
required: true
|
||||
base-branch:
|
||||
description: 'The branch to merge into.'
|
||||
required: true
|
||||
default: 'main'
|
||||
github-token:
|
||||
description: 'The GitHub token to use for creating the pull request.'
|
||||
required: true
|
||||
dry-run:
|
||||
description: 'Whether to run in dry-run mode.'
|
||||
required: false
|
||||
default: 'false'
|
||||
working-directory:
|
||||
description: 'The working directory to run the commands in.'
|
||||
required: false
|
||||
default: '.'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Creates a Pull Request'
|
||||
if: "inputs.dry-run != 'true'"
|
||||
env:
|
||||
GH_TOKEN: '${{ inputs.github-token }}'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
set -e
|
||||
if ! git ls-remote --exit-code --heads origin "${{ inputs.branch-name }}"; then
|
||||
echo "::error::Branch '${{ inputs.branch-name }}' does not exist on the remote repository."
|
||||
exit 1
|
||||
fi
|
||||
PR_URL=$(gh pr create \
|
||||
--title "${{ inputs.pr-title }}" \
|
||||
--body "${{ inputs.pr-body }}" \
|
||||
--base "${{ inputs.base-branch }}" \
|
||||
--head "${{ inputs.branch-name }}" \
|
||||
--fill)
|
||||
gh pr merge "$PR_URL" --auto
|
||||
@@ -0,0 +1,45 @@
|
||||
name: 'NPM Auth Token'
|
||||
description: 'Generates an NPM auth token for publishing a specific package'
|
||||
|
||||
inputs:
|
||||
package-name:
|
||||
description: 'The name of the package to publish'
|
||||
required: true
|
||||
github-token:
|
||||
description: 'the github token'
|
||||
required: true
|
||||
wombat-token-core:
|
||||
description: 'The npm token for the cli-core package.'
|
||||
required: true
|
||||
wombat-token-cli:
|
||||
description: 'The npm token for the cli package.'
|
||||
required: true
|
||||
wombat-token-a2a-server:
|
||||
description: 'The npm token for the a2a package.'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
auth-token:
|
||||
description: 'The generated NPM auth token'
|
||||
value: '${{ steps.npm_auth_token.outputs.auth-token }}'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: 'Generate NPM Auth Token'
|
||||
id: 'npm_auth_token'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
AUTH_TOKEN="${{ inputs.github-token }}"
|
||||
PACKAGE_NAME="${{ inputs.package-name }}"
|
||||
PRIVATE_REPO="@google-gemini/"
|
||||
if [[ "$PACKAGE_NAME" == "$PRIVATE_REPO"* ]]; then
|
||||
AUTH_TOKEN="${{ inputs.github-token }}"
|
||||
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli" ]]; then
|
||||
AUTH_TOKEN="${{ inputs.wombat-token-cli }}"
|
||||
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-core" ]]; then
|
||||
AUTH_TOKEN="${{ inputs.wombat-token-core }}"
|
||||
elif [[ "$PACKAGE_NAME" == "@google/gemini-cli-a2a-server" ]]; then
|
||||
AUTH_TOKEN="${{ inputs.wombat-token-a2a-server }}"
|
||||
fi
|
||||
echo "auth-token=$AUTH_TOKEN" >> $GITHUB_OUTPUT
|
||||
@@ -27,6 +27,11 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Prepare Coverage Comment'
|
||||
id: 'prep_coverage_comment'
|
||||
shell: 'bash'
|
||||
|
||||
@@ -9,10 +9,13 @@ inputs:
|
||||
description: 'The npm tag to publish with (e.g., latest, preview, nightly).'
|
||||
required: true
|
||||
wombat-token-core:
|
||||
description: 'The npm token for the @google/gemini-cli-core package.'
|
||||
description: 'The npm token for the cli-core package.'
|
||||
required: true
|
||||
wombat-token-cli:
|
||||
description: 'The npm token for the @google/gemini-cli package.'
|
||||
description: 'The npm token for the cli package.'
|
||||
required: true
|
||||
wombat-token-a2a-server:
|
||||
description: 'The npm token for the a2a package.'
|
||||
required: true
|
||||
github-token:
|
||||
description: 'The GitHub token for creating the release.'
|
||||
@@ -27,18 +30,53 @@ inputs:
|
||||
previous-tag:
|
||||
description: 'The previous tag to use for generating release notes.'
|
||||
required: true
|
||||
skip-github-release:
|
||||
description: 'Whether to skip creating a GitHub release.'
|
||||
type: 'boolean'
|
||||
required: false
|
||||
default: false
|
||||
working-directory:
|
||||
description: 'The working directory to run the steps in.'
|
||||
required: false
|
||||
default: '.'
|
||||
|
||||
force-skip-tests:
|
||||
description: 'Skip tests and validation'
|
||||
required: false
|
||||
default: false
|
||||
skip-branch-cleanup:
|
||||
description: 'Whether to skip cleaning up the release branch.'
|
||||
type: 'boolean'
|
||||
required: false
|
||||
default: false
|
||||
gemini_api_key:
|
||||
description: 'The API key for running integration tests.'
|
||||
required: true
|
||||
npm-registry-publish-url:
|
||||
description: 'npm registry publish url'
|
||||
required: true
|
||||
npm-registry-url:
|
||||
description: 'npm registry url'
|
||||
required: true
|
||||
npm-registry-scope:
|
||||
description: 'npm registry scope'
|
||||
required: true
|
||||
cli-package-name:
|
||||
description: 'The name of the cli package.'
|
||||
required: true
|
||||
core-package-name:
|
||||
description: 'The name of the core package.'
|
||||
required: true
|
||||
a2a-package-name:
|
||||
description: 'The name of the a2a package.'
|
||||
required: true
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "${{ toJSON(inputs) }}"
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: '👤 Configure Git User'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
@@ -69,14 +107,9 @@ runs:
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
DRY_RUN: '${{ inputs.dry-run }}'
|
||||
RELEASE_TAG: '${{ inputs.release-tag }}'
|
||||
run: |
|
||||
git add package.json packages/*/package.json
|
||||
if [ -f npm-shrinkwrap.json ]; then
|
||||
git add npm-shrinkwrap.json
|
||||
fi
|
||||
if [ -f package-lock.json ]; then
|
||||
git add package-lock.json
|
||||
fi
|
||||
run: |-
|
||||
set -e
|
||||
git add package.json package-lock.json packages/*/package.json
|
||||
git commit -m "chore(release): ${RELEASE_TAG}"
|
||||
if [[ "${DRY_RUN}" == "false" ]]; then
|
||||
echo "Pushing release branch to remote..."
|
||||
@@ -92,53 +125,134 @@ runs:
|
||||
npm run build:packages
|
||||
npm run prepare:package
|
||||
|
||||
- name: 'Configure npm for publishing'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: '${{ inputs.working-directory }}/.nvmrc'
|
||||
registry-url: 'https://wombat-dressing-room.appspot.com'
|
||||
scope: '@google'
|
||||
|
||||
- name: '📦 Publish @google/gemini-cli-core'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ inputs.wombat-token-core }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="@google/gemini-cli-core" \
|
||||
--tag="${{ inputs.npm-tag }}"
|
||||
|
||||
- name: '🔗 Install latest core package'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
if: "${{ inputs.dry-run == 'false' }}"
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm install "@google/gemini-cli-core@${{ inputs.release-version }}" \
|
||||
--workspace="@google/gemini-cli" \
|
||||
--save-exact
|
||||
|
||||
- name: '📦 Publish @google/gemini-cli'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ inputs.wombat-token-cli }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="@google/gemini-cli" \
|
||||
--tag="${{ inputs.npm-tag }}"
|
||||
|
||||
- name: '🎁 Bundle'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm run bundle
|
||||
|
||||
# TODO: Refactor this github specific publishing script to be generalized based upon inputs.
|
||||
- name: '📦 Prepare for GitHub release'
|
||||
if: "inputs.npm-registry-url == 'https://npm.pkg.github.com/'"
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
node ${{ github.workspace }}/scripts/prepare-github-release.js
|
||||
|
||||
- name: 'Configure npm for publishing to npm'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: '${{ inputs.working-directory }}/.nvmrc'
|
||||
registry-url: '${{inputs.npm-registry-publish-url}}'
|
||||
scope: '${{inputs.npm-registry-scope}}'
|
||||
|
||||
- name: 'Get core Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'core-token'
|
||||
with:
|
||||
package-name: '${{ inputs.core-package-name }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
|
||||
- name: '📦 Publish CORE to NPM'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="${{ inputs.core-package-name }}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${{ inputs.core-package-name }} false --silent
|
||||
|
||||
- name: '🔗 Install latest core package'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
if: "${{ inputs.dry-run != 'true' }}"
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm install "${{ inputs.core-package-name }}@${{ inputs.release-version }}" \
|
||||
--workspace="${{ inputs.cli-package-name }}" \
|
||||
--workspace="${{ inputs.a2a-package-name }}" \
|
||||
--save-exact
|
||||
|
||||
- name: 'Get CLI Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'cli-token'
|
||||
with:
|
||||
package-name: '${{ inputs.cli-package-name }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
|
||||
- name: '📦 Publish CLI'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="${{ inputs.cli-package-name }}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${{ inputs.cli-package-name }} false --silent
|
||||
|
||||
- name: 'Get a2a-server Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'a2a-token'
|
||||
with:
|
||||
package-name: '${{ inputs.a2a-package-name }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
|
||||
- name: '📦 Publish a2a'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
|
||||
shell: 'bash'
|
||||
# Tag staging for initial release
|
||||
run: |
|
||||
npm publish \
|
||||
--dry-run="${{ inputs.dry-run }}" \
|
||||
--workspace="${{ inputs.a2a-package-name }}" \
|
||||
--no-tag
|
||||
npm dist-tag rm ${{ inputs.a2a-package-name }} false --silent
|
||||
|
||||
- name: '🔬 Verify NPM release by version'
|
||||
uses: './.github/actions/verify-release'
|
||||
if: "${{ inputs.dry-run != 'true' && inputs.force-skip-tests != 'true' }}"
|
||||
with:
|
||||
npm-package: '${{ inputs.cli-package-name }}@${{ inputs.release-version }}'
|
||||
expected-version: '${{ inputs.release-version }}'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
gemini_api_key: '${{ inputs.gemini_api_key }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
npm-registry-url: '${{ inputs.npm-registry-url }}'
|
||||
npm-registry-scope: '${{ inputs.npm-registry-scope }}'
|
||||
|
||||
- name: '🏷️ Tag release'
|
||||
uses: './.github/actions/tag-npm-release'
|
||||
with:
|
||||
channel: '${{ inputs.npm-tag }}'
|
||||
version: '${{ inputs.release-version }}'
|
||||
dry-run: '${{ inputs.dry-run }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
cli-package-name: '${{ inputs.cli-package-name }}'
|
||||
core-package-name: '${{ inputs.core-package-name }}'
|
||||
a2a-package-name: '${{ inputs.a2a-package-name }}'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
|
||||
- name: '🎉 Create GitHub Release'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
if: "${{ inputs.dry-run == 'false' }}"
|
||||
if: "${{ inputs.dry-run != 'true' && inputs.skip-github-release != 'true' && inputs.npm-tag != 'dev' && inputs.npm-registry-url != 'https://npm.pkg.github.com/' }}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ inputs.github-token }}'
|
||||
shell: 'bash'
|
||||
@@ -148,4 +262,14 @@ runs:
|
||||
--target "${{ steps.release_branch.outputs.BRANCH_NAME }}" \
|
||||
--title "Release ${{ inputs.release-tag }}" \
|
||||
--notes-start-tag "${{ inputs.previous-tag }}" \
|
||||
--generate-notes
|
||||
--generate-notes \
|
||||
${{ inputs.npm-tag != 'latest' && '--prerelease' || '' }}
|
||||
|
||||
- name: '🧹 Clean up release branch'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
if: "${{ inputs.dry-run != 'true' && inputs.skip-branch-cleanup != 'true' }}"
|
||||
continue-on-error: true
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "Cleaning up release branch ${{ steps.release_branch.outputs.BRANCH_NAME }}..."
|
||||
git push origin --delete "${{ steps.release_branch.outputs.BRANCH_NAME }}"
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
name: 'Push to docker'
|
||||
description: 'Builds packages and pushes a docker image to GHCR'
|
||||
|
||||
inputs:
|
||||
github-actor:
|
||||
description: 'Github actor'
|
||||
required: true
|
||||
github-secret:
|
||||
description: 'Github secret'
|
||||
required: true
|
||||
ref-name:
|
||||
description: 'Github ref name'
|
||||
required: true
|
||||
github-sha:
|
||||
description: 'Github Commit SHA Hash'
|
||||
required: true
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
ref: '${{ inputs.github-sha }}'
|
||||
fetch-depth: 0
|
||||
- name: 'Install Dependencies'
|
||||
shell: 'bash'
|
||||
run: 'npm install'
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
|
||||
- name: 'build'
|
||||
shell: 'bash'
|
||||
run: 'npm run build'
|
||||
- name: 'pack @google/gemini-cli'
|
||||
shell: 'bash'
|
||||
run: 'npm pack -w @google/gemini-cli --pack-destination ./packages/cli/dist'
|
||||
- name: 'pack @google/gemini-cli-core'
|
||||
shell: 'bash'
|
||||
run: 'npm pack -w @google/gemini-cli-core --pack-destination ./packages/core/dist'
|
||||
- name: 'Log in to GitHub Container Registry'
|
||||
uses: 'docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1' # ratchet:docker/login-action@v3
|
||||
with:
|
||||
registry: 'ghcr.io'
|
||||
username: '${{ inputs.github-actor }}'
|
||||
password: '${{ inputs.github-secret }}'
|
||||
- name: 'Get branch name'
|
||||
id: 'branch_name'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
REF_NAME="${{ inputs.ref-name }}"
|
||||
echo "name=${REF_NAME%/merge}" >> $GITHUB_OUTPUT
|
||||
- name: 'Build and Push the Docker Image'
|
||||
uses: 'docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83' # ratchet:docker/build-push-action@v6
|
||||
with:
|
||||
context: '.'
|
||||
file: './Dockerfile'
|
||||
push: true
|
||||
provenance: false # avoid pushing 3 images to Aritfact Registry
|
||||
tags: |
|
||||
ghcr.io/${{ github.repository }}/cli:${{ steps.branch_name.outputs.name }}
|
||||
ghcr.io/${{ github.repository }}/cli:${{ inputs.github-sha }}
|
||||
- name: 'Create issue on failure'
|
||||
if: |-
|
||||
${{ failure() }}
|
||||
shell: 'bash'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ inputs.github-secret }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |-
|
||||
gh issue create \
|
||||
--title "Docker build failed" \
|
||||
--body "The docker build failed. See the full run for details: ${DETAILS_URL}" \
|
||||
--label "release-failure"
|
||||
@@ -0,0 +1,96 @@
|
||||
name: 'Build and push sandbox docker'
|
||||
description: 'Pushes sandbox docker image to container registry'
|
||||
|
||||
inputs:
|
||||
github-actor:
|
||||
description: 'Github actor'
|
||||
required: true
|
||||
github-secret:
|
||||
description: 'Github secret'
|
||||
required: true
|
||||
dockerhub-username:
|
||||
description: 'Dockerhub username'
|
||||
required: true
|
||||
dockerhub-token:
|
||||
description: 'Dockerhub PAT w/ R+W'
|
||||
required: true
|
||||
github-sha:
|
||||
description: 'Github Commit SHA Hash'
|
||||
required: true
|
||||
github-ref-name:
|
||||
description: 'Github ref name'
|
||||
required: true
|
||||
dry-run:
|
||||
description: 'Whether this is a dry run.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
with:
|
||||
ref: '${{ inputs.github-sha }}'
|
||||
fetch-depth: 0
|
||||
- name: 'Install Dependencies'
|
||||
shell: 'bash'
|
||||
run: 'npm install'
|
||||
- name: 'npm build'
|
||||
shell: 'bash'
|
||||
run: 'npm run build'
|
||||
- name: 'Set up Docker Buildx'
|
||||
uses: 'docker/setup-buildx-action@v3'
|
||||
- name: 'Log in to GitHub Container Registry'
|
||||
uses: 'docker/login-action@v3'
|
||||
with:
|
||||
registry: 'docker.io'
|
||||
username: '${{ inputs.dockerhub-username }}'
|
||||
password: '${{ inputs.dockerhub-token }}'
|
||||
- name: 'determine image tag'
|
||||
id: 'image_tag'
|
||||
shell: 'bash'
|
||||
run: |-
|
||||
SHELL_TAG_NAME="${{ inputs.github-ref-name }}"
|
||||
FINAL_TAG="${{ inputs.github-sha }}"
|
||||
if [[ "$SHELL_TAG_NAME" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.-]+)?$ ]]; then
|
||||
echo "Release detected."
|
||||
FINAL_TAG="${SHELL_TAG_NAME#v}"
|
||||
else
|
||||
echo "Development release detected. Using commit SHA as tag."
|
||||
fi
|
||||
echo "Determined image tag: $FINAL_TAG"
|
||||
echo "FINAL_TAG=$FINAL_TAG" >> $GITHUB_OUTPUT
|
||||
- name: 'build'
|
||||
id: 'docker_build'
|
||||
shell: 'bash'
|
||||
env:
|
||||
GEMINI_SANDBOX_IMAGE_TAG: '${{ steps.image_tag.outputs.FINAL_TAG }}'
|
||||
GEMINI_SANDBOX: 'docker'
|
||||
run: |-
|
||||
npm run build:sandbox -- \
|
||||
--image google/gemini-cli-sandbox:${{ steps.image_tag.outputs.FINAL_TAG }} \
|
||||
--output-file final_image_uri.txt
|
||||
echo "uri=$(cat final_image_uri.txt)" >> $GITHUB_OUTPUT
|
||||
- name: 'publish'
|
||||
shell: 'bash'
|
||||
if: "${{ inputs.dry-run != 'true' }}"
|
||||
run: |-
|
||||
docker push "${{ steps.docker_build.outputs.uri }}"
|
||||
- name: 'Create issue on failure'
|
||||
if: |-
|
||||
${{ failure() }}
|
||||
shell: 'bash'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ inputs.github-secret }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |-
|
||||
gh issue create \
|
||||
--title "Docker build failed" \
|
||||
--body "The docker build failed. See the full run for details: ${DETAILS_URL}" \
|
||||
--label "release-failure"
|
||||
@@ -2,11 +2,6 @@ name: 'Run Tests'
|
||||
description: 'Runs the preflight checks and integration tests.'
|
||||
|
||||
inputs:
|
||||
force_skip_tests:
|
||||
description: 'Whether to force skip the tests.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
gemini_api_key:
|
||||
description: 'The API key for running integration tests.'
|
||||
required: true
|
||||
@@ -18,14 +13,26 @@ inputs:
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
- name: 'Run Tests'
|
||||
if: '${{ !inputs.force_skip_tests }}'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ inputs.gemini_api_key }}'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |-
|
||||
echo "::group::Build"
|
||||
npm run build
|
||||
echo "::endgroup::"
|
||||
echo "::group::Unit Tests"
|
||||
npm run test:ci
|
||||
echo "::endgroup::"
|
||||
echo "::group::Integration Tests (no sandbox)"
|
||||
npm run test:integration:sandbox:none
|
||||
echo "::endgroup::"
|
||||
echo "::group::Integration Tests (docker sandbox)"
|
||||
npm run test:integration:sandbox:docker
|
||||
echo "::endgroup::"
|
||||
shell: 'bash'
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
name: 'Setup NPMRC'
|
||||
description: 'Sets up NPMRC with all the correct repos for readonly access.'
|
||||
|
||||
inputs:
|
||||
github-token:
|
||||
description: 'the github token'
|
||||
required: true
|
||||
|
||||
outputs:
|
||||
auth-token:
|
||||
description: 'The generated NPM auth token'
|
||||
value: '${{ steps.npm_auth_token.outputs.auth-token }}'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: 'Configure .npmrc'
|
||||
shell: 'bash'
|
||||
run: |-
|
||||
echo ""@google-gemini:registry=https://npm.pkg.github.com"" > ~/.npmrc
|
||||
echo ""//npm.pkg.github.com/:_authToken=${{ inputs.github-token }}"" >> ~/.npmrc
|
||||
echo ""@google:registry=https://wombat-dressing-room.appspot.com"" >> ~/.npmrc
|
||||
@@ -0,0 +1,125 @@
|
||||
name: 'Tag an NPM release'
|
||||
description: 'Tags a specific npm version to a specific channel.'
|
||||
|
||||
inputs:
|
||||
channel:
|
||||
description: 'NPM Channel tag'
|
||||
required: true
|
||||
version:
|
||||
description: 'version'
|
||||
required: true
|
||||
dry-run:
|
||||
description: 'Whether to run in dry-run mode.'
|
||||
required: true
|
||||
github-token:
|
||||
description: 'The GitHub token for creating the release.'
|
||||
required: true
|
||||
wombat-token-core:
|
||||
description: 'The npm token for the wombat @google/gemini-cli-core'
|
||||
required: true
|
||||
wombat-token-cli:
|
||||
description: 'The npm token for wombat @google/gemini-cli'
|
||||
required: true
|
||||
wombat-token-a2a-server:
|
||||
description: 'The npm token for the @google/gemini-cli-a2a-server package.'
|
||||
required: true
|
||||
cli-package-name:
|
||||
description: 'The name of the cli package.'
|
||||
required: true
|
||||
core-package-name:
|
||||
description: 'The name of the core package.'
|
||||
required: true
|
||||
a2a-package-name:
|
||||
description: 'The name of the a2a package.'
|
||||
required: true
|
||||
working-directory:
|
||||
description: 'The working directory to run the commands in.'
|
||||
required: false
|
||||
default: '.'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: '${{ inputs.working-directory }}/.nvmrc'
|
||||
|
||||
- name: 'configure .npmrc'
|
||||
uses: './.github/actions/setup-npmrc'
|
||||
with:
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
|
||||
- name: 'Get core Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'core-token'
|
||||
with:
|
||||
package-name: '${{ inputs.core-package-name }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
|
||||
- name: 'Change tag for CORE'
|
||||
if: |-
|
||||
${{ inputs.dry-run != 'true' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
npm dist-tag add ${{ inputs.core-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
|
||||
|
||||
- name: 'Get cli Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'cli-token'
|
||||
with:
|
||||
package-name: '${{ inputs.cli-package-name }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
|
||||
- name: 'Change tag for CLI'
|
||||
if: |-
|
||||
${{ inputs.dry-run != 'true' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
npm dist-tag add ${{ inputs.cli-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
|
||||
|
||||
- name: 'Get a2a Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'a2a-token'
|
||||
with:
|
||||
package-name: '${{ inputs.a2a-package-name }}'
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
wombat-token-core: '${{ inputs.wombat-token-core }}'
|
||||
wombat-token-cli: '${{ inputs.wombat-token-cli }}'
|
||||
wombat-token-a2a-server: '${{ inputs.wombat-token-a2a-server }}'
|
||||
|
||||
- name: 'Change tag for a2a'
|
||||
if: |-
|
||||
${{ inputs.dry-run == 'false' }}
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
npm dist-tag add ${{ inputs.a2a-package-name }}@${{ inputs.version }} ${{ inputs.channel }}
|
||||
|
||||
- name: 'Log dry run'
|
||||
if: |-
|
||||
${{ inputs.dry-run == 'true' }}
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |
|
||||
echo "Dry run: Would have added tag '${{ inputs.channel }}' to version '${{ inputs.version }}' for ${{ inputs.cli-package-name }}, ${{ inputs.core-package-name }}, and ${{ inputs.a2a-package-name }}."
|
||||
@@ -0,0 +1,100 @@
|
||||
name: 'Verify an NPM release'
|
||||
description: 'Fetches a package from NPM and does some basic smoke tests'
|
||||
|
||||
inputs:
|
||||
npm-package:
|
||||
description: 'NPM Package'
|
||||
required: true
|
||||
default: '@google/gemini-cli@latest'
|
||||
npm-registry-url:
|
||||
description: 'NPM Registry URL'
|
||||
required: true
|
||||
npm-registry-scope:
|
||||
description: 'NPM Registry Scope'
|
||||
required: true
|
||||
expected-version:
|
||||
description: 'Expected version'
|
||||
required: true
|
||||
gemini_api_key:
|
||||
description: 'The API key for running integration tests.'
|
||||
required: true
|
||||
github-token:
|
||||
description: 'The GitHub token for running integration tests.'
|
||||
required: true
|
||||
working-directory:
|
||||
description: 'The working directory to run the tests in.'
|
||||
required: false
|
||||
default: '.'
|
||||
|
||||
runs:
|
||||
using: 'composite'
|
||||
steps:
|
||||
- name: '📝 Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'setup node'
|
||||
uses: 'actions/setup-node@v4'
|
||||
with:
|
||||
node-version: '20'
|
||||
|
||||
- name: 'configure .npmrc'
|
||||
uses: './.github/actions/setup-npmrc'
|
||||
with:
|
||||
github-token: '${{ inputs.github-token }}'
|
||||
|
||||
- name: 'Clear npm cache'
|
||||
shell: 'bash'
|
||||
run: 'npm cache clean --force'
|
||||
|
||||
- name: 'Install from NPM'
|
||||
uses: 'nick-fields/retry@ce71cc2ab81d554ebbe88c79ab5975992d79ba08' # ratchet:nick-fields/retry@v3
|
||||
with:
|
||||
timeout_seconds: 900
|
||||
retry_wait_seconds: 30
|
||||
max_attempts: 10
|
||||
command: |-
|
||||
cd ${{ inputs.working-directory }}
|
||||
npm install --prefer-online --no-cache -g "${{ inputs.npm-package }}"
|
||||
|
||||
- name: 'Smoke test - NPM Install'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |-
|
||||
gemini_version=$(gemini --version)
|
||||
if [ "$gemini_version" != "${{ inputs.expected-version }}" ]; then
|
||||
echo "❌ NPM Version mismatch: Got $gemini_version from ${{ inputs.npm-package }}, expected ${{ inputs.expected-version }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 'Clear npm cache'
|
||||
shell: 'bash'
|
||||
run: 'npm cache clean --force'
|
||||
|
||||
- name: 'Smoke test - NPX Run'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: |-
|
||||
gemini_version=$(npx --prefer-online "${{ inputs.npm-package}}" --version)
|
||||
if [ "$gemini_version" != "${{ inputs.expected-version }}" ]; then
|
||||
echo "❌ NPX Run Version mismatch: Got $gemini_version from ${{ inputs.npm-package }}, expected ${{ inputs.expected-version }}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 'Install dependencies for integration tests'
|
||||
shell: 'bash'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: '🔬 Run integration tests against NPM release'
|
||||
working-directory: '${{ inputs.working-directory }}'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ inputs.gemini_api_key }}'
|
||||
INTEGRATION_TEST_USE_INSTALLED_GEMINI: 'true'
|
||||
# We must diable CI mode here because it interferes with interactive tests.
|
||||
# See https://github.com/google-gemini/gemini-cli/issues/10517
|
||||
CI: 'false'
|
||||
shell: 'bash'
|
||||
run: 'npm run test:integration:sandbox:none'
|
||||
+18
-20
@@ -1,35 +1,33 @@
|
||||
# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: 'npm'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
target-branch: 'main'
|
||||
commit-message:
|
||||
prefix: 'chore(deps)'
|
||||
include: 'scope'
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
open-pull-requests-limit: 10
|
||||
reviewers:
|
||||
- 'google-gemini/gemini-cli-askmode-approvers'
|
||||
- 'joshualitt'
|
||||
groups:
|
||||
# Group all non-major updates together.
|
||||
# This is to reduce the number of PRs that need to be reviewed.
|
||||
# Major updates will still be created as separate PRs.
|
||||
npm-minor-patch:
|
||||
applies-to: 'version-updates'
|
||||
npm-dependencies:
|
||||
patterns:
|
||||
- '*'
|
||||
update-types:
|
||||
- 'minor'
|
||||
- 'patch'
|
||||
open-pull-requests-limit: 0
|
||||
|
||||
- package-ecosystem: 'github-actions'
|
||||
directory: '/'
|
||||
schedule:
|
||||
interval: 'daily'
|
||||
target-branch: 'main'
|
||||
commit-message:
|
||||
prefix: 'chore(deps)'
|
||||
include: 'scope'
|
||||
interval: 'weekly'
|
||||
day: 'monday'
|
||||
open-pull-requests-limit: 10
|
||||
reviewers:
|
||||
- 'google-gemini/gemini-cli-askmode-approvers'
|
||||
open-pull-requests-limit: 0
|
||||
- 'joshualitt'
|
||||
groups:
|
||||
actions-dependencies:
|
||||
patterns:
|
||||
- '*'
|
||||
update-types:
|
||||
- 'minor'
|
||||
- 'patch'
|
||||
|
||||
@@ -1,41 +1,42 @@
|
||||
## TLDR
|
||||
## Summary
|
||||
|
||||
<!-- Add a brief description of what this pull request changes and why and any important things for reviewers to look at -->
|
||||
<!-- Concisely describe what this PR changes and why. Focus on impact and
|
||||
urgency. -->
|
||||
|
||||
## Dive Deeper
|
||||
## Details
|
||||
|
||||
<!-- more thoughts and in-depth discussion here -->
|
||||
<!-- Add any extra context and design decisions. Keep it brief but complete. -->
|
||||
|
||||
## Reviewer Test Plan
|
||||
## Related Issues
|
||||
|
||||
<!-- when a person reviews your code they should ideally be pulling and running that code. How would they validate your change works and if relevant what are some good classes of example prompts and ways they can exercise your changes -->
|
||||
<!-- Use keywords to auto-close issues (Closes #123, Fixes #456). If this PR is
|
||||
only related to an issue or is a partial fix, simply reference the issue number
|
||||
without a keyword (Related to #123). -->
|
||||
|
||||
## Testing Matrix
|
||||
## How to Validate
|
||||
|
||||
<!-- Before submitting please validate your changes on as many of these options as possible -->
|
||||
<!-- List exact steps for reviewers to validate the change. Include commands,
|
||||
expected results, and edge cases. -->
|
||||
|
||||
| | 🍏 | 🪟 | 🐧 |
|
||||
| -------- | --- | --- | --- |
|
||||
| npm run | ❓ | ❓ | ❓ |
|
||||
| npx | ❓ | ❓ | ❓ |
|
||||
| Docker | ❓ | ❓ | ❓ |
|
||||
| Podman | ❓ | - | - |
|
||||
| Seatbelt | ❓ | - | - |
|
||||
## Pre-Merge Checklist
|
||||
|
||||
## Linked issues / bugs
|
||||
<!-- Check all that apply before requesting review or merging. -->
|
||||
|
||||
<!--
|
||||
Link to any related issues or bugs.
|
||||
|
||||
**If this PR fully resolves the issue, use one of the following keywords to automatically close the issue when this PR is merged:**
|
||||
|
||||
- Closes #<issue_number>
|
||||
- Fixes #<issue_number>
|
||||
- Resolves #<issue_number>
|
||||
|
||||
*Example: `Resolves #123`*
|
||||
|
||||
**If this PR is only related to an issue or is a partial fix, simply reference the issue number without a keyword:**
|
||||
|
||||
*Example: `This PR makes progress on #456` or `Related to #789`*
|
||||
-->
|
||||
- [ ] Updated relevant documentation and README (if needed)
|
||||
- [ ] Added/updated tests (if needed)
|
||||
- [ ] Noted breaking changes (if any)
|
||||
- [ ] Validated on required platforms/methods:
|
||||
- [ ] MacOS
|
||||
- [ ] npm run
|
||||
- [ ] npx
|
||||
- [ ] Docker
|
||||
- [ ] Podman
|
||||
- [ ] Seatbelt
|
||||
- [ ] Windows
|
||||
- [ ] npm run
|
||||
- [ ] npx
|
||||
- [ ] Docker
|
||||
- [ ] Linux
|
||||
- [ ] npm run
|
||||
- [ ] npx
|
||||
- [ ] Docker
|
||||
|
||||
@@ -0,0 +1,138 @@
|
||||
/* eslint-disable */
|
||||
/* global require, console, process */
|
||||
|
||||
/**
|
||||
* Script to backfill the 'status/need-triage' label to all open issues
|
||||
* that are NOT currently labeled with '🔒 maintainer only' or 'help wanted'.
|
||||
*/
|
||||
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const isDryRun = process.argv.includes('--dry-run');
|
||||
const REPO = 'google-gemini/gemini-cli';
|
||||
|
||||
/**
|
||||
* Executes a GitHub CLI command safely using an argument array to prevent command injection.
|
||||
* @param {string[]} args
|
||||
* @returns {string|null}
|
||||
*/
|
||||
function runGh(args) {
|
||||
try {
|
||||
// Using execFileSync with an array of arguments is safe as it doesn't use a shell.
|
||||
// We set a large maxBuffer (10MB) to handle repositories with many issues.
|
||||
return execFileSync('gh', args, {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}).trim();
|
||||
} catch (error) {
|
||||
const stderr = error.stderr ? ` Stderr: ${error.stderr.trim()}` : '';
|
||||
console.error(
|
||||
`❌ Error running gh ${args.join(' ')}: ${error.message}${stderr}`,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🔐 GitHub CLI security check...');
|
||||
const authStatus = runGh(['auth', 'status']);
|
||||
if (authStatus === null) {
|
||||
console.error('❌ GitHub CLI (gh) is not installed or not authenticated.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
console.log('🧪 DRY RUN MODE ENABLED - No changes will be made.\n');
|
||||
}
|
||||
|
||||
console.log(`🔍 Fetching and filtering open issues from ${REPO}...`);
|
||||
|
||||
// We use the /issues endpoint with pagination to bypass the 1000-result limit.
|
||||
// The jq filter ensures we exclude PRs, maintainer-only, help-wanted, and existing status/need-triage.
|
||||
const jqFilter =
|
||||
'.[] | select(.pull_request == null) | select([.labels[].name] as $l | (any($l[]; . == "🔒 maintainer only") | not) and (any($l[]; . == "help wanted") | not) and (any($l[]; . == "status/need-triage") | not)) | {number: .number, title: .title}';
|
||||
|
||||
const output = runGh([
|
||||
'api',
|
||||
`repos/${REPO}/issues?state=open&per_page=100`,
|
||||
'--paginate',
|
||||
'--jq',
|
||||
jqFilter,
|
||||
]);
|
||||
|
||||
if (output === null) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const issues = output
|
||||
.split('\n')
|
||||
.filter((line) => line.trim())
|
||||
.map((line) => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch (_e) {
|
||||
console.error(`⚠️ Failed to parse line: ${line}`);
|
||||
return null;
|
||||
}
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
console.log(`✅ Found ${issues.length} issues matching criteria.`);
|
||||
|
||||
if (issues.length === 0) {
|
||||
console.log('✨ No issues need backfilling.');
|
||||
return;
|
||||
}
|
||||
|
||||
let successCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
if (isDryRun) {
|
||||
for (const issue of issues) {
|
||||
console.log(
|
||||
`[DRY RUN] Would label issue #${issue.number}: ${issue.title}`,
|
||||
);
|
||||
}
|
||||
successCount = issues.length;
|
||||
} else {
|
||||
console.log(`🏷️ Applying labels to ${issues.length} issues...`);
|
||||
|
||||
for (const issue of issues) {
|
||||
const issueNumber = String(issue.number);
|
||||
console.log(`🏷️ Labeling issue #${issueNumber}: ${issue.title}`);
|
||||
|
||||
const result = runGh([
|
||||
'issue',
|
||||
'edit',
|
||||
issueNumber,
|
||||
'--add-label',
|
||||
'status/need-triage',
|
||||
'--repo',
|
||||
REPO,
|
||||
]);
|
||||
|
||||
if (result !== null) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n📊 Summary:`);
|
||||
console.log(` - Success: ${successCount}`);
|
||||
console.log(` - Failed: ${failCount}`);
|
||||
|
||||
if (failCount > 0) {
|
||||
console.error(`\n❌ Backfill completed with ${failCount} errors.`);
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log(`\n🎉 ${isDryRun ? 'Dry run' : 'Backfill'} complete!`);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch((error) => {
|
||||
console.error('❌ Unexpected error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2026 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable */
|
||||
/* global require, console, process */
|
||||
|
||||
/**
|
||||
* Script to backfill a process change notification comment to all open PRs
|
||||
* not created by members of the 'gemini-cli-maintainers' team.
|
||||
*
|
||||
* Skip PRs that are already associated with an issue.
|
||||
*/
|
||||
|
||||
const { execFileSync } = require('child_process');
|
||||
|
||||
const isDryRun = process.argv.includes('--dry-run');
|
||||
const REPO = 'google-gemini/gemini-cli';
|
||||
const ORG = 'google-gemini';
|
||||
const TEAM_SLUG = 'gemini-cli-maintainers';
|
||||
const DISCUSSION_URL =
|
||||
'https://github.com/google-gemini/gemini-cli/discussions/16706';
|
||||
|
||||
/**
|
||||
* Executes a GitHub CLI command safely using an argument array.
|
||||
*/
|
||||
function runGh(args, options = {}) {
|
||||
const { silent = false } = options;
|
||||
try {
|
||||
return execFileSync('gh', args, {
|
||||
encoding: 'utf8',
|
||||
maxBuffer: 10 * 1024 * 1024,
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
}).trim();
|
||||
} catch (error) {
|
||||
if (!silent) {
|
||||
const stderr = error.stderr ? ` Stderr: ${error.stderr.trim()}` : '';
|
||||
console.error(
|
||||
`❌ Error running gh ${args.join(' ')}: ${error.message}${stderr}`,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a user is a member of the maintainers team.
|
||||
*/
|
||||
const membershipCache = new Map();
|
||||
function isMaintainer(username) {
|
||||
if (membershipCache.has(username)) return membershipCache.get(username);
|
||||
|
||||
// GitHub returns 404 if user is not a member.
|
||||
// We use silent: true to avoid logging 404s as errors.
|
||||
const result = runGh(
|
||||
['api', `orgs/${ORG}/teams/${TEAM_SLUG}/memberships/${username}`],
|
||||
{ silent: true },
|
||||
);
|
||||
|
||||
const isMember = result !== null;
|
||||
membershipCache.set(username, isMember);
|
||||
return isMember;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
console.log('🔐 GitHub CLI security check...');
|
||||
if (runGh(['auth', 'status']) === null) {
|
||||
console.error('❌ GitHub CLI (gh) is not authenticated.');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
console.log('🧪 DRY RUN MODE ENABLED\n');
|
||||
}
|
||||
|
||||
console.log(`📥 Fetching open PRs from ${REPO}...`);
|
||||
// Fetch number, author, and closingIssuesReferences to check if linked to an issue
|
||||
const prsJson = runGh([
|
||||
'pr',
|
||||
'list',
|
||||
'--repo',
|
||||
REPO,
|
||||
'--state',
|
||||
'open',
|
||||
'--limit',
|
||||
'1000',
|
||||
'--json',
|
||||
'number,author,closingIssuesReferences',
|
||||
]);
|
||||
|
||||
if (prsJson === null) process.exit(1);
|
||||
const prs = JSON.parse(prsJson);
|
||||
|
||||
console.log(`📊 Found ${prs.length} open PRs. Filtering...`);
|
||||
|
||||
let targetPrs = [];
|
||||
for (const pr of prs) {
|
||||
const author = pr.author.login;
|
||||
const issueCount = pr.closingIssuesReferences
|
||||
? pr.closingIssuesReferences.length
|
||||
: 0;
|
||||
|
||||
if (issueCount > 0) {
|
||||
// Skip if already linked to an issue
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isMaintainer(author)) {
|
||||
targetPrs.push(pr);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(
|
||||
`✅ Found ${targetPrs.length} PRs from non-maintainers without associated issues.`,
|
||||
);
|
||||
|
||||
const commentBody =
|
||||
"\nHi @{AUTHOR}, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.\n\nWe're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: [Improving Our Contribution Process & Introducing New Guidelines](${DISCUSSION_URL}).\n\nKey Update: Starting **January 26, 2026**, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.\n\nThank you for your understanding and for being a part of our community!\n ".trim();
|
||||
|
||||
let successCount = 0;
|
||||
let skipCount = 0;
|
||||
let failCount = 0;
|
||||
|
||||
for (const pr of targetPrs) {
|
||||
const prNumber = String(pr.number);
|
||||
const author = pr.author.login;
|
||||
|
||||
// Check if we already commented (idempotency)
|
||||
// We use silent: true here because view might fail if PR is deleted mid-run
|
||||
const existingComments = runGh(
|
||||
[
|
||||
'pr',
|
||||
'view',
|
||||
prNumber,
|
||||
'--repo',
|
||||
REPO,
|
||||
'--json',
|
||||
'comments',
|
||||
'--jq',
|
||||
`.comments[].body | contains("${DISCUSSION_URL}")`,
|
||||
],
|
||||
{ silent: true },
|
||||
);
|
||||
|
||||
if (existingComments && existingComments.includes('true')) {
|
||||
console.log(
|
||||
`⏭️ PR #${prNumber} already has the notification. Skipping.`,
|
||||
);
|
||||
skipCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isDryRun) {
|
||||
console.log(`[DRY RUN] Would notify @${author} on PR #${prNumber}`);
|
||||
successCount++;
|
||||
} else {
|
||||
console.log(`💬 Notifying @${author} on PR #${prNumber}...`);
|
||||
const personalizedComment = commentBody.replace('{AUTHOR}', author);
|
||||
const result = runGh([
|
||||
'pr',
|
||||
'comment',
|
||||
prNumber,
|
||||
'--repo',
|
||||
REPO,
|
||||
'--body',
|
||||
personalizedComment,
|
||||
]);
|
||||
|
||||
if (result !== null) {
|
||||
successCount++;
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n📊 Summary:`);
|
||||
console.log(` - Notified: ${successCount}`);
|
||||
console.log(` - Skipped: ${skipCount}`);
|
||||
console.log(` - Failed: ${failCount}`);
|
||||
|
||||
if (failCount > 0) process.exit(1);
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
});
|
||||
+155
-108
@@ -1,133 +1,180 @@
|
||||
#!/usr/bin/env bash
|
||||
# @license
|
||||
# Copyright 2026 Google LLC
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# Initialize a comma-separated string to hold PR numbers that need a comment
|
||||
PRS_NEEDING_COMMENT=""
|
||||
|
||||
# Function to process a single PR
|
||||
process_pr() {
|
||||
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
|
||||
echo "‼️ Missing \$GITHUB_REPOSITORY - this must be run from GitHub Actions"
|
||||
return 1
|
||||
# Global cache for issue labels (compatible with Bash 3.2)
|
||||
# Stores "ISSUE_NUM:LABELS" pairs separated by spaces
|
||||
ISSUE_LABELS_CACHE_FLAT=""
|
||||
|
||||
# Function to get area and priority labels from an issue (with caching)
|
||||
get_issue_labels() {
|
||||
local ISSUE_NUM="${1}"
|
||||
if [[ -z "${ISSUE_NUM}" || "${ISSUE_NUM}" == "null" || "${ISSUE_NUM}" == "" ]]; then
|
||||
return
|
||||
fi
|
||||
|
||||
if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
|
||||
echo "‼️ Missing \$GITHUB_OUTPUT - this must be run from GitHub Actions"
|
||||
return 1
|
||||
# Check cache
|
||||
case " ${ISSUE_LABELS_CACHE_FLAT} " in
|
||||
*" ${ISSUE_NUM}:"*)
|
||||
local suffix="${ISSUE_LABELS_CACHE_FLAT#* " ${ISSUE_NUM}:"}"
|
||||
echo "${suffix%% *}"
|
||||
return
|
||||
;;
|
||||
*)
|
||||
# Cache miss, proceed to fetch
|
||||
;;
|
||||
esac
|
||||
|
||||
echo " 📥 Fetching area and priority labels from issue #${ISSUE_NUM}" >&2
|
||||
local gh_output
|
||||
if ! gh_output=$(gh issue view "${ISSUE_NUM}" --repo "${GITHUB_REPOSITORY}" --json labels -q '.labels[].name' 2>/dev/null); then
|
||||
echo " ⚠️ Could not fetch issue #${ISSUE_NUM}" >&2
|
||||
ISSUE_LABELS_CACHE_FLAT="${ISSUE_LABELS_CACHE_FLAT} ${ISSUE_NUM}:"
|
||||
return
|
||||
fi
|
||||
|
||||
local PR_NUMBER=$1
|
||||
local labels
|
||||
labels=$(echo "${gh_output}" | grep -E '^(area|priority)/' | tr '\n' ',' | sed 's/,$//' || echo "")
|
||||
|
||||
# Save to flat cache
|
||||
ISSUE_LABELS_CACHE_FLAT="${ISSUE_LABELS_CACHE_FLAT} ${ISSUE_NUM}:${labels}"
|
||||
echo "${labels}"
|
||||
}
|
||||
|
||||
# Function to process a single PR with pre-fetched data
|
||||
process_pr_optimized() {
|
||||
local PR_NUMBER="${1}"
|
||||
local IS_DRAFT="${2}"
|
||||
local ISSUE_NUMBER="${3}"
|
||||
local CURRENT_LABELS="${4}" # Comma-separated labels
|
||||
|
||||
echo "🔄 Processing PR #${PR_NUMBER}"
|
||||
|
||||
# Get closing issue number with error handling
|
||||
local ISSUE_NUMBER
|
||||
if ! ISSUE_NUMBER=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json closingIssuesReferences -q '.closingIssuesReferences.nodes[0].number' 2>/dev/null); then
|
||||
echo " ⚠️ Could not fetch closing issue for PR #${PR_NUMBER}"
|
||||
local LABELS_TO_ADD=""
|
||||
local LABELS_TO_REMOVE=""
|
||||
|
||||
if [[ -z "${ISSUE_NUMBER}" || "${ISSUE_NUMBER}" == "null" || "${ISSUE_NUMBER}" == "" ]]; then
|
||||
if [[ "${IS_DRAFT}" == "true" ]]; then
|
||||
echo " 📝 PR #${PR_NUMBER} is a draft and has no linked issue"
|
||||
if [[ ",${CURRENT_LABELS}," == *",status/need-issue,"* ]]; then
|
||||
echo " ➖ Removing status/need-issue label"
|
||||
LABELS_TO_REMOVE="status/need-issue"
|
||||
fi
|
||||
else
|
||||
echo " ⚠️ No linked issue found for PR #${PR_NUMBER}"
|
||||
if [[ ",${CURRENT_LABELS}," != *",status/need-issue,"* ]]; then
|
||||
echo " ➕ Adding status/need-issue label"
|
||||
LABELS_TO_ADD="status/need-issue"
|
||||
fi
|
||||
|
||||
if [[ -z "${PRS_NEEDING_COMMENT}" ]]; then
|
||||
PRS_NEEDING_COMMENT="${PR_NUMBER}"
|
||||
else
|
||||
PRS_NEEDING_COMMENT="${PRS_NEEDING_COMMENT},${PR_NUMBER}"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
echo " 🔗 Found linked issue #${ISSUE_NUMBER}"
|
||||
|
||||
if [[ ",${CURRENT_LABELS}," == *",status/need-issue,"* ]]; then
|
||||
echo " ➖ Removing status/need-issue label"
|
||||
LABELS_TO_REMOVE="status/need-issue"
|
||||
fi
|
||||
|
||||
local ISSUE_LABELS
|
||||
ISSUE_LABELS=$(get_issue_labels "${ISSUE_NUMBER}")
|
||||
|
||||
if [[ -n "${ISSUE_LABELS}" ]]; then
|
||||
local IFS_OLD="${IFS}"
|
||||
IFS=','
|
||||
for label in ${ISSUE_LABELS}; do
|
||||
if [[ -n "${label}" ]] && [[ ",${CURRENT_LABELS}," != *",${label},"* ]]; then
|
||||
if [[ -z "${LABELS_TO_ADD}" ]]; then
|
||||
LABELS_TO_ADD="${label}"
|
||||
else
|
||||
LABELS_TO_ADD="${LABELS_TO_ADD},${label}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
IFS="${IFS_OLD}"
|
||||
fi
|
||||
|
||||
if [[ -z "${LABELS_TO_ADD}" && -z "${LABELS_TO_REMOVE}" ]]; then
|
||||
echo " ✅ Labels already synchronized"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${ISSUE_NUMBER}" ]]; then
|
||||
echo "⚠️ No linked issue found for PR #${PR_NUMBER}, adding status/need-issue label"
|
||||
if ! gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --add-label "status/need-issue" 2>/dev/null; then
|
||||
echo " ⚠️ Failed to add label (may already exist or have permission issues)"
|
||||
fi
|
||||
# Add PR number to the list
|
||||
if [[ -z "${PRS_NEEDING_COMMENT}" ]]; then
|
||||
PRS_NEEDING_COMMENT="${PR_NUMBER}"
|
||||
else
|
||||
PRS_NEEDING_COMMENT="${PRS_NEEDING_COMMENT},${PR_NUMBER}"
|
||||
fi
|
||||
echo "needs_comment=true" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
echo "🔗 Found linked issue #${ISSUE_NUMBER}"
|
||||
|
||||
# Remove status/need-issue label if present
|
||||
if ! gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --remove-label "status/need-issue" 2>/dev/null; then
|
||||
echo " status/need-issue label not present or could not be removed"
|
||||
fi
|
||||
|
||||
# Get issue labels
|
||||
echo "📥 Fetching labels from issue #${ISSUE_NUMBER}"
|
||||
local ISSUE_LABELS=""
|
||||
if ! ISSUE_LABELS=$(gh issue view "${ISSUE_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json labels -q '.labels[].name' 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo ""); then
|
||||
echo " ⚠️ Could not fetch issue #${ISSUE_NUMBER} (may not exist or be in different repo)"
|
||||
ISSUE_LABELS=""
|
||||
fi
|
||||
|
||||
# Get PR labels
|
||||
echo "📥 Fetching labels from PR #${PR_NUMBER}"
|
||||
local PR_LABELS=""
|
||||
if ! PR_LABELS=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json labels -q '.labels[].name' 2>/dev/null | tr '\n' ',' | sed 's/,$//' || echo ""); then
|
||||
echo " ⚠️ Could not fetch PR labels"
|
||||
PR_LABELS=""
|
||||
fi
|
||||
|
||||
echo " Issue labels: ${ISSUE_LABELS}"
|
||||
echo " PR labels: ${PR_LABELS}"
|
||||
|
||||
# Convert comma-separated strings to arrays
|
||||
local ISSUE_LABEL_ARRAY PR_LABEL_ARRAY
|
||||
IFS=',' read -ra ISSUE_LABEL_ARRAY <<< "${ISSUE_LABELS}"
|
||||
IFS=',' read -ra PR_LABEL_ARRAY <<< "${PR_LABELS}"
|
||||
|
||||
# Find labels to add (on issue but not on PR)
|
||||
local LABELS_TO_ADD=""
|
||||
for label in "${ISSUE_LABEL_ARRAY[@]}"; do
|
||||
if [[ -n "${label}" ]] && [[ " ${PR_LABEL_ARRAY[*]} " != *" ${label} "* ]]; then
|
||||
if [[ -z "${LABELS_TO_ADD}" ]]; then
|
||||
LABELS_TO_ADD="${label}"
|
||||
else
|
||||
LABELS_TO_ADD="${LABELS_TO_ADD},${label}"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Apply label changes
|
||||
if [[ -n "${LABELS_TO_ADD}" || -n "${LABELS_TO_REMOVE}" ]]; then
|
||||
local EDIT_CMD=("gh" "pr" "edit" "${PR_NUMBER}" "--repo" "${GITHUB_REPOSITORY}")
|
||||
if [[ -n "${LABELS_TO_ADD}" ]]; then
|
||||
echo "➕ Adding labels: ${LABELS_TO_ADD}"
|
||||
if ! gh pr edit "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --add-label "${LABELS_TO_ADD}" 2>/dev/null; then
|
||||
echo " ⚠️ Failed to add some labels"
|
||||
fi
|
||||
echo " ➕ Syncing labels to add: ${LABELS_TO_ADD}"
|
||||
EDIT_CMD+=("--add-label" "${LABELS_TO_ADD}")
|
||||
fi
|
||||
|
||||
if [[ -z "${LABELS_TO_ADD}" ]]; then
|
||||
echo "✅ Labels already synchronized"
|
||||
if [[ -n "${LABELS_TO_REMOVE}" ]]; then
|
||||
echo " ➖ Syncing labels to remove: ${LABELS_TO_REMOVE}"
|
||||
EDIT_CMD+=("--remove-label" "${LABELS_TO_REMOVE}")
|
||||
fi
|
||||
echo "needs_comment=false" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
("${EDIT_CMD[@]}" 2>/dev/null || true)
|
||||
fi
|
||||
}
|
||||
|
||||
# If PR_NUMBER is set, process only that PR
|
||||
if [[ -n "${PR_NUMBER:-}" ]]; then
|
||||
if ! process_pr "${PR_NUMBER}"; then
|
||||
echo "❌ Failed to process PR #${PR_NUMBER}"
|
||||
exit 1
|
||||
fi
|
||||
else
|
||||
# Otherwise, get all open PRs and process them
|
||||
# The script logic will determine which ones need issue linking or label sync
|
||||
echo "📥 Getting all open pull requests..."
|
||||
if ! PR_NUMBERS=$(gh pr list --repo "${GITHUB_REPOSITORY}" --state open --limit 1000 --json number -q '.[].number' 2>/dev/null); then
|
||||
echo "❌ Failed to fetch PR list"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${PR_NUMBERS}" ]]; then
|
||||
echo "✅ No open PRs found"
|
||||
else
|
||||
# Count the number of PRs
|
||||
PR_COUNT=$(echo "${PR_NUMBERS}" | wc -w | tr -d ' ')
|
||||
echo "📊 Found ${PR_COUNT} open PRs to process"
|
||||
|
||||
for pr_number in ${PR_NUMBERS}; do
|
||||
if ! process_pr "${pr_number}"; then
|
||||
echo "⚠️ Failed to process PR #${pr_number}, continuing with next PR..."
|
||||
continue
|
||||
fi
|
||||
done
|
||||
fi
|
||||
if [[ -z "${GITHUB_REPOSITORY:-}" ]]; then
|
||||
echo "‼️ Missing \$GITHUB_REPOSITORY - this must be run from GitHub Actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ -z "${GITHUB_OUTPUT:-}" ]]; then
|
||||
echo "‼️ Missing \$GITHUB_OUTPUT - this must be run from GitHub Actions"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
JQ_EXTRACT_FIELDS='{
|
||||
number: .number,
|
||||
isDraft: .isDraft,
|
||||
issue: (.closingIssuesReferences[0].number // (.body // "" | capture("(^|[^a-zA-Z0-9])#(?<num>[0-9]+)([^a-zA-Z0-9]|$)")? | .num) // "null"),
|
||||
labels: [.labels[].name] | join(",")
|
||||
}'
|
||||
|
||||
JQ_TSV_FORMAT='"\((.number | tostring))\t\(.isDraft)\t\((.issue // null) | tostring)\t\(.labels)"'
|
||||
|
||||
if [[ -n "${PR_NUMBER:-}" ]]; then
|
||||
echo "🔄 Processing single PR #${PR_NUMBER}"
|
||||
PR_DATA=$(gh pr view "${PR_NUMBER}" --repo "${GITHUB_REPOSITORY}" --json number,closingIssuesReferences,isDraft,body,labels 2>/dev/null) || {
|
||||
echo "❌ Failed to fetch data for PR #${PR_NUMBER}"
|
||||
exit 1
|
||||
}
|
||||
|
||||
line=$(echo "${PR_DATA}" | jq -r "${JQ_EXTRACT_FIELDS} | ${JQ_TSV_FORMAT}")
|
||||
IFS=$'\t' read -r pr_num is_draft issue_num current_labels <<< "${line}"
|
||||
process_pr_optimized "${pr_num}" "${is_draft}" "${issue_num}" "${current_labels}"
|
||||
else
|
||||
echo "📥 Getting all open pull requests..."
|
||||
PR_DATA_ALL=$(gh pr list --repo "${GITHUB_REPOSITORY}" --state open --limit 1000 --json number,closingIssuesReferences,isDraft,body,labels 2>/dev/null) || {
|
||||
echo "❌ Failed to fetch PR list"
|
||||
exit 1
|
||||
}
|
||||
|
||||
PR_COUNT=$(echo "${PR_DATA_ALL}" | jq '. | length')
|
||||
echo "📊 Found ${PR_COUNT} open PRs to process"
|
||||
|
||||
# Use a temporary file to avoid masking exit codes in process substitution
|
||||
tmp_file=$(mktemp)
|
||||
echo "${PR_DATA_ALL}" | jq -r ".[] | ${JQ_EXTRACT_FIELDS} | ${JQ_TSV_FORMAT}" > "${tmp_file}"
|
||||
while read -r line; do
|
||||
[[ -z "${line}" ]] && continue
|
||||
IFS=$'\t' read -r pr_num is_draft issue_num current_labels <<< "${line}"
|
||||
process_pr_optimized "${pr_num}" "${is_draft}" "${issue_num}" "${current_labels}"
|
||||
done < "${tmp_file}"
|
||||
rm -f "${tmp_file}"
|
||||
fi
|
||||
|
||||
# Ensure output is always set, even if empty
|
||||
if [[ -z "${PRS_NEEDING_COMMENT}" ]]; then
|
||||
echo "prs_needing_comment=[]" >> "${GITHUB_OUTPUT}"
|
||||
else
|
||||
|
||||
@@ -0,0 +1,355 @@
|
||||
/* eslint-disable @typescript-eslint/no-require-imports */
|
||||
/* global process, console, require */
|
||||
const { Octokit } = require('@octokit/rest');
|
||||
|
||||
/**
|
||||
* Sync Maintainer Labels (Recursive with strict parent-child relationship detection)
|
||||
* - Uses Native Sub-issues.
|
||||
* - Uses Markdown Task Lists (- [ ] #123).
|
||||
* - Filters for OPEN issues only.
|
||||
* - Skips DUPLICATES.
|
||||
* - Skips Pull Requests.
|
||||
* - ONLY labels issues in the PUBLIC (gemini-cli) repo.
|
||||
*/
|
||||
|
||||
const REPO_OWNER = 'google-gemini';
|
||||
const PUBLIC_REPO = 'gemini-cli';
|
||||
const PRIVATE_REPO = 'maintainers-gemini-cli';
|
||||
const ALLOWED_REPOS = [PUBLIC_REPO, PRIVATE_REPO];
|
||||
|
||||
const ROOT_ISSUES = [
|
||||
{ owner: REPO_OWNER, repo: PUBLIC_REPO, number: 15374 },
|
||||
{ owner: REPO_OWNER, repo: PUBLIC_REPO, number: 15456 },
|
||||
{ owner: REPO_OWNER, repo: PUBLIC_REPO, number: 15324 },
|
||||
];
|
||||
|
||||
const TARGET_LABEL = '🔒 maintainer only';
|
||||
const isDryRun =
|
||||
process.argv.includes('--dry-run') || process.env.DRY_RUN === 'true';
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: process.env.GITHUB_TOKEN,
|
||||
});
|
||||
|
||||
/**
|
||||
* Extracts child issue references from markdown Task Lists ONLY.
|
||||
* e.g. - [ ] #123 or - [x] google-gemini/gemini-cli#123
|
||||
*/
|
||||
function extractTaskListLinks(text, contextOwner, contextRepo) {
|
||||
if (!text) return [];
|
||||
const childIssues = new Map();
|
||||
|
||||
const add = (owner, repo, number) => {
|
||||
if (ALLOWED_REPOS.includes(repo)) {
|
||||
const key = `${owner}/${repo}#${number}`;
|
||||
childIssues.set(key, { owner, repo, number: parseInt(number, 10) });
|
||||
}
|
||||
};
|
||||
|
||||
// 1. Full URLs in task lists
|
||||
const urlRegex =
|
||||
/-\s+\[[ x]\].*https:\/\/github\.com\/([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)\/issues\/(\d+)\b/g;
|
||||
let match;
|
||||
while ((match = urlRegex.exec(text)) !== null) {
|
||||
add(match[1], match[2], match[3]);
|
||||
}
|
||||
|
||||
// 2. Cross-repo refs in task lists: owner/repo#123
|
||||
const crossRepoRegex =
|
||||
/-\s+\[[ x]\].*([a-zA-Z0-9._-]+)\/([a-zA-Z0-9._-]+)#(\d+)\b/g;
|
||||
while ((match = crossRepoRegex.exec(text)) !== null) {
|
||||
add(match[1], match[2], match[3]);
|
||||
}
|
||||
|
||||
// 3. Short refs in task lists: #123
|
||||
const shortRefRegex = /-\s+\[[ x]\].*#(\d+)\b/g;
|
||||
while ((match = shortRefRegex.exec(text)) !== null) {
|
||||
add(contextOwner, contextRepo, match[1]);
|
||||
}
|
||||
|
||||
return Array.from(childIssues.values());
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches issue data via GraphQL with full pagination for sub-issues, comments, and labels.
|
||||
*/
|
||||
async function fetchIssueData(owner, repo, number) {
|
||||
const query = `
|
||||
query($owner:String!, $repo:String!, $number:Int!) {
|
||||
repository(owner:$owner, name:$repo) {
|
||||
issue(number:$number) {
|
||||
state
|
||||
title
|
||||
body
|
||||
labels(first: 100) {
|
||||
nodes { name }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
subIssues(first: 100) {
|
||||
nodes {
|
||||
number
|
||||
repository {
|
||||
name
|
||||
owner { login }
|
||||
}
|
||||
}
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
comments(first: 100) {
|
||||
nodes {
|
||||
body
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
try {
|
||||
const response = await octokit.graphql(query, { owner, repo, number });
|
||||
const data = response.repository.issue;
|
||||
if (!data) return null;
|
||||
|
||||
const issue = {
|
||||
state: data.state,
|
||||
title: data.title,
|
||||
body: data.body || '',
|
||||
labels: data.labels.nodes.map((n) => n.name),
|
||||
subIssues: [...data.subIssues.nodes],
|
||||
comments: data.comments.nodes.map((n) => n.body),
|
||||
};
|
||||
|
||||
// Paginate subIssues if there are more than 100
|
||||
if (data.subIssues.pageInfo.hasNextPage) {
|
||||
const moreSubIssues = await paginateConnection(
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
'subIssues',
|
||||
'number repository { name owner { login } }',
|
||||
data.subIssues.pageInfo.endCursor,
|
||||
);
|
||||
issue.subIssues.push(...moreSubIssues);
|
||||
}
|
||||
|
||||
// Paginate labels if there are more than 100 (unlikely but for completeness)
|
||||
if (data.labels.pageInfo.hasNextPage) {
|
||||
const moreLabels = await paginateConnection(
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
'labels',
|
||||
'name',
|
||||
data.labels.pageInfo.endCursor,
|
||||
(n) => n.name,
|
||||
);
|
||||
issue.labels.push(...moreLabels);
|
||||
}
|
||||
|
||||
// Note: Comments are handled via Task Lists in body + first 100 comments.
|
||||
// If an issue has > 100 comments with task lists, we'd need to paginate those too.
|
||||
// Given the 1,100+ issue discovery count, 100 comments is usually sufficient,
|
||||
// but we can add it for absolute completeness.
|
||||
// (Skipping for now to avoid excessive API churn unless clearly needed).
|
||||
|
||||
return issue;
|
||||
} catch (error) {
|
||||
if (error.errors && error.errors.some((e) => e.type === 'NOT_FOUND')) {
|
||||
return null;
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to paginate any GraphQL connection.
|
||||
*/
|
||||
async function paginateConnection(
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
connectionName,
|
||||
nodeFields,
|
||||
initialCursor,
|
||||
transformNode = (n) => n,
|
||||
) {
|
||||
let additionalNodes = [];
|
||||
let hasNext = true;
|
||||
let cursor = initialCursor;
|
||||
|
||||
while (hasNext) {
|
||||
const query = `
|
||||
query($owner:String!, $repo:String!, $number:Int!, $cursor:String) {
|
||||
repository(owner:$owner, name:$repo) {
|
||||
issue(number:$number) {
|
||||
${connectionName}(first: 100, after: $cursor) {
|
||||
nodes { ${nodeFields} }
|
||||
pageInfo { hasNextPage endCursor }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const response = await octokit.graphql(query, {
|
||||
owner,
|
||||
repo,
|
||||
number,
|
||||
cursor,
|
||||
});
|
||||
const connection = response.repository.issue[connectionName];
|
||||
additionalNodes.push(...connection.nodes.map(transformNode));
|
||||
hasNext = connection.pageInfo.hasNextPage;
|
||||
cursor = connection.pageInfo.endCursor;
|
||||
}
|
||||
return additionalNodes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates if an issue should be processed (Open, not a duplicate, not a PR)
|
||||
*/
|
||||
function shouldProcess(issueData) {
|
||||
if (!issueData) return false;
|
||||
|
||||
if (issueData.state !== 'OPEN') return false;
|
||||
|
||||
const labels = issueData.labels.map((l) => l.toLowerCase());
|
||||
if (labels.includes('duplicate') || labels.includes('kind/duplicate')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
async function getAllDescendants(roots) {
|
||||
const allDescendants = new Map();
|
||||
const visited = new Set();
|
||||
const queue = [...roots];
|
||||
|
||||
for (const root of roots) {
|
||||
visited.add(`${root.owner}/${root.repo}#${root.number}`);
|
||||
}
|
||||
|
||||
console.log(`Starting discovery from ${roots.length} roots...`);
|
||||
|
||||
while (queue.length > 0) {
|
||||
const current = queue.shift();
|
||||
const currentKey = `${current.owner}/${current.repo}#${current.number}`;
|
||||
|
||||
try {
|
||||
const issueData = await fetchIssueData(
|
||||
current.owner,
|
||||
current.repo,
|
||||
current.number,
|
||||
);
|
||||
|
||||
if (!shouldProcess(issueData)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// ONLY add to labeling list if it's in the PUBLIC repository
|
||||
if (current.repo === PUBLIC_REPO) {
|
||||
// Don't label the roots themselves
|
||||
if (
|
||||
!ROOT_ISSUES.some(
|
||||
(r) => r.number === current.number && r.repo === current.repo,
|
||||
)
|
||||
) {
|
||||
allDescendants.set(currentKey, {
|
||||
...current,
|
||||
title: issueData.title,
|
||||
labels: issueData.labels,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const children = new Map();
|
||||
|
||||
// 1. Process Native Sub-issues
|
||||
if (issueData.subIssues) {
|
||||
for (const node of issueData.subIssues) {
|
||||
const childOwner = node.repository.owner.login;
|
||||
const childRepo = node.repository.name;
|
||||
const childNumber = node.number;
|
||||
const key = `${childOwner}/${childRepo}#${childNumber}`;
|
||||
children.set(key, {
|
||||
owner: childOwner,
|
||||
repo: childRepo,
|
||||
number: childNumber,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Process Markdown Task Lists in Body and Comments
|
||||
let combinedText = issueData.body || '';
|
||||
if (issueData.comments) {
|
||||
for (const commentBody of issueData.comments) {
|
||||
combinedText += '\n' + (commentBody || '');
|
||||
}
|
||||
}
|
||||
|
||||
const taskListLinks = extractTaskListLinks(
|
||||
combinedText,
|
||||
current.owner,
|
||||
current.repo,
|
||||
);
|
||||
for (const link of taskListLinks) {
|
||||
const key = `${link.owner}/${link.repo}#${link.number}`;
|
||||
children.set(key, link);
|
||||
}
|
||||
|
||||
// Queue children (regardless of which repo they are in, for recursion)
|
||||
for (const [key, child] of children) {
|
||||
if (!visited.has(key)) {
|
||||
visited.add(key);
|
||||
queue.push(child);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing ${currentKey}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(allDescendants.values());
|
||||
}
|
||||
|
||||
async function run() {
|
||||
if (isDryRun) {
|
||||
console.log('=== DRY RUN MODE: No labels will be applied ===');
|
||||
}
|
||||
|
||||
const descendants = await getAllDescendants(ROOT_ISSUES);
|
||||
console.log(
|
||||
`\nFound ${descendants.length} total unique open descendant issues in ${PUBLIC_REPO}.`,
|
||||
);
|
||||
|
||||
for (const issueInfo of descendants) {
|
||||
const issueKey = `${issueInfo.owner}/${issueInfo.repo}#${issueInfo.number}`;
|
||||
try {
|
||||
// Data is already available from the discovery phase
|
||||
const hasLabel = issueInfo.labels.some((l) => l === TARGET_LABEL);
|
||||
|
||||
if (!hasLabel) {
|
||||
if (isDryRun) {
|
||||
console.log(
|
||||
`[DRY RUN] Would label ${issueKey}: "${issueInfo.title}"`,
|
||||
);
|
||||
} else {
|
||||
console.log(`Labeling ${issueKey}: "${issueInfo.title}"...`);
|
||||
await octokit.rest.issues.addLabels({
|
||||
owner: issueInfo.owner,
|
||||
repo: issueInfo.repo,
|
||||
issue_number: issueInfo.number,
|
||||
labels: [TARGET_LABEL],
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing label for ${issueKey}: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
run().catch((error) => {
|
||||
console.error(error);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,349 @@
|
||||
name: 'Testing: E2E (Chained)'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
merge_group:
|
||||
workflow_run:
|
||||
workflows: ['Trigger E2E']
|
||||
types: ['completed']
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
head_sha:
|
||||
description: 'SHA of the commit to test'
|
||||
required: true
|
||||
repo_name:
|
||||
description: 'Repository name (e.g., owner/repo)'
|
||||
required: true
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.head_ref || github.event.workflow_run.head_branch || github.ref }}'
|
||||
cancel-in-progress: |-
|
||||
${{ github.event_name != 'push' && github.event_name != 'merge_group' }}
|
||||
|
||||
permissions:
|
||||
contents: 'read'
|
||||
statuses: 'write'
|
||||
|
||||
jobs:
|
||||
merge_queue_skipper:
|
||||
name: 'Merge Queue Skipper'
|
||||
permissions: 'read-all'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
outputs:
|
||||
skip: '${{ steps.merge-queue-e2e-skipper.outputs.skip-check }}'
|
||||
steps:
|
||||
- id: 'merge-queue-e2e-skipper'
|
||||
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
|
||||
with:
|
||||
secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
continue-on-error: true
|
||||
|
||||
download_repo_name:
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
if: "${{github.event_name == 'workflow_dispatch' || github.event_name == 'workflow_run'}}"
|
||||
outputs:
|
||||
repo_name: '${{ steps.output-repo-name.outputs.repo_name }}'
|
||||
head_sha: '${{ steps.output-repo-name.outputs.head_sha }}'
|
||||
steps:
|
||||
- name: 'Mock Repo Artifact'
|
||||
if: "${{ github.event_name == 'workflow_dispatch' }}"
|
||||
env:
|
||||
REPO_NAME: '${{ github.event.inputs.repo_name }}'
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
|
||||
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'repo_name'
|
||||
path: 'pr/'
|
||||
- name: 'Download the repo_name artifact'
|
||||
uses: 'actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0' # ratchet:actions/download-artifact@v5
|
||||
env:
|
||||
RUN_ID: "${{ github.event_name == 'workflow_run' && github.event.workflow_run.id || github.run_id }}"
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
name: 'repo_name'
|
||||
run-id: '${{ env.RUN_ID }}'
|
||||
path: '${{ runner.temp }}/artifacts'
|
||||
- name: 'Output Repo Name and SHA'
|
||||
id: 'output-repo-name'
|
||||
uses: 'actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd' # ratchet:actions/github-script@v8
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const temp = '${{ runner.temp }}/artifacts';
|
||||
const repoPath = path.join(temp, 'repo_name');
|
||||
if (fs.existsSync(repoPath)) {
|
||||
const repo_name = String(fs.readFileSync(repoPath)).trim();
|
||||
core.setOutput('repo_name', repo_name);
|
||||
}
|
||||
const shaPath = path.join(temp, 'head_sha');
|
||||
if (fs.existsSync(shaPath)) {
|
||||
const head_sha = String(fs.readFileSync(shaPath)).trim();
|
||||
core.setOutput('head_sha', head_sha);
|
||||
}
|
||||
|
||||
parse_run_context:
|
||||
name: 'Parse run context'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
needs: 'download_repo_name'
|
||||
if: 'always()'
|
||||
outputs:
|
||||
repository: '${{ steps.set_context.outputs.REPO }}'
|
||||
sha: '${{ steps.set_context.outputs.SHA }}'
|
||||
steps:
|
||||
- id: 'set_context'
|
||||
name: 'Set dynamic repository and SHA'
|
||||
env:
|
||||
REPO: '${{ needs.download_repo_name.outputs.repo_name || github.repository }}'
|
||||
SHA: '${{ needs.download_repo_name.outputs.head_sha || github.event.inputs.head_sha || github.event.workflow_run.head_sha || github.sha }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "REPO=$REPO" >> "$GITHUB_OUTPUT"
|
||||
echo "SHA=$SHA" >> "$GITHUB_OUTPUT"
|
||||
|
||||
set_pending_status:
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
permissions: 'write-all'
|
||||
needs:
|
||||
- 'parse_run_context'
|
||||
if: 'always()'
|
||||
steps:
|
||||
- name: 'Set pending status'
|
||||
uses: 'myrotvorets/set-commit-status-action@16037e056d73b2d3c88e37e393ff369047f70886' # ratchet:myrotvorets/set-commit-status-action@master
|
||||
if: 'always()'
|
||||
with:
|
||||
allowForks: 'true'
|
||||
repo: '${{ github.repository }}'
|
||||
sha: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
status: 'pending'
|
||||
context: 'E2E (Chained)'
|
||||
|
||||
e2e_linux:
|
||||
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
|
||||
needs:
|
||||
- 'merge_queue_skipper'
|
||||
- 'parse_run_context'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
if: |
|
||||
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sandbox:
|
||||
- 'sandbox:none'
|
||||
- 'sandbox:docker'
|
||||
node-version:
|
||||
- '20.x'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '${{ matrix.node-version }}'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Set up Docker'
|
||||
if: "${{matrix.sandbox == 'sandbox:docker'}}"
|
||||
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
|
||||
|
||||
- name: 'Run E2E tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
KEEP_OUTPUT: 'true'
|
||||
VERBOSE: 'true'
|
||||
BUILD_SANDBOX_FLAGS: '--cache-from type=gha --cache-to type=gha,mode=max'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
if [[ "${{ matrix.sandbox }}" == "sandbox:docker" ]]; then
|
||||
npm run test:integration:sandbox:docker
|
||||
else
|
||||
npm run test:integration:sandbox:none
|
||||
fi
|
||||
|
||||
e2e_mac:
|
||||
name: 'E2E Test (macOS)'
|
||||
needs:
|
||||
- 'merge_queue_skipper'
|
||||
- 'parse_run_context'
|
||||
runs-on: 'macos-latest'
|
||||
if: |
|
||||
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Fix rollup optional dependencies on macOS'
|
||||
if: "${{runner.os == 'macOS'}}"
|
||||
run: |
|
||||
npm cache clean --force
|
||||
- name: 'Run E2E tests (non-Windows)'
|
||||
if: "${{runner.os != 'Windows'}}"
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
KEEP_OUTPUT: 'true'
|
||||
SANDBOX: 'sandbox:none'
|
||||
VERBOSE: 'true'
|
||||
run: 'npm run test:integration:sandbox:none'
|
||||
|
||||
e2e_windows:
|
||||
name: 'Slow E2E - Win'
|
||||
needs:
|
||||
- 'merge_queue_skipper'
|
||||
- 'parse_run_context'
|
||||
if: |
|
||||
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
|
||||
runs-on: 'gemini-cli-windows-16-core'
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Configure Windows Defender exclusions'
|
||||
run: |
|
||||
Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -Force
|
||||
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\node_modules" -Force
|
||||
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\packages" -Force
|
||||
Add-MpPreference -ExclusionPath "$env:TEMP" -Force
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Configure npm for Windows performance'
|
||||
run: |
|
||||
npm config set progress false
|
||||
npm config set audit false
|
||||
npm config set fund false
|
||||
npm config set loglevel error
|
||||
npm config set maxsockets 32
|
||||
npm config set registry https://registry.npmjs.org/
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Run E2E tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
KEEP_OUTPUT: 'true'
|
||||
SANDBOX: 'sandbox:none'
|
||||
VERBOSE: 'true'
|
||||
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
|
||||
UV_THREADPOOL_SIZE: '32'
|
||||
NODE_ENV: 'test'
|
||||
shell: 'pwsh'
|
||||
run: 'npm run test:integration:sandbox:none'
|
||||
|
||||
evals:
|
||||
name: 'Evals (ALWAYS_PASSING)'
|
||||
needs:
|
||||
- 'merge_queue_skipper'
|
||||
- 'parse_run_context'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
if: |
|
||||
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
repository: '${{ needs.parse_run_context.outputs.repository }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Run Evals (Required to pass)'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
run: 'npm run test:always_passing_evals'
|
||||
|
||||
e2e:
|
||||
name: 'E2E'
|
||||
if: |
|
||||
always() && (needs.merge_queue_skipper.result !='success' || needs.merge_queue_skipper.outputs.skip != 'true')
|
||||
needs:
|
||||
- 'e2e_linux'
|
||||
- 'e2e_mac'
|
||||
- 'evals'
|
||||
- 'merge_queue_skipper'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
steps:
|
||||
- name: 'Check E2E test results'
|
||||
run: |
|
||||
if [[ ${{ needs.e2e_linux.result }} != 'success' || \
|
||||
${{ needs.e2e_mac.result }} != 'success' || \
|
||||
${{ needs.evals.result }} != 'success' ]]; then
|
||||
echo "One or more E2E jobs failed."
|
||||
exit 1
|
||||
fi
|
||||
echo "All required E2E jobs passed!"
|
||||
|
||||
set_workflow_status:
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
permissions: 'write-all'
|
||||
if: 'always()'
|
||||
needs:
|
||||
- 'parse_run_context'
|
||||
- 'e2e'
|
||||
steps:
|
||||
- name: 'Set workflow status'
|
||||
uses: 'myrotvorets/set-commit-status-action@16037e056d73b2d3c88e37e393ff369047f70886' # ratchet:myrotvorets/set-commit-status-action@master
|
||||
if: 'always()'
|
||||
with:
|
||||
allowForks: 'true'
|
||||
repo: '${{ github.repository }}'
|
||||
sha: '${{ needs.parse_run_context.outputs.sha }}'
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
status: '${{ needs.e2e.result }}'
|
||||
context: 'E2E (Chained)'
|
||||
+281
-256
@@ -1,4 +1,4 @@
|
||||
name: 'Gemini CLI CI'
|
||||
name: 'Testing: CI'
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -10,6 +10,13 @@ on:
|
||||
- 'main'
|
||||
- 'release/**'
|
||||
merge_group:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch_ref:
|
||||
description: 'Branch to run on'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: 'string'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
|
||||
@@ -25,68 +32,32 @@ defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
env:
|
||||
ACTIONLINT_VERSION: '1.7.7'
|
||||
SHELLCHECK_VERSION: '0.11.0'
|
||||
YAMLLINT_VERSION: '1.35.1'
|
||||
|
||||
jobs:
|
||||
#
|
||||
# Lint: GitHub Actions
|
||||
#
|
||||
lint_github_actions:
|
||||
name: 'Lint (GitHub Actions)'
|
||||
runs-on: 'ubuntu-latest'
|
||||
merge_queue_skipper:
|
||||
permissions: 'read-all'
|
||||
name: 'Merge Queue Skipper'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
outputs:
|
||||
skip: '${{ steps.merge-queue-ci-skipper.outputs.skip-check }}'
|
||||
steps:
|
||||
- id: 'merge-queue-ci-skipper'
|
||||
uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main
|
||||
with:
|
||||
secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
|
||||
lint:
|
||||
name: 'Lint'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
needs: 'merge_queue_skipper'
|
||||
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
env:
|
||||
GEMINI_LINT_TEMP_DIR: '${{ github.workspace }}/.gemini-linters'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 'Install shellcheck' # Actionlint uses shellcheck
|
||||
run: |-
|
||||
mkdir -p "${RUNNER_TEMP}/shellcheck"
|
||||
curl -sSLo "${RUNNER_TEMP}/.shellcheck.txz" "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz"
|
||||
tar -xf "${RUNNER_TEMP}/.shellcheck.txz" -C "${RUNNER_TEMP}/shellcheck" --strip-components=1
|
||||
echo "${RUNNER_TEMP}/shellcheck" >> "${GITHUB_PATH}"
|
||||
|
||||
- name: 'Install actionlint'
|
||||
run: |-
|
||||
mkdir -p "${RUNNER_TEMP}/actionlint"
|
||||
curl -sSLo "${RUNNER_TEMP}/.actionlint.tgz" "https://github.com/rhysd/actionlint/releases/download/v${ACTIONLINT_VERSION}/actionlint_${ACTIONLINT_VERSION}_linux_amd64.tar.gz"
|
||||
tar -xzf "${RUNNER_TEMP}/.actionlint.tgz" -C "${RUNNER_TEMP}/actionlint"
|
||||
echo "${RUNNER_TEMP}/actionlint" >> "${GITHUB_PATH}"
|
||||
|
||||
# For actionlint, we specifically ignore shellcheck rules that are
|
||||
# annoying or unhelpful. See the shellcheck action for a description.
|
||||
- name: 'Run actionlint'
|
||||
run: |-
|
||||
actionlint \
|
||||
-color \
|
||||
-format '{{range $err := .}}::error file={{$err.Filepath}},line={{$err.Line}},col={{$err.Column}}::{{$err.Filepath}}@{{$err.Line}} {{$err.Message}}%0A```%0A{{replace $err.Snippet "\\n" "%0A"}}%0A```\n{{end}}' \
|
||||
-ignore 'SC2002:' \
|
||||
-ignore 'SC2016:' \
|
||||
-ignore 'SC2129:' \
|
||||
-ignore 'label ".+" is unknown'
|
||||
|
||||
- name: 'Run ratchet'
|
||||
uses: 'sethvargo/ratchet@8b4ca256dbed184350608a3023620f267f0a5253' # ratchet:sethvargo/ratchet@v0.11.4
|
||||
with:
|
||||
files: |-
|
||||
.github/workflows/*.yml
|
||||
.github/actions/**/*.yml
|
||||
|
||||
#
|
||||
# Lint: Javascript
|
||||
#
|
||||
lint_javascript:
|
||||
name: 'Lint (Javascript)'
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4.4.0
|
||||
@@ -94,178 +65,84 @@ jobs:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Run lockfile check'
|
||||
run: |-
|
||||
npm run check:lockfile
|
||||
- name: 'Cache Linters'
|
||||
uses: 'actions/cache@v4'
|
||||
with:
|
||||
path: '${{ env.GEMINI_LINT_TEMP_DIR }}'
|
||||
key: "${{ runner.os }}-${{ runner.arch }}-linters-${{ hashFiles('scripts/lint.js') }}"
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: |-
|
||||
npm ci
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run formatter check'
|
||||
run: |-
|
||||
npm run format
|
||||
git diff --exit-code
|
||||
|
||||
- name: 'Run linter'
|
||||
run: |-
|
||||
npm run lint:ci
|
||||
|
||||
- name: 'Run linter on integration tests'
|
||||
run: |-
|
||||
npx eslint integration-tests --max-warnings 0
|
||||
|
||||
- name: 'Run formatter on integration tests'
|
||||
run: |-
|
||||
npx prettier --check integration-tests
|
||||
git diff --exit-code
|
||||
|
||||
- name: 'Build project'
|
||||
run: |-
|
||||
npm run build
|
||||
|
||||
- name: 'Run type check'
|
||||
run: |-
|
||||
npm run typecheck
|
||||
|
||||
#
|
||||
# Lint: Shell
|
||||
#
|
||||
lint_shell:
|
||||
name: 'Lint (Shell)'
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
- name: 'Cache ESLint'
|
||||
uses: 'actions/cache@v4'
|
||||
with:
|
||||
fetch-depth: 1
|
||||
path: '.eslintcache'
|
||||
key: "${{ runner.os }}-eslint-${{ hashFiles('package-lock.json', 'eslint.config.js') }}"
|
||||
|
||||
- name: 'Install shellcheck'
|
||||
run: |-
|
||||
mkdir -p "${RUNNER_TEMP}/shellcheck"
|
||||
curl -sSLo "${RUNNER_TEMP}/.shellcheck.txz" "https://github.com/koalaman/shellcheck/releases/download/v${SHELLCHECK_VERSION}/shellcheck-v${SHELLCHECK_VERSION}.linux.x86_64.tar.xz"
|
||||
tar -xf "${RUNNER_TEMP}/.shellcheck.txz" -C "${RUNNER_TEMP}/shellcheck" --strip-components=1
|
||||
echo "${RUNNER_TEMP}/shellcheck" >> "${GITHUB_PATH}"
|
||||
- name: 'Validate NOTICES.txt'
|
||||
run: 'git diff --exit-code packages/vscode-ide-companion/NOTICES.txt'
|
||||
|
||||
- name: 'Install shellcheck problem matcher'
|
||||
run: |-
|
||||
cat > "${RUNNER_TEMP}/shellcheck/problem-matcher-lint-shell.json" <<"EOF"
|
||||
{
|
||||
"problemMatcher": [
|
||||
{
|
||||
"owner": "lint_shell",
|
||||
"pattern": [
|
||||
{
|
||||
"regexp": "^(.*):(\\\\d+):(\\\\d+):\\\\s+(?:fatal\\\\s+)?(warning|error):\\\\s+(.*)$",
|
||||
"file": 1,
|
||||
"line": 2,
|
||||
"column": 3,
|
||||
"severity": 4,
|
||||
"message": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
echo "::add-matcher::${RUNNER_TEMP}/shellcheck/problem-matcher-lint-shell.json"
|
||||
- name: 'Check lockfile'
|
||||
run: 'npm run check:lockfile'
|
||||
|
||||
- name: 'Install linters'
|
||||
run: 'node scripts/lint.js --setup'
|
||||
|
||||
- name: 'Run ESLint'
|
||||
run: 'node scripts/lint.js --eslint'
|
||||
|
||||
- name: 'Run actionlint'
|
||||
run: 'node scripts/lint.js --actionlint'
|
||||
|
||||
# Note that only warning and error severity show up in the github files
|
||||
# page. So we replace 'style' and 'note' with 'warning' to make it show
|
||||
# up.
|
||||
#
|
||||
# We also try and find all bash scripts even if they don't have an
|
||||
# explicit extension.
|
||||
#
|
||||
# We explicitly ignore the following rules:
|
||||
#
|
||||
# - SC2002: This rule suggests using "cmd < file" instead of "cat | cmd".
|
||||
# While < is more efficient, pipes are much more readable and expected.
|
||||
#
|
||||
# - SC2129: This rule suggests grouping multiple writes to a file in
|
||||
# braces like "{ cmd1; cmd2; } >> file". This is unexpected and less
|
||||
# readable.
|
||||
#
|
||||
# - SC2310: This is an optional warning that only appears with "set -e"
|
||||
# and when a command is used as a conditional.
|
||||
- name: 'Run shellcheck'
|
||||
run: |-
|
||||
git ls-files | grep -E '^([^.]+|.*\.(sh|zsh|bash))$' | xargs file --mime-type \
|
||||
| grep "text/x-shellscript" | awk '{ print substr($1, 1, length($1)-1) }' \
|
||||
| xargs shellcheck \
|
||||
--check-sourced \
|
||||
--enable=all \
|
||||
--exclude=SC2002,SC2129,SC2310 \
|
||||
--severity=style \
|
||||
--format=gcc \
|
||||
--color=never | sed -e 's/note:/warning:/g' -e 's/style:/warning:/g'
|
||||
|
||||
#
|
||||
# Lint: YAML
|
||||
#
|
||||
lint_yaml:
|
||||
name: 'Lint (YAML)'
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 'Setup Python'
|
||||
uses: 'actions/setup-python@a26af69be951a213d495a4c3e4e4022e16d87065' # ratchet:actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3'
|
||||
|
||||
- name: 'Install yamllint'
|
||||
run: |-
|
||||
pip install --user "yamllint==${YAMLLINT_VERSION}"
|
||||
run: 'node scripts/lint.js --shellcheck'
|
||||
|
||||
- name: 'Run yamllint'
|
||||
run: |-
|
||||
git ls-files | grep -E '\.(yaml|yml)' | xargs yamllint --format github
|
||||
run: 'node scripts/lint.js --yamllint'
|
||||
|
||||
#
|
||||
# Lint: All
|
||||
#
|
||||
# This is a virtual job that other jobs depend on to wait for all linters to
|
||||
# finish. It's also used to ensure linting happens on CI via required
|
||||
# workflows.
|
||||
lint:
|
||||
name: 'Lint'
|
||||
needs:
|
||||
- 'lint_github_actions'
|
||||
- 'lint_javascript'
|
||||
- 'lint_shell'
|
||||
- 'lint_yaml'
|
||||
- name: 'Run Prettier'
|
||||
run: 'node scripts/lint.js --prettier'
|
||||
|
||||
- name: 'Build docs prerequisites'
|
||||
run: 'npm run predocs:settings'
|
||||
|
||||
- name: 'Verify settings docs'
|
||||
run: 'npm run docs:settings -- --check'
|
||||
|
||||
- name: 'Run sensitive keyword linter'
|
||||
run: 'node scripts/lint.js --sensitive-keywords'
|
||||
|
||||
link_checker:
|
||||
name: 'Link Checker'
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- run: |-
|
||||
echo 'All linters finished!'
|
||||
|
||||
#
|
||||
# Test: Node
|
||||
#
|
||||
test:
|
||||
name: 'Test'
|
||||
runs-on: '${{ matrix.os }}'
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
- name: 'Link Checker'
|
||||
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
|
||||
with:
|
||||
args: '--verbose --accept 200,503 ./**/*.md'
|
||||
fail: true
|
||||
test_linux:
|
||||
name: 'Test (Linux) - ${{ matrix.node-version }}, ${{ matrix.shard }}'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
needs:
|
||||
- 'lint'
|
||||
- 'merge_queue_skipper'
|
||||
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
permissions:
|
||||
contents: 'read'
|
||||
checks: 'write'
|
||||
pull-requests: 'write'
|
||||
strategy:
|
||||
fail-fast: false # So we can see all test failures
|
||||
matrix:
|
||||
os:
|
||||
- 'macos-latest'
|
||||
- 'ubuntu-latest'
|
||||
- 'windows-latest'
|
||||
node-version:
|
||||
- '20.x'
|
||||
- '22.x'
|
||||
- '24.x'
|
||||
shard:
|
||||
- 'cli'
|
||||
- 'others'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
@@ -277,24 +154,38 @@ jobs:
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Build project'
|
||||
run: |-
|
||||
npm run build
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Install dependencies for testing'
|
||||
run: |-
|
||||
npm ci
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run tests and generate reports'
|
||||
env:
|
||||
NO_COLOR: true
|
||||
run: 'npm run test:ci'
|
||||
run: |
|
||||
if [[ "${{ matrix.shard }}" == "cli" ]]; then
|
||||
npm run test:ci --workspace @google/gemini-cli
|
||||
else
|
||||
# Explicitly list non-cli packages to ensure they are sharded correctly
|
||||
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present
|
||||
npm run test:scripts
|
||||
fi
|
||||
|
||||
- name: 'Bundle'
|
||||
run: 'npm run bundle'
|
||||
|
||||
- name: 'Smoke test bundle'
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
|
||||
- name: 'Wait for file system sync'
|
||||
run: 'sleep 2'
|
||||
|
||||
- name: 'Publish Test Report (for non-forks)'
|
||||
if: |-
|
||||
${{ always() && (github.event.pull_request.head.repo.full_name == github.repository) }}
|
||||
uses: 'dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3' # ratchet:dorny/test-reporter@v2
|
||||
with:
|
||||
name: 'Test Results (Node ${{ matrix.node-version }})'
|
||||
name: 'Test Results (Node ${{ runner.os }}, ${{ matrix.node-version }}, ${{ matrix.shard }})'
|
||||
path: 'packages/*/junit.xml'
|
||||
reporter: 'java-junit'
|
||||
fail-on-error: 'false'
|
||||
@@ -304,7 +195,82 @@ jobs:
|
||||
${{ always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'test-results-fork-${{ matrix.node-version }}-${{ matrix.os }}'
|
||||
name: 'test-results-fork-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}'
|
||||
path: 'packages/*/junit.xml'
|
||||
|
||||
test_mac:
|
||||
name: 'Test (Mac) - ${{ matrix.node-version }}, ${{ matrix.shard }}'
|
||||
runs-on: 'macos-latest'
|
||||
needs:
|
||||
- 'merge_queue_skipper'
|
||||
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
permissions:
|
||||
contents: 'read'
|
||||
checks: 'write'
|
||||
pull-requests: 'write'
|
||||
continue-on-error: true
|
||||
strategy:
|
||||
matrix:
|
||||
node-version:
|
||||
- '20.x'
|
||||
- '22.x'
|
||||
- '24.x'
|
||||
shard:
|
||||
- 'cli'
|
||||
- 'others'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '${{ matrix.node-version }}'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Install dependencies for testing'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run tests and generate reports'
|
||||
env:
|
||||
NO_COLOR: true
|
||||
run: |
|
||||
if [[ "${{ matrix.shard }}" == "cli" ]]; then
|
||||
npm run test:ci --workspace @google/gemini-cli -- --coverage.enabled=false
|
||||
else
|
||||
# Explicitly list non-cli packages to ensure they are sharded correctly
|
||||
npm run test:ci --workspace @google/gemini-cli-core --workspace @google/gemini-cli-a2a-server --workspace gemini-cli-vscode-ide-companion --workspace @google/gemini-cli-test-utils --if-present -- --coverage.enabled=false
|
||||
npm run test:scripts
|
||||
fi
|
||||
|
||||
- name: 'Bundle'
|
||||
run: 'npm run bundle'
|
||||
|
||||
- name: 'Smoke test bundle'
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
|
||||
- name: 'Wait for file system sync'
|
||||
run: 'sleep 2'
|
||||
|
||||
- name: 'Publish Test Report (for non-forks)'
|
||||
if: |-
|
||||
${{ always() && (github.event.pull_request.head.repo.full_name == github.repository) }}
|
||||
uses: 'dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3' # ratchet:dorny/test-reporter@v2
|
||||
with:
|
||||
name: 'Test Results (Node ${{ runner.os }}, ${{ matrix.node-version }}, ${{ matrix.shard }})'
|
||||
path: 'packages/*/junit.xml'
|
||||
reporter: 'java-junit'
|
||||
fail-on-error: 'false'
|
||||
|
||||
- name: 'Upload Test Results Artifact (for forks)'
|
||||
if: |-
|
||||
${{ always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }}
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'test-results-fork-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}'
|
||||
path: 'packages/*/junit.xml'
|
||||
|
||||
- name: 'Upload coverage reports'
|
||||
@@ -312,50 +278,14 @@ jobs:
|
||||
${{ always() }}
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'coverage-reports-${{ matrix.node-version }}-${{ matrix.os }}'
|
||||
name: 'coverage-reports-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}'
|
||||
path: 'packages/*/coverage'
|
||||
|
||||
post_coverage_comment:
|
||||
name: 'Post Coverage Comment'
|
||||
runs-on: 'ubuntu-latest'
|
||||
needs: 'test'
|
||||
if: |-
|
||||
${{ always() && github.event_name == 'pull_request' && (github.event.pull_request.head.repo.full_name == github.repository) }}
|
||||
continue-on-error: true
|
||||
permissions:
|
||||
contents: 'read' # For checkout
|
||||
pull-requests: 'write' # For commenting
|
||||
strategy:
|
||||
matrix:
|
||||
# Reduce noise by only posting the comment once
|
||||
os:
|
||||
- 'ubuntu-latest'
|
||||
node-version:
|
||||
- '22.x'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
|
||||
- name: 'Download coverage reports artifact'
|
||||
uses: 'actions/download-artifact@634f93cb2916e3fdff6788551b99b062d0335ce0' # ratchet:actions/download-artifact@v5
|
||||
with:
|
||||
name: 'coverage-reports-${{ matrix.node-version }}-${{ matrix.os }}'
|
||||
path: 'coverage_artifact' # Download to a specific directory
|
||||
|
||||
- name: 'Post Coverage Comment using Composite Action'
|
||||
uses: './.github/actions/post-coverage-comment' # Path to the composite action directory
|
||||
with:
|
||||
cli_json_file: 'coverage_artifact/cli/coverage/coverage-summary.json'
|
||||
core_json_file: 'coverage_artifact/core/coverage/coverage-summary.json'
|
||||
cli_full_text_summary_file: 'coverage_artifact/cli/coverage/full-text-summary.txt'
|
||||
core_full_text_summary_file: 'coverage_artifact/core/coverage/full-text-summary.txt'
|
||||
node_version: '${{ matrix.node-version }}'
|
||||
os: '${{ matrix.os }}'
|
||||
github_token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
codeql:
|
||||
name: 'CodeQL'
|
||||
runs-on: 'ubuntu-latest'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
needs: 'merge_queue_skipper'
|
||||
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
permissions:
|
||||
actions: 'read'
|
||||
contents: 'read'
|
||||
@@ -363,6 +293,8 @@ jobs:
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
|
||||
- name: 'Initialize CodeQL'
|
||||
uses: 'github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/init@v3
|
||||
@@ -375,9 +307,9 @@ jobs:
|
||||
# Check for changes in bundle size.
|
||||
bundle_size:
|
||||
name: 'Check Bundle Size'
|
||||
if: |-
|
||||
${{ github.event_name != 'merge_group' }}
|
||||
runs-on: 'ubuntu-latest'
|
||||
needs: 'merge_queue_skipper'
|
||||
if: "${{github.event_name == 'pull_request' && needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
permissions:
|
||||
contents: 'read' # For checkout
|
||||
pull-requests: 'write' # For commenting
|
||||
@@ -386,6 +318,7 @@ jobs:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
fetch-depth: 1
|
||||
|
||||
- uses: 'preactjs/compressed-size-action@946a292cd35bd1088e0d7eb92b69d1a8d5b5d76a'
|
||||
@@ -395,3 +328,95 @@ jobs:
|
||||
minimum-change-threshold: '1000'
|
||||
compression: 'none'
|
||||
clean-script: 'clean'
|
||||
|
||||
test_windows:
|
||||
name: 'Slow Test - Win'
|
||||
runs-on: 'gemini-cli-windows-16-core'
|
||||
needs: 'merge_queue_skipper'
|
||||
if: "${{needs.merge_queue_skipper.outputs.skip == 'false'}}"
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.inputs.branch_ref || github.ref }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Configure Windows Defender exclusions'
|
||||
run: |
|
||||
Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -Force
|
||||
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\node_modules" -Force
|
||||
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\packages" -Force
|
||||
Add-MpPreference -ExclusionPath "$env:TEMP" -Force
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Configure npm for Windows performance'
|
||||
run: |
|
||||
npm config set progress false
|
||||
npm config set audit false
|
||||
npm config set fund false
|
||||
npm config set loglevel error
|
||||
npm config set maxsockets 32
|
||||
npm config set registry https://registry.npmjs.org/
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
shell: 'pwsh'
|
||||
env:
|
||||
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
|
||||
UV_THREADPOOL_SIZE: '32'
|
||||
NODE_ENV: 'production'
|
||||
|
||||
- name: 'Run tests and generate reports'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
NO_COLOR: true
|
||||
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
|
||||
UV_THREADPOOL_SIZE: '32'
|
||||
NODE_ENV: 'test'
|
||||
run: 'npm run test:ci -- --coverage.enabled=false'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Bundle'
|
||||
run: 'npm run bundle'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Smoke test bundle'
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
shell: 'pwsh'
|
||||
|
||||
ci:
|
||||
name: 'CI'
|
||||
if: 'always()'
|
||||
needs:
|
||||
- 'lint'
|
||||
- 'link_checker'
|
||||
- 'test_linux'
|
||||
- 'test_mac'
|
||||
- 'codeql'
|
||||
- 'bundle_size'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
steps:
|
||||
- name: 'Check all job results'
|
||||
run: |
|
||||
if [[ (${{ needs.lint.result }} != 'success' && ${{ needs.lint.result }} != 'skipped') || \
|
||||
(${{ needs.link_checker.result }} != 'success' && ${{ needs.link_checker.result }} != 'skipped') || \
|
||||
(${{ needs.test_linux.result }} != 'success' && ${{ needs.test_linux.result }} != 'skipped') || \
|
||||
(${{ needs.test_mac.result }} != 'success' && ${{ needs.test_mac.result }} != 'skipped') || \
|
||||
(${{ needs.codeql.result }} != 'success' && ${{ needs.codeql.result }} != 'skipped') || \
|
||||
(${{ needs.bundle_size.result }} != 'success' && ${{ needs.bundle_size.result }} != 'skipped') ]]; then
|
||||
echo "One or more CI jobs failed."
|
||||
exit 1
|
||||
fi
|
||||
echo "All CI jobs passed!"
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
name: 'Create Patch PR'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
commit:
|
||||
description: 'The commit SHA to cherry-pick for the patch.'
|
||||
required: true
|
||||
type: 'string'
|
||||
channel:
|
||||
description: 'The release channel to patch.'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'stable'
|
||||
- 'preview'
|
||||
dry_run:
|
||||
description: 'Whether to run in dry-run mode.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
create-patch:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'write'
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Configure Git User'
|
||||
run: |-
|
||||
git config user.name "gemini-cli-robot"
|
||||
git config user.email "gemini-cli-robot@google.com"
|
||||
|
||||
- name: 'Create Patch for Stable'
|
||||
if: "github.event.inputs.channel == 'stable'"
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: 'node scripts/create-patch-pr.js --commit=${{ github.event.inputs.commit }} --channel=stable --dry-run=${{ github.event.inputs.dry_run }}'
|
||||
|
||||
- name: 'Create Patch for Preview'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: 'node scripts/create-patch-pr.js --commit=${{ github.event.inputs.commit }} --channel=${{ github.event.inputs.channel }} --dry-run=${{ github.event.inputs.dry_run }}'
|
||||
@@ -0,0 +1,170 @@
|
||||
name: 'Deflake E2E'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
branch_ref:
|
||||
description: 'Branch to run on'
|
||||
required: true
|
||||
default: 'main'
|
||||
type: 'string'
|
||||
test_name_pattern:
|
||||
description: 'The test name pattern to use'
|
||||
required: false
|
||||
type: 'string'
|
||||
runs:
|
||||
description: 'The number of runs'
|
||||
required: false
|
||||
default: 5
|
||||
type: 'number'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}'
|
||||
cancel-in-progress: |-
|
||||
${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }}
|
||||
|
||||
jobs:
|
||||
deflake_e2e_linux:
|
||||
name: 'E2E Test (Linux) - ${{ matrix.sandbox }}'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
sandbox:
|
||||
- 'sandbox:none'
|
||||
- 'sandbox:docker'
|
||||
node-version:
|
||||
- '20.x'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.repository }}'
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '${{ matrix.node-version }}'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Set up Docker'
|
||||
if: "matrix.sandbox == 'sandbox:docker'"
|
||||
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
|
||||
|
||||
- name: 'Run E2E tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
IS_DOCKER: "${{ matrix.sandbox == 'sandbox:docker' }}"
|
||||
KEEP_OUTPUT: 'true'
|
||||
RUNS: '${{ github.event.inputs.runs }}'
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
VERBOSE: 'true'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
if [[ "${{ env.IS_DOCKER }}" == "true" ]]; then
|
||||
npm run deflake:test:integration:sandbox:docker -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
else
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
fi
|
||||
|
||||
deflake_e2e_mac:
|
||||
name: 'E2E Test (macOS)'
|
||||
runs-on: 'macos-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.repository }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Fix rollup optional dependencies on macOS'
|
||||
if: "runner.os == 'macOS'"
|
||||
run: |
|
||||
npm cache clean --force
|
||||
- name: 'Run E2E tests (non-Windows)'
|
||||
if: "runner.os != 'Windows'"
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
KEEP_OUTPUT: 'true'
|
||||
RUNS: '${{ github.event.inputs.runs }}'
|
||||
SANDBOX: 'sandbox:none'
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
VERBOSE: 'true'
|
||||
run: |
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
|
||||
deflake_e2e_windows:
|
||||
name: 'Slow E2E - Win'
|
||||
runs-on: 'gemini-cli-windows-16-core'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.repository }}'
|
||||
|
||||
- name: 'Set up Node.js 20.x'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '20.x'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Configure Windows Defender exclusions'
|
||||
run: |
|
||||
Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -Force
|
||||
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\node_modules" -Force
|
||||
Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\packages" -Force
|
||||
Add-MpPreference -ExclusionPath "$env:TEMP" -Force
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Configure npm for Windows performance'
|
||||
run: |
|
||||
npm config set progress false
|
||||
npm config set audit false
|
||||
npm config set fund false
|
||||
npm config set loglevel error
|
||||
npm config set maxsockets 32
|
||||
npm config set registry https://registry.npmjs.org/
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
shell: 'pwsh'
|
||||
|
||||
- name: 'Run E2E tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
KEEP_OUTPUT: 'true'
|
||||
SANDBOX: 'sandbox:none'
|
||||
VERBOSE: 'true'
|
||||
NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256'
|
||||
UV_THREADPOOL_SIZE: '32'
|
||||
NODE_ENV: 'test'
|
||||
RUNS: '${{ github.event.inputs.runs }}'
|
||||
TEST_NAME_PATTERN: '${{ github.event.inputs.test_name_pattern }}'
|
||||
shell: 'pwsh'
|
||||
run: |
|
||||
npm run deflake:test:integration:sandbox:none -- --runs="${{ env.RUNS }}" -- --testNamePattern "'${{ env.TEST_NAME_PATTERN }}'"
|
||||
@@ -0,0 +1,17 @@
|
||||
name: 'Trigger Docs Rebuild'
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
paths:
|
||||
- 'docs/**'
|
||||
jobs:
|
||||
trigger-rebuild:
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Trigger rebuild'
|
||||
run: |
|
||||
curl -X POST \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{}' \
|
||||
"${{ secrets.DOCS_REBUILD_URL }}"
|
||||
@@ -1,85 +0,0 @@
|
||||
name: 'E2E Tests'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
# This will run for PRs from the base repository, providing secrets.
|
||||
pull_request:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release/**'
|
||||
# This will run for PRs from forks when a label is added.
|
||||
pull_request_target:
|
||||
types: ['labeled']
|
||||
merge_group:
|
||||
|
||||
jobs:
|
||||
e2e-test:
|
||||
name: 'E2E Test (${{ matrix.os }}) - ${{ matrix.sandbox }}'
|
||||
# This condition ensures the job runs for pushes to main, merge groups,
|
||||
# PRs from the base repo, OR PRs from forks with the correct label.
|
||||
if: |
|
||||
github.event_name == 'push' ||
|
||||
github.event_name == 'merge_group' ||
|
||||
(github.event.pull_request.head.repo.full_name == github.repository) ||
|
||||
(github.event.label.name == 'maintainer:e2e:ok')
|
||||
runs-on: '${{ matrix.os }}'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- 'ubuntu-latest'
|
||||
- 'macos-latest'
|
||||
- 'gemini-cli-windows-16-core'
|
||||
sandbox:
|
||||
- 'sandbox:none'
|
||||
- 'sandbox:docker'
|
||||
node-version:
|
||||
- '20.x'
|
||||
exclude:
|
||||
# Docker tests are not supported on macOS or Windows
|
||||
- os: 'macos-latest'
|
||||
sandbox: 'sandbox:docker'
|
||||
- os: 'gemini-cli-windows-16-core'
|
||||
sandbox: 'sandbox:docker'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout (fork)'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
if: "github.event_name == 'pull_request_target'"
|
||||
with:
|
||||
ref: '${{ github.event.pull_request.head.sha }}'
|
||||
repository: '${{ github.event.pull_request.head.repo.full_name }}'
|
||||
|
||||
- name: 'Checkout (internal)'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
if: "github.event_name != 'pull_request_target'"
|
||||
|
||||
- name: 'Set up Node.js ${{ matrix.node-version }}'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions-node@v4
|
||||
with:
|
||||
node-version: '${{ matrix.node-version }}'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: |-
|
||||
npm ci
|
||||
|
||||
- name: 'Build project'
|
||||
run: |-
|
||||
npm run build
|
||||
|
||||
- name: 'Set up Docker'
|
||||
if: |-
|
||||
matrix.os == 'ubuntu-latest' && matrix.sandbox == 'sandbox:docker'
|
||||
uses: 'docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435' # ratchet:docker/setup-buildx-action@v3
|
||||
|
||||
- name: 'Run E2E tests'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
KEEP_OUTPUT: 'true'
|
||||
SANDBOX: '${{ matrix.sandbox }}'
|
||||
VERBOSE: 'true'
|
||||
shell: 'bash'
|
||||
run: |-
|
||||
npm run "test:integration:${SANDBOX}"
|
||||
@@ -33,7 +33,7 @@ jobs:
|
||||
steps:
|
||||
- name: 'Authenticate to Google Cloud'
|
||||
id: 'auth'
|
||||
uses: 'google-github-actions/auth@v2' # ratchet:exclude
|
||||
uses: 'google-github-actions/auth@c200f3691d83b41bf9bbd8638997a462592937ed' # ratchet:exclude pin@v2.1.7
|
||||
with:
|
||||
project_id: '${{ vars.GOOGLE_CLOUD_PROJECT }}'
|
||||
workload_identity_provider: '${{ vars.GCP_WIF_PROVIDER }}'
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
name: 'Evals: Nightly'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 1 * * *' # Runs at 1 AM every day
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
run_all:
|
||||
description: 'Run all evaluations (including usually passing)'
|
||||
type: 'boolean'
|
||||
default: true
|
||||
|
||||
permissions:
|
||||
contents: 'read'
|
||||
checks: 'write'
|
||||
actions: 'read'
|
||||
|
||||
jobs:
|
||||
evals:
|
||||
name: 'Evals (USUALLY_PASSING) nightly run'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
run_attempt: [1, 2, 3]
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
|
||||
- name: 'Set up Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Build project'
|
||||
run: 'npm run build'
|
||||
|
||||
- name: 'Create logs directory'
|
||||
run: 'mkdir -p evals/logs'
|
||||
|
||||
- name: 'Run Evals'
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
RUN_EVALS: "${{ github.event.inputs.run_all != 'false' }}"
|
||||
run: 'npm run test:all_evals'
|
||||
|
||||
- name: 'Upload Logs'
|
||||
if: 'always()'
|
||||
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'eval-logs-${{ matrix.run_attempt }}'
|
||||
path: 'evals/logs'
|
||||
retention-days: 7
|
||||
|
||||
aggregate-results:
|
||||
name: 'Aggregate Results'
|
||||
needs: ['evals']
|
||||
if: 'always()'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
|
||||
- name: 'Download Logs'
|
||||
uses: 'actions/download-artifact@cc203385981b70ca67e1cc392babf9cc229d5806' # ratchet:actions/download-artifact@v4
|
||||
with:
|
||||
path: 'artifacts'
|
||||
|
||||
- name: 'Generate Summary'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: 'node scripts/aggregate_evals.js artifacts >> "$GITHUB_STEP_SUMMARY"'
|
||||
@@ -101,7 +101,6 @@ jobs:
|
||||
"FIRESTORE_DATABASE_ID": "(default)",
|
||||
"GOOGLE_APPLICATION_CREDENTIALS": "${GOOGLE_APPLICATION_CREDENTIALS}"
|
||||
},
|
||||
"enabled": true,
|
||||
"timeout": 600000
|
||||
}
|
||||
},
|
||||
|
||||
@@ -14,9 +14,15 @@ on:
|
||||
description: 'issue number to triage'
|
||||
required: true
|
||||
type: 'number'
|
||||
workflow_call:
|
||||
inputs:
|
||||
issue_number:
|
||||
description: 'issue number to triage'
|
||||
required: false
|
||||
type: 'string'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number }}'
|
||||
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number || inputs.issue_number }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
@@ -34,18 +40,18 @@ permissions:
|
||||
jobs:
|
||||
triage-issue:
|
||||
if: |-
|
||||
github.repository == 'google-gemini/gemini-cli' &&
|
||||
(github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli') &&
|
||||
(
|
||||
github.event_name == 'workflow_dispatch' ||
|
||||
(
|
||||
(github.event_name == 'issues' || github.event_name == 'issue_comment') &&
|
||||
contains(github.event.issue.labels.*.name, 'status/need-triage') &&
|
||||
(github.event_name != 'issue_comment' || (
|
||||
contains(github.event.comment.body, '@gemini-cli /triage') &&
|
||||
(github.event.comment.author_association == 'OWNER' || github.event.comment.author_association == 'MEMBER' || github.event.comment.author_association == 'COLLABORATOR')
|
||||
))
|
||||
)
|
||||
)
|
||||
) &&
|
||||
!contains(github.event.issue.labels.*.name, 'area/')
|
||||
timeout-minutes: 5
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
@@ -57,28 +63,40 @@ jobs:
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
script: |
|
||||
const issueNumber = ${{ github.event.inputs.issue_number || inputs.issue_number }};
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: ${{ github.event.inputs.issue_number }},
|
||||
issue_number: issueNumber,
|
||||
});
|
||||
core.setOutput('title', issue.title);
|
||||
core.setOutput('body', issue.body);
|
||||
core.setOutput('labels', issue.labels.map(label => label.name).join(','));
|
||||
return issue;
|
||||
|
||||
- name: 'Check for triage label on manual trigger'
|
||||
- name: 'Manual Trigger Pre-flight Checks'
|
||||
if: |-
|
||||
github.event_name == 'workflow_dispatch' && !contains(steps.get_issue_data.outputs.labels, 'status/need-triage')
|
||||
github.event_name == 'workflow_dispatch'
|
||||
env:
|
||||
ISSUE_NUMBER_INPUT: '${{ github.event.inputs.issue_number || inputs.issue_number }}'
|
||||
LABELS: '${{ steps.get_issue_data.outputs.labels }}'
|
||||
run: |
|
||||
echo "Issue #${{ github.event.inputs.issue_number }} does not have the 'status/need-triage' label. Stopping workflow."
|
||||
exit 1
|
||||
if echo "${LABELS}" | grep -q 'area/'; then
|
||||
echo "Issue #${ISSUE_NUMBER_INPUT} already has 'area/' label. Stopping workflow."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Manual triage checks passed."
|
||||
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
env:
|
||||
APP_ID: '${{ secrets.APP_ID }}'
|
||||
if: |-
|
||||
${{ env.APP_ID != '' }}
|
||||
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
@@ -89,13 +107,23 @@ jobs:
|
||||
id: 'get_labels'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |-
|
||||
const { data: labels } = await github.rest.issues.listLabelsForRepo({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
});
|
||||
const labelNames = labels.map(label => label.name);
|
||||
const allowedLabels = [
|
||||
'area/agent',
|
||||
'area/enterprise',
|
||||
'area/non-interactive',
|
||||
'area/core',
|
||||
'area/security',
|
||||
'area/platform',
|
||||
'area/extensions',
|
||||
'area/unknown'
|
||||
];
|
||||
const labelNames = labels.map(label => label.name).filter(name => allowedLabels.includes(name));
|
||||
core.setOutput('available_labels', labelNames.join(','));
|
||||
core.info(`Found ${labelNames.length} labels: ${labelNames.join(', ')}`);
|
||||
return labelNames;
|
||||
@@ -110,7 +138,7 @@ jobs:
|
||||
ISSUE_BODY: >-
|
||||
${{ github.event_name == 'workflow_dispatch' && steps.get_issue_data.outputs.body || github.event.issue.body }}
|
||||
ISSUE_NUMBER: >-
|
||||
${{ github.event_name == 'workflow_dispatch' && github.event.inputs.issue_number || github.event.issue.number }}
|
||||
${{ github.event_name == 'workflow_dispatch' && (github.event.inputs.issue_number || inputs.issue_number) || github.event.issue.number }}
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
|
||||
with:
|
||||
@@ -124,9 +152,6 @@ jobs:
|
||||
settings: |-
|
||||
{
|
||||
"maxSessionTurns": 25,
|
||||
"coreTools": [
|
||||
"run_shell_command(echo)"
|
||||
],
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"target": "gcp"
|
||||
@@ -135,128 +160,100 @@ jobs:
|
||||
prompt: |-
|
||||
## Role
|
||||
|
||||
You are an issue triage assistant. Analyze the current GitHub issue
|
||||
and identify the most appropriate existing labels by only using the provided data. Use the available
|
||||
tools to gather information; do not ask for information to be
|
||||
provided. Do not remove the following labels titled maintainer, help wanted or good first issue.
|
||||
You are an issue triage assistant. Your role is to analyze a GitHub issue and determine the single most appropriate area/ label based on the definitions provided.
|
||||
|
||||
## Steps
|
||||
|
||||
1. You are only able to use the echo command. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
|
||||
2. Review the issue title and body provided in the environment variables: "${ISSUE_TITLE}" and "${ISSUE_BODY}".
|
||||
3. Select the most relevant labels from the existing labels, focusing on kind/*, area/*, sub-area/* and priority/*. For area/* and kind/* limit yourself to only the single most applicable label in each case.
|
||||
4. If the issue already has area/ label, dont try to change it. Similarly, if the issue already has a kind/ label don't change it. And if the issue already has a priority/ label do not change it for example:
|
||||
If an issue has area/core and kind/bug you will only add a priority/ label.
|
||||
Instead if an issue has no labels, you will could add one lable of each kind.
|
||||
5. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5 for anything more than 6 versions older than the most recent should add the status/need-retesting label.
|
||||
6. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
|
||||
7. Output the appropriate labels for this issue in JSON format with explanation, for example:
|
||||
```
|
||||
{"labels_to_set": ["kind/bug", "priority/p0"], "explanation": "This is a critical bug report affecting main functionality"}
|
||||
```
|
||||
8. If the issue cannot be classified using the available labels, output:
|
||||
```
|
||||
{"labels_to_set": [], "explanation": "Unable to classify this issue with available labels"}
|
||||
```
|
||||
9. Use Area definitions mentioned below to help you narrow down issues.
|
||||
10. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
|
||||
11. If you are uncertain and have not been able to apply one each of kind/, area/ and priority/ , apply the status/manual-triage label.
|
||||
1. Review the issue title and body: ${{ env.ISSUE_TITLE }} and ${{ env.ISSUE_BODY }}.
|
||||
2. Review the available labels: ${{ env.AVAILABLE_LABELS }}.
|
||||
3. Select exactly one area/ label that best matches the issue based on Reference 1: Area Definitions.
|
||||
4. Fallback Logic:
|
||||
- If you cannot confidently determine the correct area/ label from the definitions, you must use area/unknown.
|
||||
5. Output your selected label in JSON format and nothing else. Example:
|
||||
{"labels_to_set": ["area/core"]}
|
||||
|
||||
## Guidelines
|
||||
- Your output must contain exactly one area/ label.
|
||||
- Triage only the current issue based on its title and body.
|
||||
- Output only valid JSON format.
|
||||
- Do not include any explanation or additional text, just the JSON.
|
||||
|
||||
- Only use labels that already exist in the repository
|
||||
- Do not add comments or modify the issue content
|
||||
- Triage only the current issue
|
||||
- Identify only one area/ label
|
||||
- Identify only one kind/ label
|
||||
- Identify all applicable sub-area/* and priority/* labels based on the issue content. It's ok to have multiple of these
|
||||
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario
|
||||
- Reference all shell variables as "${VAR}" (with quotes and braces)
|
||||
- Output only valid JSON format
|
||||
- Do not include any explanation or additional text, just the JSON
|
||||
Reference 1: Area Definitions
|
||||
area/agent
|
||||
- Description: Issues related to the "brain" of the CLI. This includes the core agent logic, model quality, tool/function calling, and memory.
|
||||
- Example Issues:
|
||||
"I am not getting a reasonable or expected response."
|
||||
"The model is not calling the tool I expected."
|
||||
"The web search tool is not working as expected."
|
||||
"Feature request for a new built-in tool (e.g., read file, write file)."
|
||||
"The generated code is poor quality or incorrect."
|
||||
"The model seems stuck in a loop."
|
||||
"The response from the model is malformed (e.g., broken JSON, bad formatting)."
|
||||
"Concerns about unnecessary token consumption."
|
||||
"Issues with how memory or chat history is managed."
|
||||
"Issues with sub-agents."
|
||||
"Model is switching from one to another unexpectedly."
|
||||
|
||||
Categorization Guidelines:
|
||||
P0: Critical / Blocker
|
||||
- A P0 bug is a catastrophic failure that demands immediate attention.
|
||||
- To be a P0 it means almost all users are running into this issue and it is blocking users from being able to use the product.
|
||||
- You would see this in the form of many comments from different developers on the bug.
|
||||
- It represents a complete showstopper for a significant portion of users or for the development process itself.
|
||||
Impact:
|
||||
- Blocks development or testing for the entire team.
|
||||
- Major security vulnerability that could compromise user data or system integrity.
|
||||
- Causes data loss or corruption with no workaround.
|
||||
- Crashes the application or makes a core feature completely unusable for all or most users in a production environment. Will it cause severe quality degration? Is it preventing contributors from contributing to the repository or is it a release blocker?
|
||||
Qualifier: Is the main function of the software broken?
|
||||
Example: The gemini auth login command fails with an unrecoverable error, preventing any user from authenticating and using the rest of the CLI.
|
||||
P1: High
|
||||
- A P1 bug is a serious issue that significantly degrades the user experience or impacts a core feature.
|
||||
- While not a complete blocker, it's a major problem that needs a fast resolution. Feature requests are almost never P1.
|
||||
- Once again this would be affecting many users.
|
||||
- You would see this in the form of comments from different developers on the bug.
|
||||
Impact:
|
||||
- A core feature is broken or behaving incorrectly for a large number of users or large number of use cases.
|
||||
- Review the bug details and comments to try figure out if this issue affects a large set of use cases or if it's a narrow set of use cases.
|
||||
- Severe performance degradation making the application frustratingly slow.
|
||||
- No straightforward workaround exists, or the workaround is difficult and non-obvious.
|
||||
Qualifier: Is a key feature unusable or giving very wrong results?
|
||||
Example: Gemini CLI enters a loop when making read-many-files tool call. I am unable to break out of the loop and gemini doesn't follow instructions subsequently.
|
||||
P2: Medium
|
||||
- A P2 bug is a moderately impactful issue. It's a noticeable problem but doesn't prevent the use of the software's main functionality.
|
||||
Impact:
|
||||
- Affects a non-critical feature or a smaller, specific subset of users.
|
||||
- An inconvenient but functional workaround is available and easy to execute.
|
||||
- Noticeable UI/UX problems that don't break functionality but look unprofessional (e.g., elements are misaligned or overlapping).
|
||||
Qualifier: Is it an annoying but non-blocking problem?
|
||||
Example: An error message is unclear or contains a typo, causing user confusion but not halting their workflow.
|
||||
P3: Low
|
||||
- A P3 bug is a minor, low-impact issue that is trivial or cosmetic. It has little to no effect on the overall functionality of the application.
|
||||
Impact:
|
||||
- Minor cosmetic issues like color inconsistencies, typos in documentation, or slight alignment problems on a non-critical page.
|
||||
- An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
|
||||
Qualifier: Is it a "nice-to-fix" issue?
|
||||
Example: Spelling mistakes etc.
|
||||
Things you should know:
|
||||
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue
|
||||
- This product is designed to use different models eg.. using pro, downgrading to flash etc. when users report that they dont expect the model to change those would be categorized as feature requests.
|
||||
Definition of Areas
|
||||
area/ux:
|
||||
- Issues concerning user-facing elements like command usability, interactive features, help docs, and perceived performance.
|
||||
- I am seeing my screen flicker when using Gemini CLI
|
||||
- I am seeing the output malformed
|
||||
- Theme changes aren't taking effect
|
||||
- My keyboard inputs arent' being recognzied
|
||||
area/platform:
|
||||
- Issues related to installation, packaging, OS compatibility (Windows, macOS, Linux), and the underlying CLI framework.
|
||||
area/background: Issues related to long-running background tasks, daemons, and autonomous or proactive agent features.
|
||||
area/models:
|
||||
- i am not getting a response that is reasonable or expected. this can include things like
|
||||
- I am calling a tool and the tool is not performing as expected.
|
||||
- i am expecting a tool to be called and it is not getting called ,
|
||||
- Including experience when using
|
||||
- built-in tools (e.g., web search, code interpreter, read file, writefile, etc..),
|
||||
- Function calling issues should be under this area
|
||||
- i am getting responses from the model that are malformed.
|
||||
- Issues concerning Gemini quality of response and inference,
|
||||
- Issues talking about unnecessary token consumption.
|
||||
- Issues talking about Model getting stuck in a loop be watchful as this could be the root cause for issues that otherwise seem like model performance issues.
|
||||
- Memory compression
|
||||
- unexpected responses,
|
||||
- poor quality of generated code
|
||||
area/tools:
|
||||
- These are primarily issues related to Model Context Protocol
|
||||
- These are issues that mention MCP support
|
||||
- feature requests asking for support for new tools.
|
||||
area/core: Issues with fundamental components like command parsing, configuration management, session state, and the main API client logic. Introducing multi-modality
|
||||
area/contribution: Issues related to improving the developer contribution experience, such as CI/CD pipelines, build scripts, and test automation infrastructure.
|
||||
area/authentication: Issues related to user identity, login flows, API key handling, credential storage, and access token management, unable to sign in selecting wrong authentication path etc..
|
||||
area/security-privacy: Issues concerning vulnerability patching, dependency security, data sanitization, privacy controls, and preventing unauthorized data access.
|
||||
area/extensibility: Issues related to the plugin system, extension APIs, or making the CLI's functionality available in other applications, github actions, ide support etc..
|
||||
area/performance: Issues focused on model performance
|
||||
- Issues with running out of capacity,
|
||||
- 429 errors etc..
|
||||
- could also pertain to latency,
|
||||
- other general software performance like, memory usage, CPU consumption, and algorithmic efficiency.
|
||||
- Switching models from one to the other unexpectedly.
|
||||
area/enterprise
|
||||
- Description: Issues specific to enterprise-level features, including telemetry, policy, and licenses.
|
||||
- Example Issues:
|
||||
"Usage data is not appearing in our telemetry dashboard."
|
||||
"A user is able to perform an action that should be blocked by an admin policy."
|
||||
"Questions about billing, licensing tiers, or enterprise quotas."
|
||||
|
||||
area/non-interactive
|
||||
- Description: Issues related to using the CLI in automated or non-interactive environments (headless mode).
|
||||
- Example Issues:
|
||||
"Problems using the CLI as an SDK in another surface."
|
||||
"The CLI is behaving differently when run from a shell script vs. an interactive terminal."
|
||||
"GitHub action is failing."
|
||||
"I am having trouble running the CLI in headless mode"
|
||||
|
||||
area/core
|
||||
- Description: Issues with the fundamental CLI app itself. This includes the user interface (UI/UX), installation, OS compatibility, and performance.
|
||||
- Example Issues:
|
||||
"I am seeing my screen flicker when using the CLI."
|
||||
"The output in my terminal is malformed or unreadable."
|
||||
"Theme changes are not taking effect."
|
||||
"Keyboard inputs (e.g., arrow keys, Ctrl+C) are not being recognized."
|
||||
"The CLI failed to install or update."
|
||||
"An issue specific to running on Windows, macOS, or Linux."
|
||||
"Problems with command parsing, flags, or argument handling."
|
||||
"High CPU or memory usage by the CLI process."
|
||||
"Issues related to multi-modality (e.g., handling image inputs)."
|
||||
"Problems with the IDE integration connection or installation"
|
||||
|
||||
area/security
|
||||
- Description: Issues related to user authentication, authorization, data security, and privacy.
|
||||
- Example Issues:
|
||||
"I am unable to sign in."
|
||||
"The login flow is selecting the wrong authentication path"
|
||||
"Problems with API key handling or credential storage."
|
||||
"A report of a security vulnerability"
|
||||
"Concerns about data sanitization or potential data leaks."
|
||||
"Issues or requests related to privacy controls."
|
||||
"Preventing unauthorized data access."
|
||||
|
||||
area/platform
|
||||
- Description: Issues related to CI/CD, release management, testing, eval infrastructure, capacity, quota management, and sandbox environments.
|
||||
- Example Issues:
|
||||
"I am getting a 429 'Resource Exhausted' or 500-level server error."
|
||||
"General slowness or high latency from the service."
|
||||
"The build script is broken on the main branch."
|
||||
"Tests are failing in the CI/CD pipeline."
|
||||
"Issues with the release management or publishing process."
|
||||
"User is running out of capacity."
|
||||
"Problems specific to the sandbox or staging environments."
|
||||
"Questions about quota limits or requests for increases."
|
||||
|
||||
area/extensions
|
||||
- Description: Issues related to the extension ecosystem, including the marketplace and website.
|
||||
- Example Issues:
|
||||
"Bugs related to the extension marketplace website."
|
||||
"Issues with a specific extension."
|
||||
"Feature request for the extension ecosystem."
|
||||
|
||||
area/unknown
|
||||
- Description: Issues that do not clearly fit into any other defined area/ category, or where information is too limited to make a determination. Use this when no other area is appropriate.
|
||||
|
||||
- name: 'Apply Labels to Issue'
|
||||
if: |-
|
||||
@@ -267,54 +264,47 @@ jobs:
|
||||
LABELS_OUTPUT: '${{ steps.gemini_issue_analysis.outputs.summary }}'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
script: |-
|
||||
// Strip code block markers if present
|
||||
const rawLabels = process.env.LABELS_OUTPUT;
|
||||
core.info(`Raw labels JSON: ${rawLabels}`);
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |
|
||||
const rawOutput = process.env.LABELS_OUTPUT;
|
||||
core.info(`Raw output from model: ${rawOutput}`);
|
||||
let parsedLabels;
|
||||
try {
|
||||
const jsonMatch = rawLabels.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (!jsonMatch || !jsonMatch[1]) {
|
||||
throw new Error("Could not find a ```json ... ``` block in the output.");
|
||||
// First, try to parse the raw output as JSON.
|
||||
parsedLabels = JSON.parse(rawOutput);
|
||||
} catch (jsonError) {
|
||||
// If that fails, check for a markdown code block.
|
||||
core.warning(`Direct JSON parsing failed: ${jsonError.message}. Trying to extract from a markdown block.`);
|
||||
const jsonMatch = rawOutput.match(/```json\s*([\s\S]*?)\s*```/);
|
||||
if (jsonMatch && jsonMatch[1]) {
|
||||
try {
|
||||
parsedLabels = JSON.parse(jsonMatch[1].trim());
|
||||
} catch (markdownError) {
|
||||
core.setFailed(`Failed to parse JSON even after extracting from markdown block: ${markdownError.message}\nRaw output: ${rawOutput}`);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
core.setFailed(`Output is not valid JSON and does not contain a JSON markdown block.\nRaw output: ${rawOutput}`);
|
||||
return;
|
||||
}
|
||||
const jsonString = jsonMatch[1].trim();
|
||||
parsedLabels = JSON.parse(jsonString);
|
||||
core.info(`Parsed labels JSON: ${JSON.stringify(parsedLabels)}`);
|
||||
} catch (err) {
|
||||
core.setFailed(`Failed to parse labels JSON from Gemini output: ${err.message}\nRaw output: ${rawLabels}`);
|
||||
return;
|
||||
}
|
||||
|
||||
const issueNumber = parseInt(process.env.ISSUE_NUMBER);
|
||||
const explanation = parsedLabels.explanation || '';
|
||||
const labelsToSet = parsedLabels.labels_to_set || [];
|
||||
labelsToSet.push('status/bot-triaged');
|
||||
const labelsToAdd = parsedLabels.labels_to_set || [];
|
||||
|
||||
if (labelsToAdd.length !== 1) {
|
||||
core.setFailed(`Expected exactly 1 label (area/), but got ${labelsToAdd.length}. Labels: ${labelsToAdd.join(', ')}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set labels based on triage result
|
||||
if (labelsToSet.length > 0) {
|
||||
await github.rest.issues.setLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
labels: labelsToSet
|
||||
});
|
||||
const explanationInfo = explanation ? ` - ${explanation}` : '';
|
||||
core.info(`Successfully set labels for #${issueNumber}: ${labelsToSet.join(', ')}${explanationInfo}`);
|
||||
} else {
|
||||
// If no labels to set, leave the issue as is
|
||||
const explanationInfo = explanation ? ` - ${explanation}` : '';
|
||||
core.info(`No labels to set for #${issueNumber}, leaving as is${explanationInfo}`);
|
||||
}
|
||||
|
||||
if (explanation) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
body: explanation,
|
||||
});
|
||||
}
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
labels: labelsToAdd
|
||||
});
|
||||
core.info(`Successfully added labels for #${issueNumber}: ${labelsToAdd.join(', ')}`);
|
||||
|
||||
- name: 'Post Issue Analysis Failure Comment'
|
||||
if: |-
|
||||
@@ -324,7 +314,7 @@ jobs:
|
||||
RUN_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |-
|
||||
github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -81,7 +81,6 @@ jobs:
|
||||
"FIRESTORE_DATABASE_ID": "(default)",
|
||||
"GOOGLE_APPLICATION_CREDENTIALS": "${GOOGLE_APPLICATION_CREDENTIALS}"
|
||||
},
|
||||
"enabled": true,
|
||||
"timeout": 600000
|
||||
}
|
||||
},
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
name: '📋 Gemini Scheduled Issue Triage'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- 'opened'
|
||||
- 'reopened'
|
||||
schedule:
|
||||
- cron: '0 * * * *' # Runs every hour
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}'
|
||||
group: '${{ github.workflow }}-${{ github.event.number || github.run_id }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
@@ -35,7 +39,21 @@ jobs:
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
permission-issues: 'write'
|
||||
|
||||
- name: 'Get issue from event'
|
||||
if: |-
|
||||
${{ github.event_name == 'issues' }}
|
||||
id: 'get_issue_from_event'
|
||||
env:
|
||||
ISSUE_EVENT: '${{ toJSON(github.event.issue) }}'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
ISSUE_JSON=$(echo "$ISSUE_EVENT" | jq -c '[{number: .number, title: .title, body: .body}]')
|
||||
echo "issues_to_triage=${ISSUE_JSON}" >> "${GITHUB_OUTPUT}"
|
||||
echo "✅ Found issue #${{ github.event.issue.number }} from event to triage! 🎯"
|
||||
|
||||
- name: 'Find untriaged issues'
|
||||
if: |-
|
||||
${{ github.event_name == 'schedule' || github.event_name == 'workflow_dispatch' }}
|
||||
id: 'find_issues'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ steps.generate_token.outputs.token }}'
|
||||
@@ -43,22 +61,26 @@ jobs:
|
||||
run: |-
|
||||
set -euo pipefail
|
||||
|
||||
echo '🔍 Finding issues without labels...'
|
||||
NO_LABEL_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue no:label' --json number,title,body)"
|
||||
echo '🔍 Finding issues missing area labels...'
|
||||
NO_AREA_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue -label:area/core -label:area/agent -label:area/enterprise -label:area/non-interactive -label:area/security -label:area/platform -label:area/extensions -label:area/unknown' --limit 100 --json number,title,body)"
|
||||
|
||||
echo '🏷️ Finding issues that need triage...'
|
||||
NEED_TRIAGE_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search "is:open is:issue label:\"status/need-triage\"" --limit 1000 --json number,title,body)"
|
||||
echo '🔍 Finding issues missing kind labels...'
|
||||
NO_KIND_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue -label:kind/bug -label:kind/enhancement -label:kind/customer-issue -label:kind/question' --limit 100 --json number,title,body)"
|
||||
|
||||
echo '🏷️ Finding issues missing priority labels...'
|
||||
NO_PRIORITY_ISSUES="$(gh issue list --repo "${GITHUB_REPOSITORY}" \
|
||||
--search 'is:open is:issue -label:priority/p0 -label:priority/p1 -label:priority/p2 -label:priority/p3 -label:priority/unknown' --limit 100 --json number,title,body)"
|
||||
|
||||
echo '🔄 Merging and deduplicating issues...'
|
||||
ISSUES="$(echo "${NO_LABEL_ISSUES}" "${NEED_TRIAGE_ISSUES}" | jq -c -s 'add | unique_by(.number)')"
|
||||
ISSUES="$(echo "${NO_AREA_ISSUES}" "${NO_KIND_ISSUES}" "${NO_PRIORITY_ISSUES}" | jq -c -s 'add | unique_by(.number)')"
|
||||
|
||||
echo '📝 Setting output for GitHub Actions...'
|
||||
echo "issues_to_triage=${ISSUES}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
ISSUE_COUNT="$(echo "${ISSUES}" | jq 'length')"
|
||||
echo "✅ Found ${ISSUE_COUNT} issues to triage! 🎯"
|
||||
echo "✅ Found ${ISSUE_COUNT} unique issues to triage! 🎯"
|
||||
|
||||
- name: 'Get Repository Labels'
|
||||
id: 'get_labels'
|
||||
@@ -77,12 +99,13 @@ jobs:
|
||||
|
||||
- name: 'Run Gemini Issue Analysis'
|
||||
if: |-
|
||||
${{ steps.find_issues.outputs.issues_to_triage != '[]' }}
|
||||
(steps.get_issue_from_event.outputs.issues_to_triage != '' && steps.get_issue_from_event.outputs.issues_to_triage != '[]') ||
|
||||
(steps.find_issues.outputs.issues_to_triage != '' && steps.find_issues.outputs.issues_to_triage != '[]')
|
||||
uses: 'google-github-actions/run-gemini-cli@a3bf79042542528e91937b3a3a6fbc4967ee3c31' # ratchet:google-github-actions/run-gemini-cli@v0
|
||||
id: 'gemini_issue_analysis'
|
||||
env:
|
||||
GITHUB_TOKEN: '' # Do not pass any auth token here since this runs on untrusted inputs
|
||||
ISSUES_TO_TRIAGE: '${{ steps.find_issues.outputs.issues_to_triage }}'
|
||||
ISSUES_TO_TRIAGE: '${{ steps.get_issue_from_event.outputs.issues_to_triage || steps.find_issues.outputs.issues_to_triage }}'
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
AVAILABLE_LABELS: '${{ steps.get_labels.outputs.available_labels }}'
|
||||
with:
|
||||
@@ -116,38 +139,32 @@ jobs:
|
||||
1. You are only able to use the echo command. Review the available labels in the environment variable: "${AVAILABLE_LABELS}".
|
||||
2. Check environment variable for issues to triage: $ISSUES_TO_TRIAGE (JSON array of issues)
|
||||
3. Review the issue title, body and any comments provided in the environment variables.
|
||||
4. Identify the most relevant labels from the existing labels, focusing on kind/*, area/*, sub-area/* and priority/*.
|
||||
5. If the issue already has area/ label, dont try to change it. Similarly, if the issue already has a kind/ label don't change it. And if the issue already has a priority/ label do not change it for example:
|
||||
If an issue has area/core and kind/bug you will only add a priority/ label.
|
||||
Instead if an issue has no labels, you will could add one lable of each kind.
|
||||
4. Identify the most relevant labels from the existing labels, specifically focusing on area/*, kind/* and priority/*.
|
||||
5. Label Policy:
|
||||
- If the issue already has a kind/ label, do not change it.
|
||||
- If the issue already has a priority/ label, do not change it.
|
||||
- If the issue already has an area/ label, do not change it.
|
||||
- If any of these are missing, select exactly ONE appropriate label for the missing category.
|
||||
6. Identify other applicable labels based on the issue content, such as status/*, help wanted, good first issue, etc.
|
||||
7. For area/* and kind/* limit yourself to only the single most applicable label in each case.
|
||||
8. Give me a single short explanation about why you are selecting each label in the process.
|
||||
9. Output a JSON array of objects, each containing the issue number
|
||||
7. Give me a single short explanation about why you are selecting each label in the process.
|
||||
8. Output a JSON array of objects, each containing the issue number
|
||||
and the labels to add and remove, along with an explanation. For example:
|
||||
```
|
||||
[
|
||||
{
|
||||
"issue_number": 123,
|
||||
"labels_to_add": ["kind/bug", "priority/p2"],
|
||||
"labels_to_add": ["area/core", "kind/bug", "priority/p2"],
|
||||
"labels_to_remove": ["status/need-triage"],
|
||||
"explanation": "This issue is a bug that needs to be addressed with medium priority."
|
||||
},
|
||||
{
|
||||
"issue_number": 456,
|
||||
"labels_to_add": ["kind/enhancement"],
|
||||
"labels_to_remove": [],
|
||||
"explanation": "This issue is an enhancement request that could improve the user experience."
|
||||
"explanation": "This issue is a UI bug that needs to be addressed with medium priority."
|
||||
}
|
||||
]
|
||||
```
|
||||
If an issue cannot be classified, do not include it in the output array.
|
||||
10. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
|
||||
9. For each issue please check if CLI version is present, this is usually in the output of the /about command and will look like 0.1.5
|
||||
- Anything more than 6 versions older than the most recent should add the status/need-retesting label
|
||||
11. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
|
||||
- After identifying appropriate labels to an issue, add "status/need-triage" label to labels_to_remove in the output.
|
||||
12. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
|
||||
13. If you are uncertain and have not been able to apply one each of kind/, area/ and priority/ , apply the status/manual-triage label.
|
||||
10. If you see that the issue doesn't look like it has sufficient information recommend the status/need-information label and leave a comment politely requesting the relevant information, eg.. if repro steps are missing request for repro steps. if version information is missing request for version information into the explanation section below.
|
||||
11. If you think an issue might be a Priority/P0 do not apply the priority/p0 label. Instead apply a status/manual-triage label and include a note in your explanation.
|
||||
12. If you are uncertain about a category, use the area/unknown, kind/question, or priority/unknown labels as appropriate. If you are extremely uncertain, apply the status/manual-triage label.
|
||||
|
||||
## Guidelines
|
||||
|
||||
@@ -157,100 +174,46 @@ jobs:
|
||||
- Do not add comments or modify the issue content.
|
||||
- Do not remove the following labels maintainer, help wanted or good first issue.
|
||||
- Triage only the current issue.
|
||||
- Identify only one area/ label
|
||||
- Identify only one area/ label.
|
||||
- Identify only one kind/ label (Do not apply kind/duplicate or kind/parent-issue)
|
||||
- Identify all applicable sub-area/* and priority/* labels based on the issue content. It's ok to have multiple of these.
|
||||
- Identify only one priority/ label.
|
||||
- Once you categorize the issue if it needs information bump down the priority by 1 eg.. a p0 would become a p1 a p1 would become a p2. P2 and P3 can stay as is in this scenario.
|
||||
Categorization Guidelines:
|
||||
P0: Critical / Blocker
|
||||
- A P0 bug is a catastrophic failure that demands immediate attention.
|
||||
- To be a P0 it means almost all users are running into this issue and it is blocking users from being able to use the product.
|
||||
- You would see this in the form of many comments from different developers on the bug.
|
||||
- It represents a complete showstopper for a significant portion of users or for the development process itself.
|
||||
Impact:
|
||||
- Blocks development or testing for the entire team.
|
||||
- Major security vulnerability that could compromise user data or system integrity.
|
||||
- Causes data loss or corruption with no workaround.
|
||||
- Crashes the application or makes a core feature completely unusable for all or most users in a production environment. Will it cause severe quality degration?
|
||||
- Is it preventing contributors from contributing to the repository or is it a release blocker?
|
||||
Qualifier: Is the main function of the software broken?
|
||||
Example: The gemini auth login command fails with an unrecoverable error, preventing any user from authenticating and using the rest of the CLI.
|
||||
P1: High
|
||||
- A P1 bug is a serious issue that significantly degrades the user experience or impacts a core feature.
|
||||
- While not a complete blocker, it's a major problem that needs a fast resolution. Feature requests are almost never P1.
|
||||
- Once again this would be affecting many users.
|
||||
- You would see this in the form of comments from different developers on the bug.
|
||||
Impact:
|
||||
- A core feature is broken or behaving incorrectly for a large number of users or large number of use cases.
|
||||
- Review the bug details and comments to try figure out if this issue affects a large set of use cases or if it's a narrow set of use cases.
|
||||
- Severe performance degradation making the application frustratingly slow.
|
||||
- No straightforward workaround exists, or the workaround is difficult and non-obvious.
|
||||
Qualifier: Is a key feature unusable or giving very wrong results?
|
||||
Example: Gemini CLI enters a loop when making read-many-files tool call. I am unable to break out of the loop and gemini doesn't follow instructions subsequently.
|
||||
P2: Medium
|
||||
- A P2 bug is a moderately impactful issue. It's a noticeable problem but doesn't prevent the use of the software's main functionality.
|
||||
Impact:
|
||||
- Affects a non-critical feature or a smaller, specific subset of users.
|
||||
- An inconvenient but functional workaround is available and easy to execute.
|
||||
- Noticeable UI/UX problems that don't break functionality but look unprofessional (e.g., elements are misaligned or overlapping).
|
||||
Qualifier: Is it an annoying but non-blocking problem?
|
||||
Example: An error message is unclear or contains a typo, causing user confusion but not halting their workflow.
|
||||
P3: Low
|
||||
- A P3 bug is a minor, low-impact issue that is trivial or cosmetic. It has little to no effect on the overall functionality of the application.
|
||||
Impact:
|
||||
- Minor cosmetic issues like color inconsistencies, typos in documentation, or slight alignment problems on a non-critical page.
|
||||
- An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
|
||||
Qualifier: Is it a "nice-to-fix" issue?
|
||||
Example: Spelling mistakes etc.
|
||||
|
||||
Categorization Guidelines (Priority):
|
||||
P0 - Urgent Blocking Issues:
|
||||
- DO NOT APPLY THIS LABEL AUTOMATICALLY. Use status/manual-triage instead.
|
||||
- Definition: Urgent, block a significant percentage of the user base, and prevent frequent use of the Gemini CLI.
|
||||
- This includes core stability blockers (e.g., authentication failures, broken upgrades), critical crashes, and P0 security vulnerabilities.
|
||||
- Impact: Blocks development or testing for the entire team; Major security vulnerability; Causes data loss or corruption with no workaround; Crashes the application or makes a core feature completely unusable for all or most users.
|
||||
- Qualifier: Is the main function of the software broken?
|
||||
P1 - High-Impact Issues:
|
||||
- Definition: Affect a large number of users, blocking them from using parts of the Gemini CLI, or make the CLI frequently unusable even with workarounds available.
|
||||
- Impact: A core feature is broken or behaving incorrectly for a large number of users or use cases; Severe performance degradation; No straightforward workaround exists.
|
||||
- Qualifier: Is a key feature unusable or giving very wrong results?
|
||||
P2 - Significant Issues:
|
||||
- Definition: Affect some users significantly, such as preventing the use of certain features or authentication types.
|
||||
- Can also be issues that many users complain about, causing annoyance or hindering daily use.
|
||||
- Impact: Affects a non-critical feature or a smaller, specific subset of users; An inconvenient but functional workaround is available; Noticeable UI/UX problems that look unprofessional.
|
||||
- Qualifier: Is it an annoying but non-blocking problem?
|
||||
P3 - Low-Impact Issues:
|
||||
- Definition: Typically usability issues that cause annoyance to a limited user base.
|
||||
- Includes feature requests that could be addressed in the near future and may be suitable for community contributions.
|
||||
- Impact: Minor cosmetic issues; An edge-case bug that is very difficult to reproduce and affects a tiny fraction of users.
|
||||
- Qualifier: Is it a "nice-to-fix" issue?
|
||||
|
||||
Categorization Guidelines (Area):
|
||||
area/agent: Core Agent, Tools, Memory, Sub-Agents, Hooks, Agent Quality
|
||||
area/core: User Interface, OS Support, Core Functionality
|
||||
area/enterprise: Telemetry, Policy, Quota / Licensing
|
||||
area/extensions: Gemini CLI extensions capability
|
||||
area/non-interactive: GitHub Actions, SDK, 3P Integrations, Shell Scripting, Command line automation
|
||||
area/platform: Build infra, Release mgmt, Testing, Eval infra, Capacity, Quota mgmt
|
||||
area/security: security related issues
|
||||
|
||||
Additional Context:
|
||||
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue
|
||||
- This product is designed to use different models eg.. using pro, downgrading to flash etc.
|
||||
- When users report that they dont expect the model to change those would be categorized as feature requests.
|
||||
Definition of Areas
|
||||
area/ux:
|
||||
- Issues concerning user-facing elements like command usability, interactive features, help docs, and perceived performance.
|
||||
- I am seeing my screen flicker when using Gemini CLI
|
||||
- I am seeing the output malformed
|
||||
- Theme changes aren't taking effect
|
||||
- My keyboard inputs arent' being recognzied
|
||||
area/platform:
|
||||
- Issues related to installation, packaging, OS compatibility (Windows, macOS, Linux), and the underlying CLI framework.
|
||||
area/background: Issues related to long-running background tasks, daemons, and autonomous or proactive agent features.
|
||||
area/models:
|
||||
- i am not getting a response that is reasonable or expected. this can include things like
|
||||
- I am calling a tool and the tool is not performing as expected.
|
||||
- i am expecting a tool to be called and it is not getting called ,
|
||||
- Including experience when using
|
||||
- built-in tools (e.g., web search, code interpreter, read file, writefile, etc..),
|
||||
- Function calling issues should be under this area
|
||||
- i am getting responses from the model that are malformed.
|
||||
- Issues concerning Gemini quality of response and inference,
|
||||
- Issues talking about unnecessary token consumption.
|
||||
- Issues talking about Model getting stuck in a loop be watchful as this could be the root cause for issues that otherwise seem like model performance issues.
|
||||
- Memory compression
|
||||
- unexpected responses,
|
||||
- poor quality of generated code
|
||||
area/tools:
|
||||
- These are primarily issues related to Model Context Protocol
|
||||
- These are issues that mention MCP support
|
||||
- feature requests asking for support for new tools.
|
||||
area/core:
|
||||
- Issues with fundamental components like command parsing, configuration management, session state, and the main API client logic. Introducing multi-modality
|
||||
area/contribution:
|
||||
- Issues related to improving the developer contribution experience, such as CI/CD pipelines, build scripts, and test automation infrastructure.
|
||||
area/authentication:
|
||||
- Issues related to user identity, login flows, API key handling, credential storage, and access token management, unable to sign in selecting wrong authentication path etc..
|
||||
area/security-privacy:
|
||||
- Issues concerning vulnerability patching, dependency security, data sanitization, privacy controls, and preventing unauthorized data access.
|
||||
area/extensibility:
|
||||
- Issues related to the plugin system, extension APIs, or making the CLI's functionality available in other applications, github actions, ide support etc..
|
||||
area/performance:
|
||||
- Issues focused on model performance
|
||||
- Issues with running out of capacity,
|
||||
- 429 errors etc..
|
||||
- could also pertain to latency,
|
||||
- other general software performance like, memory usage, CPU consumption, and algorithmic efficiency.
|
||||
- Switching models from one to the other unexpectedly.
|
||||
- If users are talking about issues where the model gets downgraded from pro to flash then i want you to categorize that as a performance issue.
|
||||
- This product is designed to use different models eg.. using pro, downgrading to flash etc.
|
||||
- When users report that they dont expect the model to change those would be categorized as feature requests.
|
||||
|
||||
- name: 'Apply Labels to Issues'
|
||||
if: |-
|
||||
@@ -300,24 +263,6 @@ jobs:
|
||||
core.info(`Successfully added labels for #${issueNumber}: ${labelsToAdd.join(', ')}${explanation}`);
|
||||
}
|
||||
|
||||
if (entry.labels_to_remove && entry.labels_to_remove.length > 0) {
|
||||
for (const label of entry.labels_to_remove) {
|
||||
try {
|
||||
await github.rest.issues.removeLabel({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issueNumber,
|
||||
name: label
|
||||
});
|
||||
} catch (error) {
|
||||
if (error.status !== 404) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
core.info(`Successfully removed labels for #${issueNumber}: ${entry.labels_to_remove.join(', ')}`);
|
||||
}
|
||||
|
||||
if (entry.explanation) {
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
|
||||
@@ -39,3 +39,7 @@ jobs:
|
||||
GITHUB_REPOSITORY: '${{ github.repository }}'
|
||||
run: |-
|
||||
./.github/scripts/pr-triage.sh
|
||||
# If prs_needing_comment is empty, set it to [] explicitly for downstream steps
|
||||
if [[ -z "$(grep 'prs_needing_comment' "${GITHUB_OUTPUT}" | cut -d'=' -f2-)" ]]; then
|
||||
echo "prs_needing_comment=[]" >> "${GITHUB_OUTPUT}"
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
name: '🔒 Gemini Scheduled Stale Issue Closer'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Every Sunday at midnight UTC
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Run in dry-run mode (no changes applied)'
|
||||
required: false
|
||||
default: false
|
||||
type: 'boolean'
|
||||
|
||||
concurrency:
|
||||
group: '${{ github.workflow }}'
|
||||
cancel-in-progress: true
|
||||
|
||||
defaults:
|
||||
run:
|
||||
shell: 'bash'
|
||||
|
||||
jobs:
|
||||
close-stale-issues:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
uses: 'actions/create-github-app-token@v1'
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
permission-issues: 'write'
|
||||
|
||||
- name: 'Process Stale Issues'
|
||||
uses: 'actions/github-script@v7'
|
||||
env:
|
||||
DRY_RUN: '${{ inputs.dry_run }}'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token }}'
|
||||
script: |
|
||||
const dryRun = process.env.DRY_RUN === 'true';
|
||||
if (dryRun) {
|
||||
core.info('DRY RUN MODE ENABLED: No changes will be applied.');
|
||||
}
|
||||
const batchLabel = 'Stale';
|
||||
|
||||
const threeMonthsAgo = new Date();
|
||||
threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3);
|
||||
|
||||
const tenDaysAgo = new Date();
|
||||
tenDaysAgo.setDate(tenDaysAgo.getDate() - 10);
|
||||
|
||||
core.info(`Cutoff date for creation: ${threeMonthsAgo.toISOString()}`);
|
||||
core.info(`Cutoff date for updates: ${tenDaysAgo.toISOString()}`);
|
||||
|
||||
const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open created:<${threeMonthsAgo.toISOString()}`;
|
||||
core.info(`Searching with query: ${query}`);
|
||||
|
||||
const itemsToCheck = await github.paginate(github.rest.search.issuesAndPullRequests, {
|
||||
q: query,
|
||||
sort: 'created',
|
||||
order: 'asc',
|
||||
per_page: 100
|
||||
});
|
||||
|
||||
core.info(`Found ${itemsToCheck.length} open issues to check.`);
|
||||
|
||||
let processedCount = 0;
|
||||
|
||||
for (const issue of itemsToCheck) {
|
||||
const createdAt = new Date(issue.created_at);
|
||||
const updatedAt = new Date(issue.updated_at);
|
||||
const reactionCount = issue.reactions.total_count;
|
||||
|
||||
// Basic thresholds
|
||||
if (reactionCount >= 5) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if it has a maintainer label
|
||||
if (issue.labels.some(label => label.name.toLowerCase().includes('maintainer'))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let isStale = updatedAt < tenDaysAgo;
|
||||
|
||||
// If apparently active, check if it's only bot activity
|
||||
if (!isStale) {
|
||||
try {
|
||||
const comments = await github.rest.issues.listComments({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
per_page: 100,
|
||||
sort: 'created',
|
||||
direction: 'desc'
|
||||
});
|
||||
|
||||
const lastHumanComment = comments.data.find(comment => comment.user.type !== 'Bot');
|
||||
if (lastHumanComment) {
|
||||
isStale = new Date(lastHumanComment.created_at) < tenDaysAgo;
|
||||
} else {
|
||||
// No human comments. Check if creator is human.
|
||||
if (issue.user.type !== 'Bot') {
|
||||
isStale = createdAt < tenDaysAgo;
|
||||
} else {
|
||||
isStale = true; // Bot created, only bot comments
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
core.warning(`Failed to fetch comments for issue #${issue.number}: ${error.message}`);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (isStale) {
|
||||
processedCount++;
|
||||
const message = `Closing stale issue #${issue.number}: "${issue.title}" (${issue.html_url})`;
|
||||
core.info(message);
|
||||
|
||||
if (!dryRun) {
|
||||
// Add label
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [batchLabel]
|
||||
});
|
||||
|
||||
// Add comment
|
||||
await github.rest.issues.createComment({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
body: 'Hello! As part of our effort to keep our backlog manageable and focus on the most active issues, we are tidying up older reports.\n\nIt looks like this issue hasn\'t been active for a while, so we are closing it for now. However, if you are still experiencing this bug on the latest stable build, please feel free to comment on this issue or create a new one with updated details.\n\nThank you for your contribution!'
|
||||
});
|
||||
|
||||
// Close issue
|
||||
await github.rest.issues.update({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: issue.number,
|
||||
state: 'closed',
|
||||
state_reason: 'not_planned'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
core.info(`\nTotal issues processed: ${processedCount}`);
|
||||
@@ -0,0 +1,46 @@
|
||||
name: '🏷️ Issue Opened Labeler'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- 'opened'
|
||||
|
||||
jobs:
|
||||
label-issue:
|
||||
runs-on: 'ubuntu-latest'
|
||||
if: |-
|
||||
${{ github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli' }}
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
env:
|
||||
APP_ID: '${{ secrets.APP_ID }}'
|
||||
if: |-
|
||||
${{ env.APP_ID != '' }}
|
||||
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
|
||||
- name: 'Add need-triage label'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |-
|
||||
const { data: issue } = await github.rest.issues.get({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
});
|
||||
|
||||
const hasLabel = issue.labels.some(l => l.name === 'status/need-triage');
|
||||
if (!hasLabel) {
|
||||
await github.rest.issues.addLabels({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: ['status/need-triage']
|
||||
});
|
||||
} else {
|
||||
core.info('Issue already has status/need-triage label. Skipping.');
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
name: 'Label Child Issues for Project Rollup'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: ['opened', 'edited', 'reopened']
|
||||
schedule:
|
||||
- cron: '0 * * * *' # Run every hour
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
issues: 'write'
|
||||
contents: 'read'
|
||||
|
||||
jobs:
|
||||
# Event-based: Quick reaction to new/edited issues in THIS repo
|
||||
labeler:
|
||||
if: "github.event_name == 'issues'"
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run Multi-Repo Sync Script'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: 'node .github/scripts/sync-maintainer-labels.cjs'
|
||||
|
||||
# Scheduled/Manual: Recursive sync across multiple repos
|
||||
sync-maintainer-labels:
|
||||
if: "github.event_name == 'schedule' || github.event_name == 'workflow_dispatch'"
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@v4'
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@v4'
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run Multi-Repo Sync Script'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: 'node .github/scripts/sync-maintainer-labels.cjs'
|
||||
@@ -0,0 +1,119 @@
|
||||
name: '🏷️ Enforce Restricted Label Permissions'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types:
|
||||
- 'labeled'
|
||||
- 'unlabeled'
|
||||
|
||||
jobs:
|
||||
enforce-label:
|
||||
# Run this job only when restricted labels are changed
|
||||
if: |-
|
||||
${{ (github.event.label.name == 'help wanted' || github.event.label.name == 'status/need-triage' || github.event.label.name == '🔒 maintainer only') &&
|
||||
(github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli') }}
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
env:
|
||||
APP_ID: '${{ secrets.APP_ID }}'
|
||||
if: |-
|
||||
${{ env.APP_ID != '' }}
|
||||
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
|
||||
- name: 'Check if user is in the maintainers team'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |-
|
||||
const org = context.repo.owner;
|
||||
const username = context.payload.sender.login;
|
||||
const team_slug = 'gemini-cli-maintainers';
|
||||
const action = context.payload.action; // 'labeled' or 'unlabeled'
|
||||
const labelName = context.payload.label.name;
|
||||
|
||||
// Skip if the change was made by a bot to avoid infinite loops
|
||||
if (username === 'github-actions[bot]' || username === 'gemini-cli[bot]' || username.endsWith('[bot]')) {
|
||||
core.info('Change made by a bot. Skipping.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Check repository permission level directly.
|
||||
// This is more robust than team membership as it doesn't require Org-level read permissions
|
||||
// and correctly handles Repo Admins/Writers who might not be in the specific team.
|
||||
const { data: { permission } } = await github.rest.repos.getCollaboratorPermissionLevel({
|
||||
owner: org,
|
||||
repo: context.repo.repo,
|
||||
username,
|
||||
});
|
||||
|
||||
if (permission === 'admin' || permission === 'write') {
|
||||
core.info(`${username} has '${permission}' permission. Allowed.`);
|
||||
return;
|
||||
}
|
||||
|
||||
core.info(`${username} has '${permission}' permission (needs 'write' or 'admin'). Reverting '${action}' action for '${labelName}' label.`);
|
||||
} catch (error) {
|
||||
core.error(`Failed to check permissions for ${username}: ${error.message}`);
|
||||
// Fall through to revert logic if we can't verify permissions (fail safe)
|
||||
}
|
||||
|
||||
// If we are here, the user is NOT authorized.
|
||||
if (true) { // wrapping block to preserve variable scope if needed
|
||||
if (action === 'labeled') {
|
||||
// 1. Remove the label if added by a non-maintainer
|
||||
await github.rest.issues.removeLabel ({
|
||||
owner: org,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
name: labelName
|
||||
});
|
||||
|
||||
// 2. Post a polite comment
|
||||
const comment = `
|
||||
Hi @${username}, thank you for your interest in helping triage issues!
|
||||
|
||||
The \`${labelName}\` label is reserved for project maintainers to apply. This helps us ensure that an issue is ready and properly vetted for community contribution.
|
||||
|
||||
A maintainer will review this issue soon. Please see our [CONTRIBUTING.md](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md) for more details on our labeling process.
|
||||
`.trim().replace(/^[ ]+/gm, '');
|
||||
|
||||
await github.rest.issues.createComment ({
|
||||
owner: org,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: comment
|
||||
});
|
||||
} else if (action === 'unlabeled') {
|
||||
// 1. Add the label back if removed by a non-maintainer
|
||||
await github.rest.issues.addLabels ({
|
||||
owner: org,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
labels: [labelName]
|
||||
});
|
||||
|
||||
// 2. Post a polite comment
|
||||
const comment = `
|
||||
Hi @${username}, it looks like the \`${labelName}\` label was removed.
|
||||
|
||||
This label is managed by project maintainers. We've added it back to ensure the issue remains visible to potential contributors until a maintainer decides otherwise.
|
||||
|
||||
Thank you for your understanding!
|
||||
`.trim().replace(/^[ ]+/gm, '');
|
||||
|
||||
await github.rest.issues.createComment ({
|
||||
owner: org,
|
||||
repo: context.repo.repo,
|
||||
issue_number: context.issue.number,
|
||||
body: comment
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
name: 'Label Workstream Rollup'
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: ['opened', 'edited', 'reopened']
|
||||
schedule:
|
||||
- cron: '0 * * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
labeler:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Check for Parent Workstream and Apply Label'
|
||||
uses: 'actions/github-script@v7'
|
||||
with:
|
||||
script: |
|
||||
const labelToAdd = 'workstream-rollup';
|
||||
|
||||
// Allow-list of parent issue URLs
|
||||
const allowedParentUrls = [
|
||||
'https://github.com/google-gemini/gemini-cli/issues/15374',
|
||||
'https://github.com/google-gemini/gemini-cli/issues/15456',
|
||||
'https://github.com/google-gemini/gemini-cli/issues/15324'
|
||||
];
|
||||
|
||||
async function getIssueParent(owner, repo, number) {
|
||||
const query = `
|
||||
query($owner:String!, $repo:String!, $number:Int!) {
|
||||
repository(owner:$owner, name:$repo) {
|
||||
issue(number:$number) {
|
||||
parent {
|
||||
url
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
try {
|
||||
const result = await github.graphql(query, { owner, repo, number });
|
||||
return result.repository.issue.parent ? result.repository.issue.parent.url : null;
|
||||
} catch (error) {
|
||||
console.error(`Failed to fetch parent for #${number}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which issues to process
|
||||
let issuesToProcess = [];
|
||||
|
||||
if (context.eventName === 'issues') {
|
||||
// Context payload for 'issues' event already has the issue object
|
||||
issuesToProcess.push({
|
||||
number: context.payload.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
});
|
||||
} else {
|
||||
// For schedule/dispatch, fetch open issues (lightweight list)
|
||||
console.log(`Running for event: ${context.eventName}. Fetching open issues...`);
|
||||
const openIssues = await github.paginate(github.rest.issues.listForRepo, {
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
state: 'open'
|
||||
});
|
||||
issuesToProcess = openIssues.map(i => ({
|
||||
number: i.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo
|
||||
}));
|
||||
}
|
||||
|
||||
console.log(`Processing ${issuesToProcess.length} issue(s)...`);
|
||||
|
||||
for (const issue of issuesToProcess) {
|
||||
const parentUrl = await getIssueParent(issue.owner, issue.repo, issue.number);
|
||||
|
||||
if (parentUrl && allowedParentUrls.includes(parentUrl)) {
|
||||
console.log(`SUCCESS: Issue #${issue.number} is a direct child of ${parentUrl}. Adding label.`);
|
||||
await github.rest.issues.addLabels({
|
||||
owner: issue.owner,
|
||||
repo: issue.repo,
|
||||
issue_number: issue.number,
|
||||
labels: [labelToAdd]
|
||||
});
|
||||
} else {
|
||||
// logging only for single execution to avoid spam
|
||||
if (context.eventName === 'issues') {
|
||||
console.log(`Issue #${issue.number} parent is ${parentUrl || 'None'}. No action.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
name: 'Links'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ['main']
|
||||
pull_request:
|
||||
branches: ['main']
|
||||
repository_dispatch:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '00 18 * * *'
|
||||
|
||||
jobs:
|
||||
linkChecker:
|
||||
if: |-
|
||||
${{ github.repository == 'google-gemini/gemini-cli' }}
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
|
||||
- name: 'Link Checker'
|
||||
id: 'lychee'
|
||||
uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1
|
||||
with:
|
||||
args: '--verbose --no-progress --accept 200,503 ./**/*.md'
|
||||
@@ -1,84 +0,0 @@
|
||||
name: 'Nightly Release'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to release from.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run Tests'
|
||||
uses: './.github/actions/run-tests'
|
||||
with:
|
||||
force_skip_tests: '${{ github.event.inputs.force_skip_tests }}'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
|
||||
- name: 'Get Nightly Version'
|
||||
id: 'nightly_version'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
VERSION_JSON=$(node scripts/get-release-version.js --type=nightly)
|
||||
echo "RELEASE_TAG=$(echo "${VERSION_JSON}" | jq -r .releaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
echo "RELEASE_VERSION=$(echo "${VERSION_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
|
||||
echo "NPM_TAG=$(echo "${VERSION_JSON}" | jq -r .npmTag)" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_TAG=$(echo "${VERSION_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Publish Release'
|
||||
uses: './.github/actions/publish-release'
|
||||
with:
|
||||
release-version: '${{ steps.nightly_version.outputs.RELEASE_VERSION }}'
|
||||
release-tag: '${{ steps.nightly_version.outputs.RELEASE_TAG }}'
|
||||
npm-tag: '${{ steps.nightly_version.outputs.NPM_TAG }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
previous-tag: '${{ steps.nightly_version.outputs.PREVIOUS_TAG }}'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry_run == false }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.nightly_version.outputs.RELEASE_TAG }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |
|
||||
gh issue create \
|
||||
--title 'Nightly Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')' \
|
||||
--body 'The nightly-release workflow failed. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'kind/bug,release-failure,priority/p0'
|
||||
@@ -1,56 +0,0 @@
|
||||
name: 'Patch from Comment'
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: ['created']
|
||||
|
||||
jobs:
|
||||
slash-command:
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Slash Command Dispatch'
|
||||
id: 'slash_command'
|
||||
uses: 'peter-evans/slash-command-dispatch@40877f718dce0101edfc7aea2b3800cc192f9ed5'
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
commands: 'patch'
|
||||
permission: 'write'
|
||||
issue-type: 'pull-request'
|
||||
static-args: |
|
||||
dry_run=false
|
||||
|
||||
- name: 'Get PR Status'
|
||||
id: 'pr_status'
|
||||
if: "steps.slash_command.outputs.dispatched == 'true'"
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
gh pr view "${{ github.event.issue.number }}" --json mergeCommit,state > pr_status.json
|
||||
echo "MERGE_COMMIT_SHA=$(jq -r .mergeCommit.oid pr_status.json)" >> "$GITHUB_OUTPUT"
|
||||
echo "STATE=$(jq -r .state pr_status.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Dispatch if Merged'
|
||||
if: "steps.pr_status.outputs.STATE == 'MERGED'"
|
||||
uses: 'actions/github-script@00f12e3e20659f42342b1c0226afda7f7c042325'
|
||||
with:
|
||||
script: |
|
||||
const args = JSON.parse('${{ steps.slash_command.outputs.command-arguments }}');
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'create-patch-pr.yml',
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
commit: '${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}',
|
||||
channel: args.channel,
|
||||
dry_run: args.dry_run
|
||||
}
|
||||
})
|
||||
|
||||
- name: 'Comment on Failure'
|
||||
if: "steps.pr_status.outputs.STATE != 'MERGED'"
|
||||
uses: 'peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d'
|
||||
with:
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
:x: The `/patch` command failed. This pull request must be merged before a patch can be created.
|
||||
@@ -1,94 +0,0 @@
|
||||
name: 'Patch Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
description: 'The type of release to patch from.'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'stable'
|
||||
- 'preview'
|
||||
ref:
|
||||
description: 'The branch or ref (full git sha) to release from.'
|
||||
required: true
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
dry_run:
|
||||
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment:
|
||||
name: 'production-release'
|
||||
url: '${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
if: |-
|
||||
${{ github.repository == 'google-gemini/gemini-cli' }}
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
id-token: 'write'
|
||||
issues: 'write' # For creating issues on failure
|
||||
outputs:
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref || github.sha }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: |-
|
||||
npm ci
|
||||
|
||||
- name: 'Get the version'
|
||||
id: 'version'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |-
|
||||
VERSION_JSON="$(node scripts/get-release-version.js --type=patch --patch-from=${{ github.event.inputs.type }})"
|
||||
echo "${VERSION_JSON}"
|
||||
echo "RELEASE_TAG=$(echo "${VERSION_JSON}" | jq -r .releaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
echo "RELEASE_VERSION=$(echo "${VERSION_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
|
||||
echo "NPM_TAG=$(echo "${VERSION_JSON}" | jq -r .npmTag)" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_TAG=$(echo "${VERSION_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Print Calculated Version'
|
||||
run: |-
|
||||
echo "Calculated version: ${{ steps.version.outputs.RELEASE_VERSION }}"
|
||||
|
||||
- name: 'Run Tests'
|
||||
uses: './.github/actions/run-tests'
|
||||
with:
|
||||
force_skip_tests: '${{ github.event.inputs.force_skip_tests }}'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
|
||||
- name: 'Publish Release'
|
||||
uses: './.github/actions/publish-release'
|
||||
with:
|
||||
release-version: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
||||
release-tag: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
npm-tag: '${{ steps.version.outputs.NPM_TAG }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
previous-tag: '${{ steps.version.outputs.PREVIOUS_TAG }}'
|
||||
@@ -0,0 +1,83 @@
|
||||
name: '🏷️ PR Contribution Guidelines Notifier'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- 'opened'
|
||||
|
||||
jobs:
|
||||
notify-process-change:
|
||||
runs-on: 'ubuntu-latest'
|
||||
if: |-
|
||||
github.repository == 'google-gemini/gemini-cli' || github.repository == 'google-gemini/maintainers-gemini-cli'
|
||||
permissions:
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Generate GitHub App Token'
|
||||
id: 'generate_token'
|
||||
env:
|
||||
APP_ID: '${{ secrets.APP_ID }}'
|
||||
if: |-
|
||||
${{ env.APP_ID != '' }}
|
||||
uses: 'actions/create-github-app-token@a8d616148505b5069dccd32f177bb87d7f39123b' # ratchet:actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: '${{ secrets.APP_ID }}'
|
||||
private-key: '${{ secrets.PRIVATE_KEY }}'
|
||||
|
||||
- name: 'Check membership and post comment'
|
||||
uses: 'actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea'
|
||||
with:
|
||||
github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}'
|
||||
script: |-
|
||||
const org = context.repo.owner;
|
||||
const repo = context.repo.repo;
|
||||
const username = context.payload.pull_request.user.login;
|
||||
const pr_number = context.payload.pull_request.number;
|
||||
|
||||
// 1. Check if the PR author is a maintainer
|
||||
const authorAssociation = context.payload.pull_request.author_association;
|
||||
if (['OWNER', 'MEMBER', 'COLLABORATOR'].includes(authorAssociation)) {
|
||||
core.info(`${username} is a maintainer (Association: ${authorAssociation}). No notification needed.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. Check if the PR is already associated with an issue
|
||||
const query = `
|
||||
query($owner:String!, $repo:String!, $number:Int!) {
|
||||
repository(owner:$owner, name:$repo) {
|
||||
pullRequest(number:$number) {
|
||||
closingIssuesReferences(first: 1) {
|
||||
totalCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
const variables = { owner: org, repo: repo, number: pr_number };
|
||||
const result = await github.graphql(query, variables);
|
||||
const issueCount = result.repository.pullRequest.closingIssuesReferences.totalCount;
|
||||
|
||||
if (issueCount > 0) {
|
||||
core.info(`PR #${pr_number} is already associated with an issue. No notification needed.`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Post the notification comment
|
||||
core.info(`${username} is not a maintainer and PR #${pr_number} has no linked issue. Posting notification.`);
|
||||
|
||||
const comment = `
|
||||
Hi @${username}, thank you so much for your contribution to Gemini CLI! We really appreciate the time and effort you've put into this.
|
||||
|
||||
We're making some updates to our contribution process to improve how we track and review changes. Please take a moment to review our recent discussion post: [Improving Our Contribution Process & Introducing New Guidelines](https://github.com/google-gemini/gemini-cli/discussions/16706).
|
||||
|
||||
Key Update: Starting **January 26, 2026**, the Gemini CLI project will require all pull requests to be associated with an existing issue. Any pull requests not linked to an issue by that date will be automatically closed.
|
||||
|
||||
Thank you for your understanding and for being a part of our community!
|
||||
`.trim().replace(/^[ ]+/gm, '');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner: org,
|
||||
repo: repo,
|
||||
issue_number: pr_number,
|
||||
body: comment
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
name: 'Release: Change Tags'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'The package version to tag (e.g., 0.5.0-preview-2). This version must already exist on the npm registry.'
|
||||
required: true
|
||||
type: 'string'
|
||||
channel:
|
||||
description: 'The npm dist-tag to apply (e.g., latest, preview, nightly).'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'dev'
|
||||
- 'latest'
|
||||
- 'preview'
|
||||
- 'nightly'
|
||||
dry-run:
|
||||
description: 'Whether to run in dry-run mode.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: true
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
change-tags:
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@v4'
|
||||
with:
|
||||
ref: '${{ github.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: 'Change tag'
|
||||
uses: './.github/actions/tag-npm-release'
|
||||
with:
|
||||
channel: '${{ github.event.inputs.channel }}'
|
||||
version: '${{ github.event.inputs.version }}'
|
||||
dry-run: '${{ github.event.inputs.dry-run }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
working-directory: '.'
|
||||
@@ -0,0 +1,135 @@
|
||||
name: 'Release: Manual'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'The version to release (e.g., v0.1.11). Must be a valid semver string with a "v" prefix.'
|
||||
required: true
|
||||
type: 'string'
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to release from.'
|
||||
required: true
|
||||
type: 'string'
|
||||
npm_channel:
|
||||
description: 'The npm channel to publish to'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'dev'
|
||||
- 'preview'
|
||||
- 'nightly'
|
||||
- 'latest'
|
||||
default: 'latest'
|
||||
dry_run:
|
||||
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
skip_github_release:
|
||||
description: 'Select to skip creating a GitHub release (only used when environment is PROD)'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Checkout Release Code'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Debug Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: './release/.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
working-directory: './release'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Prepare Release Info'
|
||||
id: 'release_info'
|
||||
working-directory: './release'
|
||||
env:
|
||||
INPUT_VERSION: '${{ github.event.inputs.version }}'
|
||||
run: |
|
||||
RELEASE_VERSION="${INPUT_VERSION}"
|
||||
echo "RELEASE_VERSION=${RELEASE_VERSION#v}" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_TAG=$(git describe --tags --abbrev=0)" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Run Tests'
|
||||
if: "${{github.event.inputs.force_skip_tests != 'true'}}"
|
||||
uses: './.github/actions/run-tests'
|
||||
with:
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
working-directory: './release'
|
||||
|
||||
- name: 'Publish Release'
|
||||
uses: './.github/actions/publish-release'
|
||||
with:
|
||||
force-skip-tests: '${{ github.event.inputs.force_skip_tests }}'
|
||||
release-version: '${{ steps.release_info.outputs.RELEASE_VERSION }}'
|
||||
release-tag: '${{ github.event.inputs.version }}'
|
||||
npm-tag: '${{ github.event.inputs.npm_channel }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
previous-tag: '${{ steps.release_info.outputs.PREVIOUS_TAG }}'
|
||||
skip-github-release: '${{ github.event.inputs.skip_github_release }}'
|
||||
working-directory: './release'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
npm-registry-publish-url: '${{ vars.NPM_REGISTRY_PUBLISH_URL }}'
|
||||
npm-registry-url: '${{ vars.NPM_REGISTRY_URL }}'
|
||||
npm-registry-scope: '${{ vars.NPM_REGISTRY_SCOPE }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry_run == false }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ github.event.inputs.version }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |
|
||||
gh issue create \
|
||||
--title 'Manual Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')' \
|
||||
--body 'The manual release workflow failed. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'release-failure,priority/p0'
|
||||
@@ -0,0 +1,161 @@
|
||||
name: 'Release: Nightly'
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
dry_run:
|
||||
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: true
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to release from.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
if: "github.repository == 'google-gemini/gemini-cli'"
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Checkout Release Code'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: './release/.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
working-directory: './release'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(github.event.inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'Calculate Release Variables'
|
||||
id: 'vars'
|
||||
uses: './.github/actions/calculate-vars'
|
||||
with:
|
||||
dry_run: '${{ github.event.inputs.dry_run }}'
|
||||
|
||||
- name: 'Print Calculated vars'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_VARS: '${{ toJSON(steps.vars.outputs) }}'
|
||||
run: 'echo "$JSON_VARS"'
|
||||
|
||||
- name: 'Run Tests'
|
||||
if: "${{ github.event_name == 'schedule' || github.event.inputs.force_skip_tests == 'false' }}"
|
||||
uses: './.github/actions/run-tests'
|
||||
with:
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
working-directory: './release'
|
||||
|
||||
- name: 'Get Nightly Version'
|
||||
id: 'nightly_version'
|
||||
working-directory: './release'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
# Calculate the version using the centralized script
|
||||
VERSION_JSON=$(node scripts/get-release-version.js --type=nightly)
|
||||
|
||||
# Extract values for logging and outputs
|
||||
RELEASE_TAG=$(echo "${VERSION_JSON}" | jq -r .releaseTag)
|
||||
RELEASE_VERSION=$(echo "${VERSION_JSON}" | jq -r .releaseVersion)
|
||||
NPM_TAG=$(echo "${VERSION_JSON}" | jq -r .npmTag)
|
||||
PREVIOUS_TAG=$(echo "${VERSION_JSON}" | jq -r .previousReleaseTag)
|
||||
|
||||
# Print calculated values for logging
|
||||
echo "Calculated Release Tag: ${RELEASE_TAG}"
|
||||
echo "Calculated Release Version: ${RELEASE_VERSION}"
|
||||
echo "Calculated Previous Tag: ${PREVIOUS_TAG}"
|
||||
|
||||
# Set outputs for subsequent steps
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
echo "NPM_TAG=${NPM_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_TAG=${PREVIOUS_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Publish Release'
|
||||
if: true
|
||||
uses: './.github/actions/publish-release'
|
||||
with:
|
||||
release-version: '${{ steps.nightly_version.outputs.RELEASE_VERSION }}'
|
||||
release-tag: '${{ steps.nightly_version.outputs.RELEASE_TAG }}'
|
||||
npm-tag: '${{ steps.nightly_version.outputs.NPM_TAG }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ steps.vars.outputs.is_dry_run }}'
|
||||
previous-tag: '${{ steps.nightly_version.outputs.PREVIOUS_TAG }}'
|
||||
working-directory: './release'
|
||||
skip-branch-cleanup: true
|
||||
force-skip-tests: "${{ github.event_name != 'schedule' && github.event.inputs.force_skip_tests == 'true' }}"
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
npm-registry-publish-url: '${{ vars.NPM_REGISTRY_PUBLISH_URL }}'
|
||||
npm-registry-url: '${{ vars.NPM_REGISTRY_URL }}'
|
||||
npm-registry-scope: '${{ vars.NPM_REGISTRY_SCOPE }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
|
||||
- name: 'Create and Merge Pull Request'
|
||||
if: "github.event.inputs.environment != 'dev'"
|
||||
uses: './.github/actions/create-pull-request'
|
||||
with:
|
||||
branch-name: 'release/${{ steps.nightly_version.outputs.RELEASE_TAG }}'
|
||||
pr-title: 'chore/release: bump version to ${{ steps.nightly_version.outputs.RELEASE_VERSION }}'
|
||||
pr-body: 'Automated version bump for nightly release.'
|
||||
github-token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
dry-run: '${{ steps.vars.outputs.is_dry_run }}'
|
||||
working-directory: './release'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: "${{ failure() && github.event.inputs.environment != 'dev' && (github.event_name == 'schedule' || github.event.inputs.dry_run != 'true') }}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.nightly_version.outputs.RELEASE_TAG }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |
|
||||
gh issue create \
|
||||
--title "Nightly Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')" \
|
||||
--body "The nightly-release workflow failed. See the full run for details: ${DETAILS_URL}" \
|
||||
--label 'release-failure,priority/p0'
|
||||
@@ -0,0 +1,188 @@
|
||||
name: 'Release: Patch (0) from Comment'
|
||||
|
||||
on:
|
||||
issue_comment:
|
||||
types: ['created']
|
||||
|
||||
jobs:
|
||||
slash-command:
|
||||
runs-on: 'ubuntu-latest'
|
||||
# Only run if the comment is from a human user (not automated)
|
||||
if: "github.event.comment.user.type == 'User' && github.event.comment.user.login != 'github-actions[bot]'"
|
||||
permissions:
|
||||
contents: 'write'
|
||||
pull-requests: 'write'
|
||||
actions: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 'Slash Command Dispatch'
|
||||
id: 'slash_command'
|
||||
uses: 'peter-evans/slash-command-dispatch@40877f718dce0101edfc7aea2b3800cc192f9ed5'
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
commands: 'patch'
|
||||
permission: 'write'
|
||||
issue-type: 'pull-request'
|
||||
|
||||
- name: 'Get PR Status'
|
||||
id: 'pr_status'
|
||||
if: "startsWith(github.event.comment.body, '/patch')"
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
run: |
|
||||
gh pr view "${{ github.event.issue.number }}" --json mergeCommit,state > pr_status.json
|
||||
echo "MERGE_COMMIT_SHA=$(jq -r .mergeCommit.oid pr_status.json)" >> "$GITHUB_OUTPUT"
|
||||
echo "STATE=$(jq -r .state pr_status.json)" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Dispatch if Merged'
|
||||
if: "steps.pr_status.outputs.STATE == 'MERGED'"
|
||||
id: 'dispatch_patch'
|
||||
uses: 'actions/github-script@00f12e3e20659f42342b1c0226afda7f7c042325'
|
||||
env:
|
||||
COMMENT_BODY: '${{ github.event.comment.body }}'
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
script: |
|
||||
// Parse the comment body directly to extract channel(s)
|
||||
const commentBody = process.env.COMMENT_BODY;
|
||||
console.log('Comment body:', commentBody);
|
||||
|
||||
let channels = ['stable', 'preview']; // default to both
|
||||
|
||||
// Parse different formats:
|
||||
// /patch (defaults to both)
|
||||
// /patch both
|
||||
// /patch stable
|
||||
// /patch preview
|
||||
if (commentBody.trim() === '/patch' || commentBody.trim() === '/patch both') {
|
||||
channels = ['stable', 'preview'];
|
||||
} else if (commentBody.trim() === '/patch stable') {
|
||||
channels = ['stable'];
|
||||
} else if (commentBody.trim() === '/patch preview') {
|
||||
channels = ['preview'];
|
||||
} else {
|
||||
// Fallback parsing for legacy formats
|
||||
if (commentBody.includes('channel=preview')) {
|
||||
channels = ['preview'];
|
||||
} else if (commentBody.includes('--channel preview')) {
|
||||
channels = ['preview'];
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Detected channels:', channels);
|
||||
|
||||
const dispatchedRuns = [];
|
||||
|
||||
// Dispatch workflow for each channel
|
||||
for (const channel of channels) {
|
||||
console.log(`Dispatching workflow for channel: ${channel}`);
|
||||
|
||||
const response = await github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'release-patch-1-create-pr.yml',
|
||||
ref: 'main',
|
||||
inputs: {
|
||||
commit: '${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}',
|
||||
channel: channel,
|
||||
original_pr: '${{ github.event.issue.number }}',
|
||||
environment: 'prod'
|
||||
}
|
||||
});
|
||||
|
||||
dispatchedRuns.push({ channel, response });
|
||||
}
|
||||
|
||||
// Wait a moment for the workflows to be created
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
|
||||
const runs = await github.rest.actions.listWorkflowRuns({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'release-patch-1-create-pr.yml',
|
||||
per_page: 20 // Increased to handle multiple runs
|
||||
});
|
||||
|
||||
// Find the recent runs that match our trigger
|
||||
const recentRuns = runs.data.workflow_runs.filter(run =>
|
||||
run.event === 'workflow_dispatch' &&
|
||||
new Date(run.created_at) > new Date(Date.now() - 15000) // Within last 15 seconds
|
||||
).slice(0, channels.length); // Limit to the number of channels we dispatched
|
||||
|
||||
// Set outputs
|
||||
core.setOutput('dispatched_channels', channels.join(','));
|
||||
core.setOutput('dispatched_run_count', channels.length.toString());
|
||||
|
||||
if (recentRuns.length > 0) {
|
||||
core.setOutput('dispatched_run_urls', recentRuns.map(r => r.html_url).join(','));
|
||||
core.setOutput('dispatched_run_ids', recentRuns.map(r => r.id).join(','));
|
||||
}
|
||||
|
||||
- name: 'Comment on Failure'
|
||||
if: "startsWith(github.event.comment.body, '/patch') && steps.pr_status.outputs.STATE != 'MERGED'"
|
||||
uses: 'peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d'
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
:x: The `/patch` command failed. This pull request must be merged before a patch can be created.
|
||||
|
||||
- name: 'Final Status Comment - Success'
|
||||
if: "always() && startsWith(github.event.comment.body, '/patch') && steps.dispatch_patch.outcome == 'success' && steps.dispatch_patch.outputs.dispatched_run_urls"
|
||||
uses: 'peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d'
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
✅ **Patch workflow(s) dispatched successfully!**
|
||||
|
||||
**📋 Details:**
|
||||
- **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}`
|
||||
- **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}`
|
||||
- **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }}
|
||||
|
||||
**🔗 Track Progress:**
|
||||
- [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
- name: 'Final Status Comment - Dispatch Success (No URL)'
|
||||
if: "always() && startsWith(github.event.comment.body, '/patch') && steps.dispatch_patch.outcome == 'success' && !steps.dispatch_patch.outputs.dispatched_run_urls"
|
||||
uses: 'peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d'
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
✅ **Patch workflow(s) dispatched successfully!**
|
||||
|
||||
**📋 Details:**
|
||||
- **Channels**: `${{ steps.dispatch_patch.outputs.dispatched_channels }}`
|
||||
- **Commit**: `${{ steps.pr_status.outputs.MERGE_COMMIT_SHA }}`
|
||||
- **Workflows Created**: ${{ steps.dispatch_patch.outputs.dispatched_run_count }}
|
||||
|
||||
**🔗 Track Progress:**
|
||||
- [View patch workflows](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
|
||||
- name: 'Final Status Comment - Failure'
|
||||
if: "always() && startsWith(github.event.comment.body, '/patch') && (steps.dispatch_patch.outcome == 'failure' || steps.dispatch_patch.outcome == 'cancelled')"
|
||||
uses: 'peter-evans/create-or-update-comment@67dcc547d311b736a8e6c5c236542148a47adc3d'
|
||||
with:
|
||||
token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
issue-number: '${{ github.event.issue.number }}'
|
||||
body: |
|
||||
❌ **Patch workflow dispatch failed!**
|
||||
|
||||
There was an error dispatching the patch creation workflow.
|
||||
|
||||
**🔍 Troubleshooting:**
|
||||
- Check that the PR is properly merged
|
||||
- Verify workflow permissions
|
||||
- Review error logs in the workflow run
|
||||
|
||||
**🔗 Debug Links:**
|
||||
- [This workflow run](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
|
||||
- [Patch workflow history](https://github.com/${{ github.repository }}/actions/workflows/release-patch-1-create-pr.yml)
|
||||
@@ -0,0 +1,134 @@
|
||||
name: 'Release: Patch (1) Create PR'
|
||||
|
||||
run-name: >-
|
||||
Release Patch (1) Create PR | S:${{ inputs.channel }} | C:${{ inputs.commit }} ${{ inputs.original_pr && format('| PR:#{0}', inputs.original_pr) || '' }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
commit:
|
||||
description: 'The commit SHA to cherry-pick for the patch.'
|
||||
required: true
|
||||
type: 'string'
|
||||
channel:
|
||||
description: 'The release channel to patch.'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'stable'
|
||||
- 'preview'
|
||||
dry_run:
|
||||
description: 'Whether to run in dry-run mode.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to test from.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
original_pr:
|
||||
description: 'The original PR number to comment back on.'
|
||||
required: false
|
||||
type: 'string'
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
create-patch:
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
contents: 'write'
|
||||
pull-requests: 'write'
|
||||
actions: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'configure .npmrc'
|
||||
uses: './.github/actions/setup-npmrc'
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
- name: 'Install Script Dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Configure Git User'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
run: |-
|
||||
git config user.name "gemini-cli-robot"
|
||||
git config user.email "gemini-cli-robot@google.com"
|
||||
# Configure git to use GITHUB_TOKEN for remote operations (has actions:write for workflow files)
|
||||
git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${REPOSITORY}.git"
|
||||
|
||||
- name: 'Create Patch'
|
||||
id: 'create_patch'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
GH_TOKEN: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
CLI_PACKAGE_NAME: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
PATCH_COMMIT: '${{ github.event.inputs.commit }}'
|
||||
PATCH_CHANNEL: '${{ github.event.inputs.channel }}'
|
||||
ORIGINAL_PR: '${{ github.event.inputs.original_pr }}'
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
# Capture output and display it in logs using tee
|
||||
{
|
||||
node scripts/releasing/create-patch-pr.js \
|
||||
--cli-package-name="${CLI_PACKAGE_NAME}" \
|
||||
--commit="${PATCH_COMMIT}" \
|
||||
--channel="${PATCH_CHANNEL}" \
|
||||
--pullRequestNumber="${ORIGINAL_PR}" \
|
||||
--dry-run="${DRY_RUN}"
|
||||
} 2>&1 | tee >(
|
||||
echo "LOG_CONTENT<<EOF" >> "$GITHUB_ENV"
|
||||
cat >> "$GITHUB_ENV"
|
||||
echo "EOF" >> "$GITHUB_ENV"
|
||||
)
|
||||
echo "EXIT_CODE=${PIPESTATUS[0]}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Comment on Original PR'
|
||||
if: 'always() && inputs.original_pr'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ORIGINAL_PR: '${{ github.event.inputs.original_pr }}'
|
||||
EXIT_CODE: '${{ steps.create_patch.outputs.EXIT_CODE }}'
|
||||
COMMIT: '${{ github.event.inputs.commit }}'
|
||||
CHANNEL: '${{ github.event.inputs.channel }}'
|
||||
REPOSITORY: '${{ github.repository }}'
|
||||
GITHUB_RUN_ID: '${{ github.run_id }}'
|
||||
LOG_CONTENT: '${{ env.LOG_CONTENT }}'
|
||||
TARGET_REF: '${{ github.event.inputs.ref }}'
|
||||
ENVIRONMENT: '${{ github.event.inputs.environment }}'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
git checkout "${TARGET_REF}"
|
||||
node scripts/releasing/patch-create-comment.js
|
||||
|
||||
- name: 'Fail Workflow if Main Task Failed'
|
||||
if: 'always() && steps.create_patch.outputs.EXIT_CODE != 0'
|
||||
env:
|
||||
EXIT_CODE: '${{ steps.create_patch.outputs.EXIT_CODE }}'
|
||||
run: |
|
||||
echo "Patch creation failed with exit code: ${EXIT_CODE}"
|
||||
echo "Check the logs above and the comment posted to the original PR for details."
|
||||
exit 1
|
||||
@@ -0,0 +1,94 @@
|
||||
name: 'Release: Patch (2) Trigger'
|
||||
|
||||
run-name: >-
|
||||
Release Patch (2) Trigger |
|
||||
${{ github.event.pull_request.number && format('PR #{0}', github.event.pull_request.number) || 'Manual' }} |
|
||||
${{ github.event.pull_request.head.ref || github.event.inputs.ref }}
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- 'closed'
|
||||
branches:
|
||||
- 'release/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'The head ref of the merged hotfix PR to trigger the release for (e.g. hotfix/v1.2.3/cherry-pick-abc).'
|
||||
required: true
|
||||
type: 'string'
|
||||
workflow_ref:
|
||||
description: 'The ref to checkout the workflow code from.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
workflow_id:
|
||||
description: 'The workflow to trigger. Defaults to release-patch-3-release.yml'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'release-patch-3-release.yml'
|
||||
dry_run:
|
||||
description: 'Whether this is a dry run.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
test_mode:
|
||||
description: 'Whether or not to run in test mode'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
trigger-patch-release:
|
||||
if: "(github.event_name == 'pull_request' && github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'hotfix/')) || github.event_name == 'workflow_dispatch'"
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
actions: 'write'
|
||||
contents: 'write'
|
||||
pull-requests: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: "${{ github.event.inputs.workflow_ref || 'main' }}"
|
||||
fetch-depth: 1
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Trigger Patch Release'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
HEAD_REF: "${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.event.inputs.ref }}"
|
||||
PR_BODY: "${{ github.event_name == 'pull_request' && github.event.pull_request.body || '' }}"
|
||||
WORKFLOW_ID: '${{ github.event.inputs.workflow_id }}'
|
||||
GITHUB_REPOSITORY_OWNER: '${{ github.repository_owner }}'
|
||||
GITHUB_REPOSITORY_NAME: '${{ github.event.repository.name }}'
|
||||
GITHUB_EVENT_NAME: '${{ github.event_name }}'
|
||||
GITHUB_EVENT_PAYLOAD: '${{ toJSON(github.event) }}'
|
||||
FORCE_SKIP_TESTS: '${{ github.event.inputs.force_skip_tests }}'
|
||||
TEST_MODE: '${{ github.event.inputs.test_mode }}'
|
||||
ENVIRONMENT: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
run: |
|
||||
node scripts/releasing/patch-trigger.js --dry-run="${DRY_RUN}"
|
||||
@@ -0,0 +1,251 @@
|
||||
name: 'Release: Patch (3) Release'
|
||||
|
||||
run-name: >-
|
||||
Release Patch (3) Release | T:${{ inputs.type }} | R:${{ inputs.release_ref }} ${{ inputs.original_pr && format('| PR:#{0}', inputs.original_pr) || '' }}
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
type:
|
||||
description: 'The type of release to perform.'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'stable'
|
||||
- 'preview'
|
||||
dry_run:
|
||||
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
release_ref:
|
||||
description: 'The branch, tag, or SHA to release from.'
|
||||
required: true
|
||||
type: 'string'
|
||||
original_pr:
|
||||
description: 'The original PR number to comment back on.'
|
||||
required: false
|
||||
type: 'string'
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
pull-requests: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
fetch-depth: 0
|
||||
fetch-tags: true
|
||||
|
||||
- name: 'Checkout Release Code'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: '${{ github.event.inputs.release_ref }}'
|
||||
path: 'release'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'configure .npmrc'
|
||||
uses: './.github/actions/setup-npmrc'
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
- name: 'Install Script Dependencies'
|
||||
run: |-
|
||||
npm ci
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
working-directory: './release'
|
||||
run: |-
|
||||
npm ci
|
||||
|
||||
- name: 'Print Inputs'
|
||||
shell: 'bash'
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'Get Patch Version'
|
||||
id: 'patch_version'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
PATCH_FROM: '${{ github.event.inputs.type }}'
|
||||
CLI_PACKAGE_NAME: '${{vars.CLI_PACKAGE_NAME}}'
|
||||
run: |
|
||||
# Use the existing get-release-version.js script to calculate patch version
|
||||
# Run from main checkout which has full git history and access to npm
|
||||
PATCH_JSON=$(node scripts/get-release-version.js --type=patch --cli-package-name="${CLI_PACKAGE_NAME}" --patch-from="${PATCH_FROM}")
|
||||
echo "Patch version calculation result: ${PATCH_JSON}"
|
||||
|
||||
RELEASE_VERSION=$(echo "${PATCH_JSON}" | jq -r .releaseVersion)
|
||||
RELEASE_TAG=$(echo "${PATCH_JSON}" | jq -r .releaseTag)
|
||||
NPM_TAG=$(echo "${PATCH_JSON}" | jq -r .npmTag)
|
||||
PREVIOUS_TAG=$(echo "${PATCH_JSON}" | jq -r .previousReleaseTag)
|
||||
|
||||
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "NPM_TAG=${NPM_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_TAG=${PREVIOUS_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Verify Version Consistency'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
CHANNEL: '${{ github.event.inputs.type }}'
|
||||
ORIGINAL_RELEASE_VERSION: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
|
||||
ORIGINAL_RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
|
||||
ORIGINAL_PREVIOUS_TAG: '${{ steps.patch_version.outputs.PREVIOUS_TAG }}'
|
||||
run: |
|
||||
echo "🔍 Verifying no concurrent patch releases have occurred..."
|
||||
|
||||
# Store original calculation for comparison
|
||||
echo "Original calculation:"
|
||||
echo " Release version: ${ORIGINAL_RELEASE_VERSION}"
|
||||
echo " Release tag: ${ORIGINAL_RELEASE_TAG}"
|
||||
echo " Previous tag: ${ORIGINAL_PREVIOUS_TAG}"
|
||||
|
||||
# Re-run the same version calculation script
|
||||
echo "Re-calculating version to check for changes..."
|
||||
CURRENT_PATCH_JSON=$(node scripts/get-release-version.js --cli-package-name="${{vars.CLI_PACKAGE_NAME}}" --type=patch --patch-from="${CHANNEL}")
|
||||
CURRENT_RELEASE_VERSION=$(echo "${CURRENT_PATCH_JSON}" | jq -r .releaseVersion)
|
||||
CURRENT_RELEASE_TAG=$(echo "${CURRENT_PATCH_JSON}" | jq -r .releaseTag)
|
||||
CURRENT_PREVIOUS_TAG=$(echo "${CURRENT_PATCH_JSON}" | jq -r .previousReleaseTag)
|
||||
|
||||
echo "Current calculation:"
|
||||
echo " Release version: ${CURRENT_RELEASE_VERSION}"
|
||||
echo " Release tag: ${CURRENT_RELEASE_TAG}"
|
||||
echo " Previous tag: ${CURRENT_PREVIOUS_TAG}"
|
||||
|
||||
# Compare calculations
|
||||
if [[ "${ORIGINAL_RELEASE_VERSION}" != "${CURRENT_RELEASE_VERSION}" ]] || \
|
||||
[[ "${ORIGINAL_RELEASE_TAG}" != "${CURRENT_RELEASE_TAG}" ]] || \
|
||||
[[ "${ORIGINAL_PREVIOUS_TAG}" != "${CURRENT_PREVIOUS_TAG}" ]]; then
|
||||
echo "❌ RACE CONDITION DETECTED: Version calculations have changed!"
|
||||
echo "This indicates another patch release completed while this one was in progress."
|
||||
echo ""
|
||||
echo "Originally planned: ${ORIGINAL_RELEASE_VERSION} (from ${ORIGINAL_PREVIOUS_TAG})"
|
||||
echo "Should now build: ${CURRENT_RELEASE_VERSION} (from ${CURRENT_PREVIOUS_TAG})"
|
||||
echo ""
|
||||
echo "# Setting outputs for failure comment"
|
||||
echo "CURRENT_RELEASE_VERSION=${CURRENT_RELEASE_VERSION}" >> "${GITHUB_ENV}"
|
||||
echo "CURRENT_RELEASE_TAG=${CURRENT_RELEASE_TAG}" >> "${GITHUB_ENV}"
|
||||
echo "CURRENT_PREVIOUS_TAG=${CURRENT_PREVIOUS_TAG}" >> "${GITHUB_ENV}"
|
||||
echo "The patch release must be restarted to use the correct version numbers."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Version calculations unchanged - proceeding with release"
|
||||
|
||||
- name: 'Print Calculated Version'
|
||||
run: |-
|
||||
echo "Patch Release Summary:"
|
||||
echo " Release Version: ${{ steps.patch_version.outputs.RELEASE_VERSION }}"
|
||||
echo " Release Tag: ${{ steps.patch_version.outputs.RELEASE_TAG }}"
|
||||
echo " NPM Tag: ${{ steps.patch_version.outputs.NPM_TAG }}"
|
||||
echo " Previous Tag: ${{ steps.patch_version.outputs.PREVIOUS_TAG }}"
|
||||
|
||||
- name: 'Run Tests'
|
||||
if: "${{github.event.inputs.force_skip_tests != 'true'}}"
|
||||
uses: './.github/actions/run-tests'
|
||||
with:
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
working-directory: './release'
|
||||
|
||||
- name: 'Publish Release'
|
||||
uses: './.github/actions/publish-release'
|
||||
with:
|
||||
release-version: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
|
||||
release-tag: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
|
||||
npm-tag: '${{ steps.patch_version.outputs.NPM_TAG }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
previous-tag: '${{ steps.patch_version.outputs.PREVIOUS_TAG }}'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
npm-registry-publish-url: '${{ vars.NPM_REGISTRY_PUBLISH_URL }}'
|
||||
npm-registry-url: '${{ vars.NPM_REGISTRY_URL }}'
|
||||
npm-registry-scope: '${{ vars.NPM_REGISTRY_SCOPE }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
working-directory: './release'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry_run == false }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |
|
||||
gh issue create \
|
||||
--title 'Patch Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')' \
|
||||
--body 'The patch-release workflow failed. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'release-failure,priority/p0'
|
||||
|
||||
- name: 'Comment Success on Original PR'
|
||||
if: '${{ success() && github.event.inputs.original_pr }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ORIGINAL_PR: '${{ github.event.inputs.original_pr }}'
|
||||
SUCCESS: 'true'
|
||||
RELEASE_VERSION: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
|
||||
RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
|
||||
NPM_TAG: '${{ steps.patch_version.outputs.NPM_TAG }}'
|
||||
CHANNEL: '${{ github.event.inputs.type }}'
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
GITHUB_RUN_ID: '${{ github.run_id }}'
|
||||
GITHUB_REPOSITORY_OWNER: '${{ github.repository_owner }}'
|
||||
GITHUB_REPOSITORY_NAME: '${{ github.event.repository.name }}'
|
||||
run: |
|
||||
node scripts/releasing/patch-comment.js
|
||||
|
||||
- name: 'Comment Failure on Original PR'
|
||||
if: '${{ failure() && github.event.inputs.original_pr }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ORIGINAL_PR: '${{ github.event.inputs.original_pr }}'
|
||||
SUCCESS: 'false'
|
||||
RELEASE_VERSION: '${{ steps.patch_version.outputs.RELEASE_VERSION }}'
|
||||
RELEASE_TAG: '${{ steps.patch_version.outputs.RELEASE_TAG }}'
|
||||
NPM_TAG: '${{ steps.patch_version.outputs.NPM_TAG }}'
|
||||
CHANNEL: '${{ github.event.inputs.type }}'
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
GITHUB_RUN_ID: '${{ github.run_id }}'
|
||||
GITHUB_REPOSITORY_OWNER: '${{ github.repository_owner }}'
|
||||
GITHUB_REPOSITORY_NAME: '${{ github.event.repository.name }}'
|
||||
# Pass current version info for race condition failures
|
||||
CURRENT_RELEASE_VERSION: '${{ env.CURRENT_RELEASE_VERSION }}'
|
||||
CURRENT_RELEASE_TAG: '${{ env.CURRENT_RELEASE_TAG }}'
|
||||
CURRENT_PREVIOUS_TAG: '${{ env.CURRENT_PREVIOUS_TAG }}'
|
||||
run: |
|
||||
# Check if this was a version consistency failure
|
||||
if [[ -n "${CURRENT_RELEASE_VERSION}" ]]; then
|
||||
echo "Detected version race condition failure - posting specific comment with current version info"
|
||||
export RACE_CONDITION_FAILURE=true
|
||||
fi
|
||||
node scripts/releasing/patch-comment.js
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 'Promote Release'
|
||||
name: 'Release: Promote'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
@@ -26,11 +26,21 @@ on:
|
||||
description: 'Manually override the preview version number.'
|
||||
required: false
|
||||
type: 'string'
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
calculate-versions:
|
||||
name: 'Calculate Versions and Plan'
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
|
||||
outputs:
|
||||
STABLE_VERSION: '${{ steps.versions.outputs.STABLE_VERSION }}'
|
||||
STABLE_SHA: '${{ steps.versions.outputs.STABLE_SHA }}'
|
||||
@@ -59,49 +69,89 @@ jobs:
|
||||
|
||||
- name: 'Print Inputs'
|
||||
shell: 'bash'
|
||||
run: |-
|
||||
echo "${{ toJSON(inputs) }}"
|
||||
env:
|
||||
JSON_INPUTS: '${{ toJSON(inputs) }}'
|
||||
run: 'echo "$JSON_INPUTS"'
|
||||
|
||||
- name: 'Calculate Versions and SHAs'
|
||||
id: 'versions'
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
STABLE_OVERRIDE: '${{ github.event.inputs.stable_version_override }}'
|
||||
PREVIEW_OVERRIDE: '${{ github.event.inputs.preview_version_override }}'
|
||||
REF_INPUT: '${{ github.event.inputs.ref }}'
|
||||
run: |
|
||||
set -e
|
||||
STABLE_JSON=$(node scripts/get-release-version.js --type=stable ${{ github.event.inputs.stable_version_override && format('--stable_version_override={0}', github.event.inputs.stable_version_override) || '' }})
|
||||
PREVIEW_JSON=$(node scripts/get-release-version.js --type=preview ${{ github.event.inputs.preview_version_override && format('--preview_version_override={0}', github.event.inputs.preview_version_override) || '' }})
|
||||
NIGHTLY_JSON=$(node scripts/get-release-version.js --type=nightly)
|
||||
echo "STABLE_JSON_COMMAND=node scripts/get-release-version.js --type=stable ${{ github.event.inputs.stable_version_override && format('--stable_version_override={0}', github.event.inputs.stable_version_override) || '' }}"
|
||||
echo "PREVIEW_JSON_COMMAND=node scripts/get-release-version.js --type=preview ${{ github.event.inputs.preview_version_override && format('--preview_version_override={0}', github.event.inputs.preview_version_override) || '' }}"
|
||||
echo "NIGHTLY_JSON_COMMAND=node scripts/get-release-version.js --type=nightly"
|
||||
STABLE_COMMAND="node scripts/get-release-version.js --type=stable"
|
||||
if [[ -n "${STABLE_OVERRIDE}" ]]; then
|
||||
STABLE_COMMAND+=" --stable_version_override=${STABLE_OVERRIDE}"
|
||||
fi
|
||||
PREVIEW_COMMAND="node scripts/get-release-version.js --type=preview"
|
||||
if [[ -n "${PREVIEW_OVERRIDE}" ]]; then
|
||||
PREVIEW_COMMAND+=" --preview_version_override=${PREVIEW_OVERRIDE}"
|
||||
fi
|
||||
NIGHTLY_COMMAND="node scripts/get-release-version.js --type=promote-nightly"
|
||||
STABLE_JSON=$(${STABLE_COMMAND})
|
||||
STABLE_VERSION=$(echo "${STABLE_JSON}" | jq -r .releaseVersion)
|
||||
PREVIEW_COMMAND+=" --stable-base-version=${STABLE_VERSION}"
|
||||
NIGHTLY_COMMAND+=" --stable-base-version=${STABLE_VERSION}"
|
||||
PREVIEW_JSON=$(${PREVIEW_COMMAND})
|
||||
NIGHTLY_JSON=$(${NIGHTLY_COMMAND})
|
||||
echo "STABLE_JSON_COMMAND=${STABLE_COMMAND}"
|
||||
echo "PREVIEW_JSON_COMMAND=${PREVIEW_COMMAND}"
|
||||
echo "NIGHTLY_JSON_COMMAND=${NIGHTLY_COMMAND}"
|
||||
echo "STABLE_JSON: ${STABLE_JSON}"
|
||||
echo "PREVIEW_JSON: ${PREVIEW_JSON}"
|
||||
echo "NIGHTLY_JSON: ${NIGHTLY_JSON}"
|
||||
echo "STABLE_VERSION=$(echo "${STABLE_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
|
||||
echo "STABLE_VERSION=${STABLE_VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
# shellcheck disable=SC1083
|
||||
echo "STABLE_SHA=$(git rev-parse "$(echo "${PREVIEW_JSON}" | jq -r .previousReleaseTag)"^{commit})" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_STABLE_TAG=$(echo "${STABLE_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIEW_VERSION=$(echo "${PREVIEW_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
|
||||
# shellcheck disable=SC1083
|
||||
echo "PREVIEW_SHA=$(git rev-parse '${{ github.event.inputs.ref }}'^{commit})" >> "${GITHUB_OUTPUT}"
|
||||
REF="${REF_INPUT}"
|
||||
SHA=$(git ls-remote origin "$REF" | awk -v ref="$REF" '$2 == "refs/heads/"ref || $2 == "refs/tags/"ref || $2 == ref {print $1}' | head -n 1)
|
||||
if [ -z "$SHA" ]; then
|
||||
if [[ "$REF" =~ ^[0-9a-f]{7,40}$ ]]; then
|
||||
SHA="$REF"
|
||||
else
|
||||
echo "::error::Could not resolve ref '$REF' to a commit SHA."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
echo "PREVIEW_SHA=$SHA" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_PREVIEW_TAG=$(echo "${PREVIEW_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
echo "NEXT_NIGHTLY_VERSION=$(echo "${NIGHTLY_JSON}" | jq -r .releaseVersion)" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_NIGHTLY_TAG=$(echo "${NIGHTLY_JSON}" | jq -r .previousReleaseTag)" >> "${GITHUB_OUTPUT}"
|
||||
CURRENT_NIGHTLY_TAG=$(git describe --tags --abbrev=0 --match="*nightly*")
|
||||
echo "CURRENT_NIGHTLY_TAG=${CURRENT_NIGHTLY_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "NEXT_SHA=$(git rev-parse HEAD)" >> "${GITHUB_OUTPUT}"
|
||||
echo "NEXT_SHA=$SHA" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Display Pending Updates'
|
||||
env:
|
||||
STABLE_VERSION: '${{ steps.versions.outputs.STABLE_VERSION }}'
|
||||
STABLE_SHA: '${{ steps.versions.outputs.STABLE_SHA }}'
|
||||
PREVIOUS_STABLE_TAG: '${{ steps.versions.outputs.PREVIOUS_STABLE_TAG }}'
|
||||
PREVIEW_VERSION: '${{ steps.versions.outputs.PREVIEW_VERSION }}'
|
||||
PREVIEW_SHA: '${{ steps.versions.outputs.PREVIEW_SHA }}'
|
||||
PREVIOUS_PREVIEW_TAG: '${{ steps.versions.outputs.PREVIOUS_PREVIEW_TAG }}'
|
||||
NEXT_NIGHTLY_VERSION: '${{ steps.versions.outputs.NEXT_NIGHTLY_VERSION }}'
|
||||
PREVIOUS_NIGHTLY_TAG: '${{ steps.versions.outputs.PREVIOUS_NIGHTLY_TAG }}'
|
||||
INPUT_REF: '${{ github.event.inputs.ref }}'
|
||||
run: |
|
||||
echo "Pending Changes, Next Versions:"
|
||||
echo " Nightly: ${{ steps.versions.outputs.NEXT_NIGHTLY_VERSION }}"
|
||||
echo " Preview: ${{ steps.versions.outputs.PREVIEW_VERSION }}"
|
||||
echo " Stable: ${{ steps.versions.outputs.STABLE_VERSION }}"
|
||||
echo "Release Plan:"
|
||||
echo "-----------"
|
||||
echo "Stable Release: ${STABLE_VERSION}"
|
||||
echo " - Commit: ${STABLE_SHA}"
|
||||
echo " - Previous Tag: ${PREVIOUS_STABLE_TAG}"
|
||||
echo ""
|
||||
echo "Relevant SHAs:"
|
||||
echo " Stable: Will be cut from: : ${{ steps.versions.outputs.STABLE_SHA }} / ${{ steps.versions.outputs.PREVIOUS_STABLE_TAG }}"
|
||||
echo " Preview will be cut from: : ${{ steps.versions.outputs.PREVIEW_SHA }} (${{ github.event.inputs.ref }})". Users last saw preview as: ${{ steps.versions.outputs.PREVIOUS_PREVIEW_TAG }}
|
||||
echo " Nightly will be udpated in : ${{ steps.versions.outputs.NEXT_SHA }} (${{ github.event.inputs.ref }}). Previous nightly tag: ${{ steps.versions.outputs.PREVIOUS_NIGHTLY_TAG }}"
|
||||
echo "Preview Release: ${PREVIEW_VERSION}"
|
||||
echo " - Commit: ${PREVIEW_SHA} (${INPUT_REF})"
|
||||
echo " - Previous Tag: ${PREVIOUS_PREVIEW_TAG}"
|
||||
echo ""
|
||||
echo "Preparing Next Nightly Release: ${NEXT_NIGHTLY_VERSION}"
|
||||
echo " - Merging Version Update PR to Branch: ${INPUT_REF}"
|
||||
echo " - Previous Tag: ${PREVIOUS_NIGHTLY_TAG}"
|
||||
|
||||
test:
|
||||
name: 'Test ${{ matrix.channel }}'
|
||||
@@ -141,9 +191,9 @@ jobs:
|
||||
run: 'npm ci'
|
||||
|
||||
- name: 'Run Tests'
|
||||
if: "${{github.event.inputs.force_skip_tests != 'true'}}"
|
||||
uses: './.github/actions/run-tests'
|
||||
with:
|
||||
force_skip_tests: '${{ github.event.inputs.force_skip_tests }}'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
working-directory: './release'
|
||||
|
||||
@@ -151,6 +201,7 @@ jobs:
|
||||
name: 'Publish preview'
|
||||
needs: ['calculate-versions', 'test']
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
@@ -186,10 +237,19 @@ jobs:
|
||||
npm-tag: 'preview'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
previous-tag: '${{ needs.calculate-versions.outputs.PREVIOUS_PREVIEW_TAG }}'
|
||||
working-directory: './release'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
force-skip-tests: '${{ github.event.inputs.force_skip_tests }}'
|
||||
npm-registry-publish-url: '${{ vars.NPM_REGISTRY_PUBLISH_URL }}'
|
||||
npm-registry-url: '${{ vars.NPM_REGISTRY_URL }}'
|
||||
npm-registry-scope: '${{ vars.NPM_REGISTRY_SCOPE }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry_run == false }}'
|
||||
@@ -201,12 +261,13 @@ jobs:
|
||||
gh issue create \
|
||||
--title 'Promote Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')' \
|
||||
--body 'The promote-release workflow failed during preview publish. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'kind/bug,release-failure,priority/p0'
|
||||
--label 'release-failure,priority/p0'
|
||||
|
||||
publish-stable:
|
||||
name: 'Publish stable'
|
||||
needs: ['calculate-versions', 'test', 'publish-preview']
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
@@ -242,10 +303,19 @@ jobs:
|
||||
npm-tag: 'latest'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
previous-tag: '${{ needs.calculate-versions.outputs.PREVIOUS_STABLE_TAG }}'
|
||||
working-directory: './release'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
force-skip-tests: '${{ github.event.inputs.force_skip_tests }}'
|
||||
npm-registry-publish-url: '${{ vars.NPM_REGISTRY_PUBLISH_URL }}'
|
||||
npm-registry-url: '${{ vars.NPM_REGISTRY_URL }}'
|
||||
npm-registry-scope: '${{ vars.NPM_REGISTRY_SCOPE }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry_run == false }}'
|
||||
@@ -257,7 +327,7 @@ jobs:
|
||||
gh issue create \
|
||||
--title 'Promote Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')' \
|
||||
--body 'The promote-release workflow failed during stable publish. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'kind/bug,release-failure,priority/p0'
|
||||
--label 'release-failure,priority/p0'
|
||||
|
||||
nightly-pr:
|
||||
name: 'Create Nightly PR'
|
||||
@@ -303,9 +373,6 @@ jobs:
|
||||
DRY_RUN: '${{ github.event.inputs.dry_run }}'
|
||||
run: |-
|
||||
git add package.json packages/*/package.json
|
||||
if [ -f npm-shrinkwrap.json ]; then
|
||||
git add npm-shrinkwrap.json
|
||||
fi
|
||||
if [ -f package-lock.json ]; then
|
||||
git add package-lock.json
|
||||
fi
|
||||
@@ -317,20 +384,14 @@ jobs:
|
||||
echo "Dry run enabled. Skipping push."
|
||||
fi
|
||||
|
||||
- name: 'Create and Approve Pull Request'
|
||||
if: |-
|
||||
${{ github.event.inputs.dry_run == 'false' }}
|
||||
env:
|
||||
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
run: |
|
||||
gh pr create \
|
||||
--title "chore(release): bump version to ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}" \
|
||||
--body "Automated version bump to prepare for the next nightly release." \
|
||||
--base "main" \
|
||||
--head "${BRANCH_NAME}" \
|
||||
--fill
|
||||
gh pr merge --auto --squash
|
||||
- name: 'Create and Merge Pull Request'
|
||||
uses: './.github/actions/create-pull-request'
|
||||
with:
|
||||
branch-name: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
pr-title: 'chore(release): bump version to ${{ needs.calculate-versions.outputs.NEXT_NIGHTLY_VERSION }}'
|
||||
pr-body: 'Automated version bump to prepare for the next nightly release.'
|
||||
github-token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}'
|
||||
dry-run: '${{ github.event.inputs.dry_run }}'
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry_run == false }}'
|
||||
@@ -342,4 +403,4 @@ jobs:
|
||||
gh issue create \
|
||||
--title 'Promote Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')' \
|
||||
--body 'The promote-release workflow failed during nightly PR creation. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'kind/bug,release-failure,priority/p0'
|
||||
--label 'release-failure,priority/p0'
|
||||
@@ -0,0 +1,241 @@
|
||||
name: 'Release: Rollback change'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
rollback_origin:
|
||||
description: 'The package version to rollback FROM and delete (e.g., 0.5.0-preview-2)'
|
||||
required: true
|
||||
type: 'string'
|
||||
rollback_destination:
|
||||
description: 'The package version to rollback TO (e.g., 0.5.0-preview-2). This version must already exist on the npm registry.'
|
||||
required: false
|
||||
type: 'string'
|
||||
channel:
|
||||
description: 'The npm dist-tag to apply to rollback_destination (e.g., latest, preview, nightly). REQUIRED IF rollback_destination is set.'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'latest'
|
||||
- 'preview'
|
||||
- 'nightly'
|
||||
- 'dev'
|
||||
default: 'dev'
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to run from.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
dry-run:
|
||||
description: 'Whether to run in dry-run mode.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: true
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
change-tags:
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout repository'
|
||||
uses: 'actions/checkout@08eba0b27e820071cde6df949e0beb9ba4906955' # ratchet:actions/checkout@v4
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020'
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
|
||||
- name: 'configure .npmrc'
|
||||
uses: './.github/actions/setup-npmrc'
|
||||
with:
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
|
||||
- name: 'Get Origin Version Tag'
|
||||
id: 'origin_tag'
|
||||
shell: 'bash'
|
||||
env:
|
||||
ROLLBACK_ORIGIN: '${{ github.event.inputs.rollback_origin }}'
|
||||
run: |
|
||||
TAG_VALUE="v${ROLLBACK_ORIGIN}"
|
||||
echo "ORIGIN_TAG=$TAG_VALUE" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Get Origin Commit Hash'
|
||||
id: 'origin_hash'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ORIGIN_TAG: '${{ steps.origin_tag.outputs.ORIGIN_TAG }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "ORIGIN_HASH=$(git rev-parse "${ORIGIN_TAG}")" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: 'Change tag'
|
||||
if: "${{ github.event.inputs.rollback_destination != '' }}"
|
||||
uses: './.github/actions/tag-npm-release'
|
||||
with:
|
||||
channel: '${{ github.event.inputs.channel }}'
|
||||
version: '${{ github.event.inputs.rollback_destination }}'
|
||||
dry-run: '${{ github.event.inputs.dry-run }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
cli-package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
core-package-name: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
a2a-package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
|
||||
- name: 'Get cli Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'cli-token'
|
||||
with:
|
||||
package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
|
||||
- name: 'Deprecate Cli Npm Package'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' && github.event.inputs.environment == 'prod' }}"
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.cli-token.outputs.auth-token }}'
|
||||
PACKAGE_NAME: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
ROLLBACK_ORIGIN: '${{ github.event.inputs.rollback_origin }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm deprecate "${PACKAGE_NAME}@${ROLLBACK_ORIGIN}" "This version has been rolled back."
|
||||
|
||||
- name: 'Get core Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'core-token'
|
||||
with:
|
||||
package-name: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
|
||||
- name: 'Deprecate Core Npm Package'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' && github.event.inputs.environment == 'prod' }}"
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.core-token.outputs.auth-token }}'
|
||||
PACKAGE_NAME: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
ROLLBACK_ORIGIN: '${{ github.event.inputs.rollback_origin }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm deprecate "${PACKAGE_NAME}@${ROLLBACK_ORIGIN}" "This version has been rolled back."
|
||||
|
||||
- name: 'Get a2a Token'
|
||||
uses: './.github/actions/npm-auth-token'
|
||||
id: 'a2a-token'
|
||||
with:
|
||||
package-name: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
wombat-token-cli: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
wombat-token-a2a-server: '${{ secrets.WOMBAT_TOKEN_A2A_SERVER }}'
|
||||
|
||||
- name: 'Deprecate A2A Server Npm Package'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' && github.event.inputs.environment == 'prod' }}"
|
||||
env:
|
||||
NODE_AUTH_TOKEN: '${{ steps.a2a-token.outputs.auth-token }}'
|
||||
PACKAGE_NAME: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
ROLLBACK_ORIGIN: '${{ github.event.inputs.rollback_origin }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
npm deprecate "${PACKAGE_NAME}@${ROLLBACK_ORIGIN}" "This version has been rolled back."
|
||||
|
||||
- name: 'Delete Github Release'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' && github.event.inputs.environment == 'prod'}}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ORIGIN_TAG: '${{ steps.origin_tag.outputs.ORIGIN_TAG }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
gh release delete "${ORIGIN_TAG}" --yes
|
||||
|
||||
- name: 'Verify Origin Release Deletion'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' }}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
TARGET_TAG: '${{ steps.origin_tag.outputs.ORIGIN_TAG }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
RELEASE_TAG=$(gh release view "$TARGET_TAG" --json tagName --jq .tagName)
|
||||
if [ "$RELEASE_TAG" = "$TARGET_TAG" ]; then
|
||||
echo "❌ Failed to delete release with tag ${TARGET_TAG}"
|
||||
echo '❌ This means the release was not deleted, and the workflow should fail.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 'Add Rollback Tag'
|
||||
id: 'rollback_tag'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' }}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
ROLLBACK_TAG_NAME: '${{ steps.origin_tag.outputs.ORIGIN_TAG }}-rollback'
|
||||
ORIGIN_HASH: '${{ steps.origin_hash.outputs.ORIGIN_HASH }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "ROLLBACK_TAG=$ROLLBACK_TAG_NAME" >> "$GITHUB_OUTPUT"
|
||||
git tag "$ROLLBACK_TAG_NAME" "${ORIGIN_HASH}"
|
||||
git push origin --tags
|
||||
|
||||
- name: 'Verify Rollback Tag Added'
|
||||
if: "${{ github.event.inputs.dry-run == 'false' }}"
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
TARGET_TAG: '${{ steps.rollback_tag.outputs.ROLLBACK_TAG }}'
|
||||
TARGET_HASH: '${{ steps.origin_hash.outputs.ORIGIN_HASH }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
ROLLBACK_COMMIT=$(git rev-parse -q --verify "$TARGET_TAG")
|
||||
if [ "$ROLLBACK_COMMIT" != "$TARGET_HASH" ]; then
|
||||
echo '❌ Failed to add tag $TARGET_TAG to commit $TARGET_HASH'
|
||||
echo '❌ This means the tag was not added, and the workflow should fail.'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: 'Log Dry run'
|
||||
if: "${{ github.event.inputs.dry-run == 'true' }}"
|
||||
env:
|
||||
ROLLBACK_ORIGIN: '${{ github.event.inputs.rollback_origin }}'
|
||||
ROLLBACK_DESTINATION: '${{ github.event.inputs.rollback_destination }}'
|
||||
CHANNEL: '${{ github.event.inputs.channel }}'
|
||||
REF_INPUT: '${{ github.event.inputs.ref }}'
|
||||
ORIGIN_TAG: '${{ steps.origin_tag.outputs.ORIGIN_TAG }}'
|
||||
ORIGIN_HASH: '${{ steps.origin_hash.outputs.ORIGIN_HASH }}'
|
||||
ROLLBACK_TAG: '${{ steps.rollback_tag.outputs.ROLLBACK_TAG }}'
|
||||
CLI_PACKAGE_NAME: '${{ vars.CLI_PACKAGE_NAME }}'
|
||||
CORE_PACKAGE_NAME: '${{ vars.CORE_PACKAGE_NAME }}'
|
||||
A2A_PACKAGE_NAME: '${{ vars.A2A_PACKAGE_NAME }}'
|
||||
shell: 'bash'
|
||||
run: |
|
||||
echo "
|
||||
Inputs:
|
||||
- rollback_origin: '${ROLLBACK_ORIGIN}'
|
||||
- rollback_destination: '${ROLLBACK_DESTINATION}'
|
||||
- channel: '${CHANNEL}'
|
||||
- ref: '${REF_INPUT}'
|
||||
|
||||
Outputs:
|
||||
- ORIGIN_TAG: '${ORIGIN_TAG}'
|
||||
- ORIGIN_HASH: '${ORIGIN_HASH}'
|
||||
- ROLLBACK_TAG: '${ROLLBACK_TAG}'
|
||||
|
||||
Would have npm deprecate ${CLI_PACKAGE_NAME}@${ROLLBACK_ORIGIN}, ${CORE_PACKAGE_NAME}@${ROLLBACK_ORIGIN}, and ${A2A_PACKAGE_NAME}@${ROLLBACK_ORIGIN}
|
||||
Would have deleted the github release with tag ${ORIGIN_TAG}
|
||||
Would have added tag ${ORIGIN_TAG}-rollback to ${ORIGIN_HASH}
|
||||
"
|
||||
@@ -0,0 +1,49 @@
|
||||
name: 'Release Sandbox'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to release from.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
dry-run:
|
||||
description: 'Whether this is a dry run.'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'read'
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref || github.sha }}'
|
||||
fetch-depth: 0
|
||||
- name: 'Push'
|
||||
uses: './.github/actions/push-sandbox'
|
||||
with:
|
||||
dockerhub-username: '${{ secrets.DOCKER_SERVICE_ACCOUNT_NAME }}'
|
||||
dockerhub-token: '${{ secrets.DOCKER_SERVICE_ACCOUNT_KEY }}'
|
||||
github-actor: '${{ github.actor }}'
|
||||
github-secret: '${{ secrets.GITHUB_TOKEN }}'
|
||||
github-sha: '${{ github.sha }}'
|
||||
github-ref-name: '${{github.event.inputs.ref}}'
|
||||
dry-run: '${{ github.event.inputs.dry-run }}'
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry-run == false }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |
|
||||
gh issue create \
|
||||
--title 'Sandbox Release Failed on $(date +'%Y-%m-%d')' \
|
||||
--body 'The sandbox-release workflow failed. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'release-failure,priority/p0'
|
||||
@@ -1,202 +0,0 @@
|
||||
name: 'Release'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'The version to release (e.g., v0.1.11).'
|
||||
required: true
|
||||
type: 'string'
|
||||
ref:
|
||||
description: 'The branch or ref (full git sha) to release from.'
|
||||
required: true
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
dry_run:
|
||||
description: 'Run a dry-run of the release process; no branches, npm packages or GitHub releases will be created.'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
force_skip_tests:
|
||||
description: 'Select to skip the "Run Tests" step in testing. Prod releases should run tests'
|
||||
required: false
|
||||
type: 'boolean'
|
||||
default: false
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: 'ubuntu-latest'
|
||||
environment:
|
||||
name: 'production-release'
|
||||
url: '${{ github.server_url }}/${{ github.repository }}/releases/tag/${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
if: |-
|
||||
${{ github.repository == 'google-gemini/gemini-cli' }}
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
id-token: 'write'
|
||||
issues: 'write' # For creating issues on failure
|
||||
outputs:
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref || github.sha }}'
|
||||
fetch-depth: 0
|
||||
|
||||
- name: 'Set booleans for simplified logic'
|
||||
env:
|
||||
DRY_RUN_INPUT: '${{ github.event.inputs.dry_run }}'
|
||||
id: 'vars'
|
||||
run: |-
|
||||
is_dry_run="false"
|
||||
if [[ "${DRY_RUN_INPUT}" == "true" ]]; then
|
||||
is_dry_run="true"
|
||||
fi
|
||||
echo "is_dry_run=${is_dry_run}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Setup Node.js'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
cache: 'npm'
|
||||
|
||||
- name: 'Install Dependencies'
|
||||
run: |-
|
||||
npm ci
|
||||
|
||||
- name: 'Get the version'
|
||||
id: 'version'
|
||||
run: |-
|
||||
RELEASE_TAG="${{ inputs.version }}"
|
||||
# The version for npm should not have the 'v' prefix.
|
||||
RELEASE_VERSION="${RELEASE_TAG#v}"
|
||||
NPM_TAG="latest"
|
||||
if [[ "${RELEASE_TAG}" == *"preview"* ]]; then
|
||||
NPM_TAG="preview"
|
||||
fi
|
||||
PREVIOUS_TAG=$(git describe --tags --abbrev=0)
|
||||
echo "RELEASE_TAG=${RELEASE_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "RELEASE_VERSION=${RELEASE_VERSION}" >> "${GITHUB_OUTPUT}"
|
||||
echo "NPM_TAG=${NPM_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
echo "PREVIOUS_TAG=${PREVIOUS_TAG}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Run Tests'
|
||||
if: |-
|
||||
${{ github.event.inputs.force_skip_tests != 'true' }}
|
||||
env:
|
||||
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
|
||||
run: |-
|
||||
npm run preflight
|
||||
npm run test:integration:sandbox:none
|
||||
npm run test:integration:sandbox:docker
|
||||
|
||||
- name: 'Configure Git User'
|
||||
run: |-
|
||||
git config user.name "gemini-cli-robot"
|
||||
git config user.email "gemini-cli-robot@google.com"
|
||||
|
||||
- name: 'Create and switch to a release branch'
|
||||
id: 'release_branch'
|
||||
env:
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
BRANCH_NAME="release/${RELEASE_TAG}"
|
||||
git switch -c "${BRANCH_NAME}"
|
||||
echo "BRANCH_NAME=${BRANCH_NAME}" >> "${GITHUB_OUTPUT}"
|
||||
|
||||
- name: 'Update package versions'
|
||||
env:
|
||||
RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
||||
run: |-
|
||||
npm run release:version "${RELEASE_VERSION}"
|
||||
|
||||
- name: 'Commit and Conditionally Push package versions'
|
||||
env:
|
||||
BRANCH_NAME: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
IS_DRY_RUN: '${{ steps.vars.outputs.is_dry_run }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
run: |-
|
||||
git add package.json npm-shrinkwrap.json packages/*/package.json
|
||||
git commit -m "chore(release): ${RELEASE_TAG}"
|
||||
if [[ "${IS_DRY_RUN}" == "false" ]]; then
|
||||
echo "Pushing release branch to remote..."
|
||||
git push --set-upstream origin "${BRANCH_NAME}" --follow-tags
|
||||
else
|
||||
echo "Dry run enabled. Skipping push."
|
||||
fi
|
||||
|
||||
- name: 'Build and Prepare Packages'
|
||||
run: |-
|
||||
npm run build:packages
|
||||
npm run prepare:package
|
||||
|
||||
- name: 'Configure npm for publishing'
|
||||
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
|
||||
with:
|
||||
node-version-file: '.nvmrc'
|
||||
registry-url: 'https://wombat-dressing-room.appspot.com'
|
||||
scope: '@google'
|
||||
|
||||
- name: 'Publish @google/gemini-cli-core'
|
||||
env:
|
||||
IS_DRY_RUN: '${{ steps.vars.outputs.is_dry_run }}'
|
||||
NODE_AUTH_TOKEN: '${{ secrets.WOMBAT_TOKEN_CORE }}'
|
||||
NPM_TAG: '${{ steps.version.outputs.NPM_TAG }}'
|
||||
run: |-
|
||||
npm publish \
|
||||
--dry-run="${IS_DRY_RUN}" \
|
||||
--workspace="@google/gemini-cli-core" \
|
||||
--tag="${NPM_TAG}"
|
||||
|
||||
- name: 'Install latest core package'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
env:
|
||||
RELEASE_VERSION: '${{ steps.version.outputs.RELEASE_VERSION }}'
|
||||
run: |-
|
||||
npm install "@google/gemini-cli-core@${RELEASE_VERSION}" \
|
||||
--workspace="@google/gemini-cli" \
|
||||
--save-exact
|
||||
|
||||
- name: 'Publish @google/gemini-cli'
|
||||
env:
|
||||
IS_DRY_RUN: '${{ steps.vars.outputs.is_dry_run }}'
|
||||
NODE_AUTH_TOKEN: '${{ secrets.WOMBAT_TOKEN_CLI }}'
|
||||
NPM_TAG: '${{ steps.version.outputs.NPM_TAG }}'
|
||||
run: |-
|
||||
npm publish \
|
||||
--dry-run="${IS_DRY_RUN}" \
|
||||
--workspace="@google/gemini-cli" \
|
||||
--tag="${NPM_TAG}"
|
||||
|
||||
- name: 'Create GitHub Release and Tag'
|
||||
if: |-
|
||||
${{ steps.vars.outputs.is_dry_run == 'false' }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_BRANCH: '${{ steps.release_branch.outputs.BRANCH_NAME }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }}'
|
||||
PREVIOUS_TAG: '${{ steps.version.outputs.PREVIOUS_TAG }}'
|
||||
run: |-
|
||||
gh release create "${RELEASE_TAG}" \
|
||||
bundle/gemini.js \
|
||||
--target "$RELEASE_BRANCH" \
|
||||
--title "Release ${RELEASE_TAG}" \
|
||||
--notes-start-tag "$PREVIOUS_TAG" \
|
||||
--generate-notes
|
||||
|
||||
- name: 'Create Issue on Failure'
|
||||
if: |-
|
||||
${{ failure() }}
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
RELEASE_TAG: '${{ steps.version.outputs.RELEASE_TAG }} || "N/A"'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
run: |-
|
||||
gh issue create \
|
||||
--title "Release Failed for ${RELEASE_TAG} on $(date +'%Y-%m-%d')" \
|
||||
--body "The release workflow failed. See the full run for details: ${DETAILS_URL}" \
|
||||
--label "kind/bug,release-failure,priority/p0"
|
||||
@@ -0,0 +1,50 @@
|
||||
name: 'On Merge Smoke Test'
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
- 'release/**'
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'The branch, tag, or SHA to test on.'
|
||||
required: false
|
||||
type: 'string'
|
||||
default: 'main'
|
||||
dry-run:
|
||||
description: 'Run a dry-run of the smoke test; No bug will be created'
|
||||
required: true
|
||||
type: 'boolean'
|
||||
default: true
|
||||
|
||||
jobs:
|
||||
smoke-test:
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'write'
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: 'Checkout'
|
||||
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
with:
|
||||
ref: '${{ github.event.inputs.ref || github.sha }}'
|
||||
fetch-depth: 0
|
||||
- name: 'Install Dependencies'
|
||||
run: 'npm ci'
|
||||
- name: 'Build bundle'
|
||||
run: 'npm run bundle'
|
||||
- name: 'Smoke test bundle'
|
||||
run: 'node ./bundle/gemini.js --version'
|
||||
- name: 'Create Issue on Failure'
|
||||
if: '${{ failure() && github.event.inputs.dry-run == false }}'
|
||||
env:
|
||||
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
|
||||
DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}'
|
||||
REF: '${{ github.event.inputs.ref }}'
|
||||
run: |
|
||||
gh issue create \
|
||||
--title 'Smoke test failed on ${REF} @ $(date +'%Y-%m-%d')' \
|
||||
--body 'Smoke test build failed. See the full run for details: ${DETAILS_URL}' \
|
||||
--label 'priority/p0'
|
||||
@@ -13,7 +13,6 @@ jobs:
|
||||
matrix:
|
||||
runner:
|
||||
- 'ubuntu-latest' # GitHub-hosted
|
||||
- 'self-hosted' # GHA on GCP runners
|
||||
runs-on: '${{ matrix.runner }}'
|
||||
if: |-
|
||||
${{ github.repository == 'google-gemini/gemini-cli' }}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
name: 'Trigger Patch Release'
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- 'closed'
|
||||
|
||||
jobs:
|
||||
trigger-patch-release:
|
||||
if: "github.event.pull_request.merged == true && startsWith(github.head_ref, 'hotfix/')"
|
||||
runs-on: 'ubuntu-latest'
|
||||
steps:
|
||||
- name: 'Trigger Patch Release'
|
||||
uses: 'actions/github-script@00f12e3e20659f42342b1c0226afda7f7c042325'
|
||||
with:
|
||||
script: |
|
||||
const body = context.payload.pull_request.body;
|
||||
const isDryRun = body.includes('[DRY RUN]');
|
||||
const ref = context.payload.pull_request.base.ref;
|
||||
const channel = ref.includes('preview') ? 'preview' : 'stable';
|
||||
github.rest.actions.createWorkflowDispatch({
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
workflow_id: 'patch-release.yml',
|
||||
ref: ref,
|
||||
inputs: {
|
||||
type: channel,
|
||||
dry_run: isDryRun.toString()
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
name: 'Trigger E2E'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
repo_name:
|
||||
description: 'Repository name (e.g., owner/repo)'
|
||||
required: false
|
||||
type: 'string'
|
||||
head_sha:
|
||||
description: 'SHA of the commit to test'
|
||||
required: false
|
||||
type: 'string'
|
||||
pull_request:
|
||||
|
||||
jobs:
|
||||
save_repo_name:
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
steps:
|
||||
- name: 'Save Repo name'
|
||||
env:
|
||||
REPO_NAME: '${{ github.event.inputs.repo_name || github.event.pull_request.head.repo.full_name }}'
|
||||
HEAD_SHA: '${{ github.event.inputs.head_sha || github.event.pull_request.head.sha }}'
|
||||
run: |
|
||||
mkdir -p ./pr
|
||||
echo '${{ env.REPO_NAME }}' > ./pr/repo_name
|
||||
echo '${{ env.HEAD_SHA }}' > ./pr/head_sha
|
||||
- uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
|
||||
with:
|
||||
name: 'repo_name'
|
||||
path: 'pr/'
|
||||
trigger_e2e:
|
||||
name: 'Trigger e2e'
|
||||
runs-on: 'gemini-cli-ubuntu-16-core'
|
||||
steps:
|
||||
- id: 'trigger-e2e'
|
||||
run: |
|
||||
echo "Trigger e2e workflow"
|
||||
@@ -0,0 +1,51 @@
|
||||
name: 'Verify NPM release tag'
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'The expected Gemini binary version that should be released (e.g., 0.5.0-preview-2).'
|
||||
required: true
|
||||
type: 'string'
|
||||
npm-tag:
|
||||
description: 'NPM tag to verify'
|
||||
required: true
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'dev'
|
||||
- 'latest'
|
||||
- 'preview'
|
||||
- 'nightly'
|
||||
default: 'latest'
|
||||
environment:
|
||||
description: 'Environment'
|
||||
required: false
|
||||
type: 'choice'
|
||||
options:
|
||||
- 'prod'
|
||||
- 'dev'
|
||||
default: 'prod'
|
||||
|
||||
jobs:
|
||||
verify-release:
|
||||
environment: "${{ github.event.inputs.environment || 'prod' }}"
|
||||
runs-on: 'ubuntu-latest'
|
||||
permissions:
|
||||
contents: 'read'
|
||||
packages: 'write'
|
||||
issues: 'write'
|
||||
steps:
|
||||
- name: '📝 Print vars'
|
||||
shell: 'bash'
|
||||
run: 'echo "${{ toJSON(vars) }}"'
|
||||
- uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8'
|
||||
- name: 'Verify release'
|
||||
uses: './.github/actions/verify-release'
|
||||
with:
|
||||
npm-package: '${{vars.CLI_PACKAGE_NAME}}@${{github.event.inputs.npm-tag}}'
|
||||
expected-version: '${{github.event.inputs.version}}'
|
||||
working-directory: '.'
|
||||
gemini_api_key: '${{ secrets.GEMINI_API_KEY }}'
|
||||
npm-registry-url: '${{ vars.NPM_REGISTRY_URL }}'
|
||||
github-token: '${{ secrets.GITHUB_TOKEN }}'
|
||||
npm-registry-scope: '${{ vars.NPM_REGISTRY_SCOPE }}'
|
||||
+11
@@ -11,6 +11,8 @@
|
||||
.gemini/*
|
||||
!.gemini/config.yaml
|
||||
!.gemini/commands/
|
||||
!.gemini/skills/
|
||||
!.gemini/settings.json
|
||||
|
||||
# Note: .gemini-clipboard/ is NOT in gitignore so Gemini can access pasted images
|
||||
|
||||
@@ -46,6 +48,15 @@ packages/cli/src/generated/
|
||||
packages/core/src/generated/
|
||||
.integration-tests/
|
||||
packages/vscode-ide-companion/*.vsix
|
||||
packages/cli/download-ripgrep*/
|
||||
|
||||
# GHA credentials
|
||||
gha-creds-*.json
|
||||
|
||||
# Log files
|
||||
patch_output.log
|
||||
|
||||
.genkit
|
||||
.gemini-clipboard/
|
||||
.eslintcache
|
||||
evals/logs/
|
||||
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
npm run pre-commit || {
|
||||
echo ''
|
||||
echo '===================================================='
|
||||
echo 'pre-commit checks failed. in case of emergency, run:'
|
||||
echo ''
|
||||
echo 'git commit --no-verify'
|
||||
echo '===================================================='
|
||||
exit 1
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
http://localhost:16686/
|
||||
https://github.com/google-gemini/gemini-cli/issues/new/choose
|
||||
https://github.com/google-gemini/maintainers-gemini-cli/blob/main/npm.md
|
||||
https://github.com/settings/personal-access-tokens/new
|
||||
https://github.com/settings/tokens/new
|
||||
https://www.npmjs.com/package/@google/gemini-cli
|
||||
+3
-1
@@ -17,5 +17,7 @@ eslint.config.js
|
||||
**/generated
|
||||
gha-creds-*.json
|
||||
junit.xml
|
||||
npm-shrinkwrap.json
|
||||
.gemini-linters/
|
||||
Thumbs.db
|
||||
.pytest_cache
|
||||
**/SKILL.md
|
||||
|
||||
+11
-1
@@ -3,5 +3,15 @@
|
||||
"trailingComma": "all",
|
||||
"singleQuote": true,
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2
|
||||
"tabWidth": 2,
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["**/*.md"],
|
||||
"options": {
|
||||
"tabWidth": 2,
|
||||
"printWidth": 80,
|
||||
"proseWrap": "always"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Vendored
+5
-1
@@ -12,5 +12,9 @@
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
},
|
||||
"[markdown]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"vitest.disableWorkspaceWarning": true
|
||||
}
|
||||
|
||||
+327
-82
@@ -1,6 +1,18 @@
|
||||
# How to Contribute
|
||||
# How to contribute
|
||||
|
||||
We would love to accept your patches and contributions to this project.
|
||||
We would love to accept your patches and contributions to this project. This
|
||||
document includes:
|
||||
|
||||
- **[Before you begin](#before-you-begin):** Essential steps to take before
|
||||
becoming a Gemini CLI contributor.
|
||||
- **[Code contribution process](#code-contribution-process):** How to contribute
|
||||
code to Gemini CLI.
|
||||
- **[Development setup and workflow](#development-setup-and-workflow):** How to
|
||||
set up your development environment and workflow.
|
||||
- **[Documentation contribution process](#documentation-contribution-process):**
|
||||
How to contribute documentation to Gemini CLI.
|
||||
|
||||
We're looking forward to seeing your contributions!
|
||||
|
||||
## Before you begin
|
||||
|
||||
@@ -20,88 +32,156 @@ sign a new one.
|
||||
|
||||
### Review our Community Guidelines
|
||||
|
||||
This project follows [Google's Open Source Community
|
||||
Guidelines](https://opensource.google/conduct/).
|
||||
This project follows
|
||||
[Google's Open Source Community Guidelines](https://opensource.google/conduct/).
|
||||
|
||||
## Contribution Process
|
||||
## Code contribution process
|
||||
|
||||
### Code Reviews
|
||||
### Get started
|
||||
|
||||
The process for contributing code is as follows:
|
||||
|
||||
1. **Find an issue** that you want to work on. If an issue is tagged as
|
||||
`🔒Maintainers only`, this means it is reserved for project maintainers. We
|
||||
will not accept pull requests related to these issues. In the near future,
|
||||
we will explicitly mark issues looking for contributions using the
|
||||
`help wanted` label. If you believe an issue is a good candidate for
|
||||
community contribution, please leave a comment on the issue. A maintainer
|
||||
will review it and apply the `help-wanted` label if appropriate. Only
|
||||
maintainers should attempt to add the `help-wanted` label to an issue.
|
||||
2. **Fork the repository** and create a new branch.
|
||||
3. **Make your changes** in the `packages/` directory.
|
||||
4. **Ensure all checks pass** by running `npm run preflight`.
|
||||
5. **Open a pull request** with your changes.
|
||||
|
||||
### Code reviews
|
||||
|
||||
All submissions, including submissions by project members, require review. We
|
||||
use [GitHub pull requests](https://docs.github.com/articles/about-pull-requests)
|
||||
for this purpose.
|
||||
|
||||
### Self Assigning Issues
|
||||
If your pull request involves changes to `packages/cli` (the frontend), we
|
||||
recommend running our automated frontend review tool. **Note: This tool is
|
||||
currently experimental.** It helps detect common React anti-patterns, testing
|
||||
issues, and other frontend-specific best practices that are easy to miss.
|
||||
|
||||
If you're looking for an issue to work on, check out our list of issues that are labeled ["help wanted"](https://github.com/google-gemini/gemini-cli/issues?q=is%3Aissue+state%3Aopen+label%3A%22help+wanted%22).
|
||||
To run the review tool, enter the following command from within Gemini CLI:
|
||||
|
||||
To assign an issue to yourself, simply add a comment with the text `/assign`. The comment must contain only that text and nothing else. This command will assign the issue to you, provided it is not already assigned.
|
||||
```text
|
||||
/review-frontend <PR_NUMBER>
|
||||
```
|
||||
|
||||
Please note that you can have a maximum of 3 issues assigned to you at any given time.
|
||||
Replace `<PR_NUMBER>` with your pull request number. Authors are encouraged to
|
||||
run this on their own PRs for self-review, and reviewers should use it to
|
||||
augment their manual review process.
|
||||
|
||||
### Pull Request Guidelines
|
||||
### Self assigning issues
|
||||
|
||||
To help us review and merge your PRs quickly, please follow these guidelines. PRs that do not meet these standards may be closed.
|
||||
To assign an issue to yourself, simply add a comment with the text `/assign`.
|
||||
The comment must contain only that text and nothing else. This command will
|
||||
assign the issue to you, provided it is not already assigned.
|
||||
|
||||
#### 1. Link to an Existing Issue
|
||||
Please note that you can have a maximum of 3 issues assigned to you at any given
|
||||
time.
|
||||
|
||||
All PRs should be linked to an existing issue in our tracker. This ensures that every change has been discussed and is aligned with the project's goals before any code is written.
|
||||
### Pull request guidelines
|
||||
|
||||
To help us review and merge your PRs quickly, please follow these guidelines.
|
||||
PRs that do not meet these standards may be closed.
|
||||
|
||||
#### 1. Link to an existing issue
|
||||
|
||||
All PRs should be linked to an existing issue in our tracker. This ensures that
|
||||
every change has been discussed and is aligned with the project's goals before
|
||||
any code is written.
|
||||
|
||||
- **For bug fixes:** The PR should be linked to the bug report issue.
|
||||
- **For features:** The PR should be linked to the feature request or proposal issue that has been approved by a maintainer.
|
||||
- **For features:** The PR should be linked to the feature request or proposal
|
||||
issue that has been approved by a maintainer.
|
||||
|
||||
If an issue for your change doesn't exist, please **open one first** and wait for feedback before you start coding.
|
||||
If an issue for your change doesn't exist, we will automatically close your PR
|
||||
along with a comment reminding you to associate the PR with an issue. The ideal
|
||||
workflow starts with an issue that has been reviewed and approved by a
|
||||
maintainer. Please **open the issue first** and wait for feedback before you
|
||||
start coding.
|
||||
|
||||
#### 2. Keep It Small and Focused
|
||||
#### 2. Keep it small and focused
|
||||
|
||||
We favor small, atomic PRs that address a single issue or add a single, self-contained feature.
|
||||
We favor small, atomic PRs that address a single issue or add a single,
|
||||
self-contained feature.
|
||||
|
||||
- **Do:** Create a PR that fixes one specific bug or adds one specific feature.
|
||||
- **Don't:** Bundle multiple unrelated changes (e.g., a bug fix, a new feature, and a refactor) into a single PR.
|
||||
- **Don't:** Bundle multiple unrelated changes (e.g., a bug fix, a new feature,
|
||||
and a refactor) into a single PR.
|
||||
|
||||
Large changes should be broken down into a series of smaller, logical PRs that can be reviewed and merged independently.
|
||||
Large changes should be broken down into a series of smaller, logical PRs that
|
||||
can be reviewed and merged independently.
|
||||
|
||||
#### 3. Use Draft PRs for Work in Progress
|
||||
#### 3. Use draft PRs for work in progress
|
||||
|
||||
If you'd like to get early feedback on your work, please use GitHub's **Draft Pull Request** feature. This signals to the maintainers that the PR is not yet ready for a formal review but is open for discussion and initial feedback.
|
||||
If you'd like to get early feedback on your work, please use GitHub's **Draft
|
||||
Pull Request** feature. This signals to the maintainers that the PR is not yet
|
||||
ready for a formal review but is open for discussion and initial feedback.
|
||||
|
||||
#### 4. Ensure All Checks Pass
|
||||
#### 4. Ensure all checks pass
|
||||
|
||||
Before submitting your PR, ensure that all automated checks are passing by running `npm run preflight`. This command runs all tests, linting, and other style checks.
|
||||
Before submitting your PR, ensure that all automated checks are passing by
|
||||
running `npm run preflight`. This command runs all tests, linting, and other
|
||||
style checks.
|
||||
|
||||
#### 5. Update Documentation
|
||||
#### 5. Update documentation
|
||||
|
||||
If your PR introduces a user-facing change (e.g., a new command, a modified flag, or a change in behavior), you must also update the relevant documentation in the `/docs` directory.
|
||||
If your PR introduces a user-facing change (e.g., a new command, a modified
|
||||
flag, or a change in behavior), you must also update the relevant documentation
|
||||
in the `/docs` directory.
|
||||
|
||||
#### 6. Write Clear Commit Messages and a Good PR Description
|
||||
See more about writing documentation:
|
||||
[Documentation contribution process](#documentation-contribution-process).
|
||||
|
||||
Your PR should have a clear, descriptive title and a detailed description of the changes. Follow the [Conventional Commits](https://www.conventionalcommits.org/) standard for your commit messages.
|
||||
#### 6. Write clear commit messages and a good PR description
|
||||
|
||||
- **Good PR Title:** `feat(cli): Add --json flag to 'config get' command`
|
||||
- **Bad PR Title:** `Made some changes`
|
||||
Your PR should have a clear, descriptive title and a detailed description of the
|
||||
changes. Follow the [Conventional Commits](https://www.conventionalcommits.org/)
|
||||
standard for your commit messages.
|
||||
|
||||
In the PR description, explain the "why" behind your changes and link to the relevant issue (e.g., `Fixes #123`).
|
||||
- **Good PR title:** `feat(cli): Add --json flag to 'config get' command`
|
||||
- **Bad PR title:** `Made some changes`
|
||||
|
||||
## Forking
|
||||
In the PR description, explain the "why" behind your changes and link to the
|
||||
relevant issue (e.g., `Fixes #123`).
|
||||
|
||||
If you are forking the repository you will be able to run the Build, Test and Integration test workflows. However in order to make the integration tests run you'll need to add a [GitHub Repository Secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository) with a value of `GEMINI_API_KEY` and set that to a valid API key that you have available. Your key and secret are private to your repo; no one without access can see your key and you cannot see any secrets related to this repo.
|
||||
### Forking
|
||||
|
||||
Additionally you will need to click on the `Actions` tab and enable workflows for your repository, you'll find it's the large blue button in the center of the screen.
|
||||
If you are forking the repository you will be able to run the Build, Test and
|
||||
Integration test workflows. However in order to make the integration tests run
|
||||
you'll need to add a
|
||||
[GitHub Repository Secret](https://docs.github.com/en/actions/security-for-github-actions/security-guides/using-secrets-in-github-actions#creating-secrets-for-a-repository)
|
||||
with a value of `GEMINI_API_KEY` and set that to a valid API key that you have
|
||||
available. Your key and secret are private to your repo; no one without access
|
||||
can see your key and you cannot see any secrets related to this repo.
|
||||
|
||||
## Development Setup and Workflow
|
||||
Additionally you will need to click on the `Actions` tab and enable workflows
|
||||
for your repository, you'll find it's the large blue button in the center of the
|
||||
screen.
|
||||
|
||||
This section guides contributors on how to build, modify, and understand the development setup of this project.
|
||||
### Development setup and workflow
|
||||
|
||||
### Setting Up the Development Environment
|
||||
This section guides contributors on how to build, modify, and understand the
|
||||
development setup of this project.
|
||||
|
||||
### Setting up the development environment
|
||||
|
||||
**Prerequisites:**
|
||||
|
||||
1. **Node.js**:
|
||||
- **Development:** Please use Node.js `~20.19.0`. This specific version is required due to an upstream development dependency issue. You can use a tool like [nvm](https://github.com/nvm-sh/nvm) to manage Node.js versions.
|
||||
- **Production:** For running the CLI in a production environment, any version of Node.js `>=20` is acceptable.
|
||||
- **Development:** Please use Node.js `~20.19.0`. This specific version is
|
||||
required due to an upstream development dependency issue. You can use a
|
||||
tool like [nvm](https://github.com/nvm-sh/nvm) to manage Node.js versions.
|
||||
- **Production:** For running the CLI in a production environment, any
|
||||
version of Node.js `>=20` is acceptable.
|
||||
2. **Git**
|
||||
|
||||
### Build Process
|
||||
### Build process
|
||||
|
||||
To clone the repository:
|
||||
|
||||
@@ -122,13 +202,19 @@ To build the entire project (all packages):
|
||||
npm run build
|
||||
```
|
||||
|
||||
This command typically compiles TypeScript to JavaScript, bundles assets, and prepares the packages for execution. Refer to `scripts/build.js` and `package.json` scripts for more details on what happens during the build.
|
||||
This command typically compiles TypeScript to JavaScript, bundles assets, and
|
||||
prepares the packages for execution. Refer to `scripts/build.js` and
|
||||
`package.json` scripts for more details on what happens during the build.
|
||||
|
||||
### Enabling Sandboxing
|
||||
### Enabling sandboxing
|
||||
|
||||
[Sandboxing](#sandboxing) is highly recommended and requires, at a minimum, setting `GEMINI_SANDBOX=true` in your `~/.env` and ensuring a sandboxing provider (e.g. `macOS Seatbelt`, `docker`, or `podman`) is available. See [Sandboxing](#sandboxing) for details.
|
||||
[Sandboxing](#sandboxing) is highly recommended and requires, at a minimum,
|
||||
setting `GEMINI_SANDBOX=true` in your `~/.env` and ensuring a sandboxing
|
||||
provider (e.g. `macOS Seatbelt`, `docker`, or `podman`) is available. See
|
||||
[Sandboxing](#sandboxing) for details.
|
||||
|
||||
To build both the `gemini` CLI utility and the sandbox container, run `build:all` from the root directory:
|
||||
To build both the `gemini` CLI utility and the sandbox container, run
|
||||
`build:all` from the root directory:
|
||||
|
||||
```bash
|
||||
npm run build:all
|
||||
@@ -136,21 +222,25 @@ npm run build:all
|
||||
|
||||
To skip building the sandbox container, you can use `npm run build` instead.
|
||||
|
||||
### Running
|
||||
### Running the CLI
|
||||
|
||||
To start the Gemini CLI from the source code (after building), run the following command from the root directory:
|
||||
To start the Gemini CLI from the source code (after building), run the following
|
||||
command from the root directory:
|
||||
|
||||
```bash
|
||||
npm start
|
||||
```
|
||||
|
||||
If you'd like to run the source build outside of the gemini-cli folder you can utilize `npm link path/to/gemini-cli/packages/cli` (see: [docs](https://docs.npmjs.com/cli/v9/commands/npm-link)) or `alias gemini="node path/to/gemini-cli/packages/cli"` to run with `gemini`
|
||||
If you'd like to run the source build outside of the gemini-cli folder, you can
|
||||
utilize `npm link path/to/gemini-cli/packages/cli` (see:
|
||||
[docs](https://docs.npmjs.com/cli/v9/commands/npm-link)) or
|
||||
`alias gemini="node path/to/gemini-cli/packages/cli"` to run with `gemini`
|
||||
|
||||
### Running Tests
|
||||
### Running tests
|
||||
|
||||
This project contains two types of tests: unit tests and integration tests.
|
||||
|
||||
#### Unit Tests
|
||||
#### Unit tests
|
||||
|
||||
To execute the unit test suite for the project:
|
||||
|
||||
@@ -158,11 +248,14 @@ To execute the unit test suite for the project:
|
||||
npm run test
|
||||
```
|
||||
|
||||
This will run tests located in the `packages/core` and `packages/cli` directories. Ensure tests pass before submitting any changes. For a more comprehensive check, it is recommended to run `npm run preflight`.
|
||||
This will run tests located in the `packages/core` and `packages/cli`
|
||||
directories. Ensure tests pass before submitting any changes. For a more
|
||||
comprehensive check, it is recommended to run `npm run preflight`.
|
||||
|
||||
#### Integration Tests
|
||||
#### Integration tests
|
||||
|
||||
The integration tests are designed to validate the end-to-end functionality of the Gemini CLI. They are not run as part of the default `npm run test` command.
|
||||
The integration tests are designed to validate the end-to-end functionality of
|
||||
the Gemini CLI. They are not run as part of the default `npm run test` command.
|
||||
|
||||
To run the integration tests, use the following command:
|
||||
|
||||
@@ -170,9 +263,10 @@ To run the integration tests, use the following command:
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
For more detailed information on the integration testing framework, please see the [Integration Tests documentation](./docs/integration-tests.md).
|
||||
For more detailed information on the integration testing framework, please see
|
||||
the [Integration Tests documentation](/docs/integration-tests.md).
|
||||
|
||||
### Linting and Preflight Checks
|
||||
### Linting and preflight checks
|
||||
|
||||
To ensure code quality and formatting consistency, run the preflight check:
|
||||
|
||||
@@ -180,11 +274,13 @@ To ensure code quality and formatting consistency, run the preflight check:
|
||||
npm run preflight
|
||||
```
|
||||
|
||||
This command will run ESLint, Prettier, all tests, and other checks as defined in the project's `package.json`.
|
||||
This command will run ESLint, Prettier, all tests, and other checks as defined
|
||||
in the project's `package.json`.
|
||||
|
||||
_ProTip_
|
||||
|
||||
after cloning create a git precommit hook file to ensure your commits are always clean.
|
||||
after cloning create a git precommit hook file to ensure your commits are always
|
||||
clean.
|
||||
|
||||
```bash
|
||||
echo "
|
||||
@@ -198,51 +294,71 @@ fi
|
||||
|
||||
#### Formatting
|
||||
|
||||
To separately format the code in this project by running the following command from the root directory:
|
||||
To separately format the code in this project by running the following command
|
||||
from the root directory:
|
||||
|
||||
```bash
|
||||
npm run format
|
||||
```
|
||||
|
||||
This command uses Prettier to format the code according to the project's style guidelines.
|
||||
This command uses Prettier to format the code according to the project's style
|
||||
guidelines.
|
||||
|
||||
#### Linting
|
||||
|
||||
To separately lint the code in this project, run the following command from the root directory:
|
||||
To separately lint the code in this project, run the following command from the
|
||||
root directory:
|
||||
|
||||
```bash
|
||||
npm run lint
|
||||
```
|
||||
|
||||
### Coding Conventions
|
||||
### Coding conventions
|
||||
|
||||
- Please adhere to the coding style, patterns, and conventions used throughout the existing codebase.
|
||||
- Consult [GEMINI.md](https://github.com/google-gemini/gemini-cli/blob/main/GEMINI.md) (typically found in the project root) for specific instructions related to AI-assisted development, including conventions for React, comments, and Git usage.
|
||||
- **Imports:** Pay special attention to import paths. The project uses ESLint to enforce restrictions on relative imports between packages.
|
||||
- Please adhere to the coding style, patterns, and conventions used throughout
|
||||
the existing codebase.
|
||||
- Consult
|
||||
[GEMINI.md](https://github.com/google-gemini/gemini-cli/blob/main/GEMINI.md)
|
||||
(typically found in the project root) for specific instructions related to
|
||||
AI-assisted development, including conventions for React, comments, and Git
|
||||
usage.
|
||||
- **Imports:** Pay special attention to import paths. The project uses ESLint to
|
||||
enforce restrictions on relative imports between packages.
|
||||
|
||||
### Project Structure
|
||||
### Project structure
|
||||
|
||||
- `packages/`: Contains the individual sub-packages of the project.
|
||||
- `a2a-server`: A2A server implementation for the Gemini CLI. (Experimental)
|
||||
- `cli/`: The command-line interface.
|
||||
- `core/`: The core backend logic for the Gemini CLI.
|
||||
- `test-utils` Utilities for creating and cleaning temporary file systems for
|
||||
testing.
|
||||
- `vscode-ide-companion/`: The Gemini CLI Companion extension pairs with
|
||||
Gemini CLI.
|
||||
- `docs/`: Contains all project documentation.
|
||||
- `scripts/`: Utility scripts for building, testing, and development tasks.
|
||||
|
||||
For more detailed architecture, see `docs/architecture.md`.
|
||||
|
||||
## Debugging
|
||||
### Debugging
|
||||
|
||||
### VS Code:
|
||||
#### VS Code
|
||||
|
||||
0. Run the CLI to interactively debug in VS Code with `F5`
|
||||
1. Start the CLI in debug mode from the root directory:
|
||||
```bash
|
||||
npm run debug
|
||||
```
|
||||
This command runs `node --inspect-brk dist/gemini.js` within the `packages/cli` directory, pausing execution until a debugger attaches. You can then open `chrome://inspect` in your Chrome browser to connect to the debugger.
|
||||
2. In VS Code, use the "Attach" launch configuration (found in `.vscode/launch.json`).
|
||||
This command runs `node --inspect-brk dist/gemini.js` within the
|
||||
`packages/cli` directory, pausing execution until a debugger attaches. You
|
||||
can then open `chrome://inspect` in your Chrome browser to connect to the
|
||||
debugger.
|
||||
2. In VS Code, use the "Attach" launch configuration (found in
|
||||
`.vscode/launch.json`).
|
||||
|
||||
Alternatively, you can use the "Launch Program" configuration in VS Code if you prefer to launch the currently open file directly, but 'F5' is generally recommended.
|
||||
Alternatively, you can use the "Launch Program" configuration in VS Code if you
|
||||
prefer to launch the currently open file directly, but 'F5' is generally
|
||||
recommended.
|
||||
|
||||
To hit a breakpoint inside the sandbox container run:
|
||||
|
||||
@@ -250,11 +366,14 @@ To hit a breakpoint inside the sandbox container run:
|
||||
DEBUG=1 gemini
|
||||
```
|
||||
|
||||
**Note:** If you have `DEBUG=true` in a project's `.env` file, it won't affect gemini-cli due to automatic exclusion. Use `.gemini/.env` files for gemini-cli specific debug settings.
|
||||
**Note:** If you have `DEBUG=true` in a project's `.env` file, it won't affect
|
||||
gemini-cli due to automatic exclusion. Use `.gemini/.env` files for gemini-cli
|
||||
specific debug settings.
|
||||
|
||||
### React DevTools
|
||||
|
||||
To debug the CLI's React-based UI, you can use React DevTools. Ink, the library used for the CLI's interface, is compatible with React DevTools version 4.x.
|
||||
To debug the CLI's React-based UI, you can use React DevTools. Ink, the library
|
||||
used for the CLI's interface, is compatible with React DevTools version 4.x.
|
||||
|
||||
1. **Start the Gemini CLI in development mode:**
|
||||
|
||||
@@ -262,7 +381,8 @@ To debug the CLI's React-based UI, you can use React DevTools. Ink, the library
|
||||
DEV=true npm start
|
||||
```
|
||||
|
||||
2. **Install and run React DevTools version 4.28.5 (or the latest compatible 4.x version):**
|
||||
2. **Install and run React DevTools version 4.28.5 (or the latest compatible
|
||||
4.x version):**
|
||||
|
||||
You can either install it globally:
|
||||
|
||||
@@ -280,25 +400,62 @@ To debug the CLI's React-based UI, you can use React DevTools. Ink, the library
|
||||
Your running CLI application should then connect to React DevTools.
|
||||

|
||||
|
||||
## Sandboxing
|
||||
### Sandboxing
|
||||
|
||||
### macOS Seatbelt
|
||||
#### macOS Seatbelt
|
||||
|
||||
On macOS, `gemini` uses Seatbelt (`sandbox-exec`) under a `permissive-open` profile (see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) that restricts writes to the project folder but otherwise allows all other operations and outbound network traffic ("open") by default. You can switch to a `restrictive-closed` profile (see `packages/cli/src/utils/sandbox-macos-restrictive-closed.sb`) that declines all operations and outbound network traffic ("closed") by default by setting `SEATBELT_PROFILE=restrictive-closed` in your environment or `.env` file. Available built-in profiles are `{permissive,restrictive}-{open,closed,proxied}` (see below for proxied networking). You can also switch to a custom profile `SEATBELT_PROFILE=<profile>` if you also create a file `.gemini/sandbox-macos-<profile>.sb` under your project settings directory `.gemini`.
|
||||
On macOS, `gemini` uses Seatbelt (`sandbox-exec`) under a `permissive-open`
|
||||
profile (see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) that
|
||||
restricts writes to the project folder but otherwise allows all other operations
|
||||
and outbound network traffic ("open") by default. You can switch to a
|
||||
`restrictive-closed` profile (see
|
||||
`packages/cli/src/utils/sandbox-macos-restrictive-closed.sb`) that declines all
|
||||
operations and outbound network traffic ("closed") by default by setting
|
||||
`SEATBELT_PROFILE=restrictive-closed` in your environment or `.env` file.
|
||||
Available built-in profiles are `{permissive,restrictive}-{open,closed,proxied}`
|
||||
(see below for proxied networking). You can also switch to a custom profile
|
||||
`SEATBELT_PROFILE=<profile>` if you also create a file
|
||||
`.gemini/sandbox-macos-<profile>.sb` under your project settings directory
|
||||
`.gemini`.
|
||||
|
||||
### Container-based Sandboxing (All Platforms)
|
||||
#### Container-based sandboxing (all platforms)
|
||||
|
||||
For stronger container-based sandboxing on macOS or other platforms, you can set `GEMINI_SANDBOX=true|docker|podman|<command>` in your environment or `.env` file. The specified command (or if `true` then either `docker` or `podman`) must be installed on the host machine. Once enabled, `npm run build:all` will build a minimal container ("sandbox") image and `npm start` will launch inside a fresh instance of that container. The first build can take 20-30s (mostly due to downloading of the base image) but after that both build and start overhead should be minimal. Default builds (`npm run build`) will not rebuild the sandbox.
|
||||
For stronger container-based sandboxing on macOS or other platforms, you can set
|
||||
`GEMINI_SANDBOX=true|docker|podman|<command>` in your environment or `.env`
|
||||
file. The specified command (or if `true` then either `docker` or `podman`) must
|
||||
be installed on the host machine. Once enabled, `npm run build:all` will build a
|
||||
minimal container ("sandbox") image and `npm start` will launch inside a fresh
|
||||
instance of that container. The first build can take 20-30s (mostly due to
|
||||
downloading of the base image) but after that both build and start overhead
|
||||
should be minimal. Default builds (`npm run build`) will not rebuild the
|
||||
sandbox.
|
||||
|
||||
Container-based sandboxing mounts the project directory (and system temp directory) with read-write access and is started/stopped/removed automatically as you start/stop Gemini CLI. Files created within the sandbox should be automatically mapped to your user/group on host machine. You can easily specify additional mounts, ports, or environment variables by setting `SANDBOX_{MOUNTS,PORTS,ENV}` as needed. You can also fully customize the sandbox for your projects by creating the files `.gemini/sandbox.Dockerfile` and/or `.gemini/sandbox.bashrc` under your project settings directory (`.gemini`) and running `gemini` with `BUILD_SANDBOX=1` to trigger building of your custom sandbox.
|
||||
Container-based sandboxing mounts the project directory (and system temp
|
||||
directory) with read-write access and is started/stopped/removed automatically
|
||||
as you start/stop Gemini CLI. Files created within the sandbox should be
|
||||
automatically mapped to your user/group on host machine. You can easily specify
|
||||
additional mounts, ports, or environment variables by setting
|
||||
`SANDBOX_{MOUNTS,PORTS,ENV}` as needed. You can also fully customize the sandbox
|
||||
for your projects by creating the files `.gemini/sandbox.Dockerfile` and/or
|
||||
`.gemini/sandbox.bashrc` under your project settings directory (`.gemini`) and
|
||||
running `gemini` with `BUILD_SANDBOX=1` to trigger building of your custom
|
||||
sandbox.
|
||||
|
||||
#### Proxied Networking
|
||||
#### Proxied networking
|
||||
|
||||
All sandboxing methods, including macOS Seatbelt using `*-proxied` profiles, support restricting outbound network traffic through a custom proxy server that can be specified as `GEMINI_SANDBOX_PROXY_COMMAND=<command>`, where `<command>` must start a proxy server that listens on `:::8877` for relevant requests. See `docs/examples/proxy-script.md` for a minimal proxy that only allows `HTTPS` connections to `example.com:443` (e.g. `curl https://example.com`) and declines all other requests. The proxy is started and stopped automatically alongside the sandbox.
|
||||
All sandboxing methods, including macOS Seatbelt using `*-proxied` profiles,
|
||||
support restricting outbound network traffic through a custom proxy server that
|
||||
can be specified as `GEMINI_SANDBOX_PROXY_COMMAND=<command>`, where `<command>`
|
||||
must start a proxy server that listens on `:::8877` for relevant requests. See
|
||||
`docs/examples/proxy-script.md` for a minimal proxy that only allows `HTTPS`
|
||||
connections to `example.com:443` (e.g. `curl https://example.com`) and declines
|
||||
all other requests. The proxy is started and stopped automatically alongside the
|
||||
sandbox.
|
||||
|
||||
## Manual Publish
|
||||
### Manual publish
|
||||
|
||||
We publish an artifact for each commit to our internal registry. But if you need to manually cut a local build, then run the following commands:
|
||||
We publish an artifact for each commit to our internal registry. But if you need
|
||||
to manually cut a local build, then run the following commands:
|
||||
|
||||
```
|
||||
npm run clean
|
||||
@@ -307,3 +464,91 @@ npm run auth
|
||||
npm run prerelease:dev
|
||||
npm publish --workspaces
|
||||
```
|
||||
|
||||
## Documentation contribution process
|
||||
|
||||
Our documentation must be kept up-to-date with our code contributions. We want
|
||||
our documentation to be clear, concise, and helpful to our users. We value:
|
||||
|
||||
- **Clarity:** Use simple and direct language. Avoid jargon where possible.
|
||||
- **Accuracy:** Ensure all information is correct and up-to-date.
|
||||
- **Completeness:** Cover all aspects of a feature or topic.
|
||||
- **Examples:** Provide practical examples to help users understand how to use
|
||||
Gemini CLI.
|
||||
|
||||
### Getting started
|
||||
|
||||
The process for contributing to the documentation is similar to contributing
|
||||
code.
|
||||
|
||||
1. **Fork the repository** and create a new branch.
|
||||
2. **Make your changes** in the `/docs` directory.
|
||||
3. **Preview your changes locally** in Markdown rendering.
|
||||
4. **Lint and format your changes.** Our preflight check includes linting and
|
||||
formatting for documentation files.
|
||||
```bash
|
||||
npm run preflight
|
||||
```
|
||||
5. **Open a pull request** with your changes.
|
||||
|
||||
### Documentation structure
|
||||
|
||||
Our documentation is organized using [sidebar.json](/docs/sidebar.json) as the
|
||||
table of contents. When adding new documentation:
|
||||
|
||||
1. Create your markdown file **in the appropriate directory** under `/docs`.
|
||||
2. Add an entry to `sidebar.json` in the relevant section.
|
||||
3. Ensure all internal links use relative paths and point to existing files.
|
||||
|
||||
### Style guide
|
||||
|
||||
We follow the
|
||||
[Google Developer Documentation Style Guide](https://developers.google.com/style).
|
||||
Please refer to it for guidance on writing style, tone, and formatting.
|
||||
|
||||
#### Key style points
|
||||
|
||||
- Use sentence case for headings.
|
||||
- Write in second person ("you") when addressing the reader.
|
||||
- Use present tense.
|
||||
- Keep paragraphs short and focused.
|
||||
- Use code blocks with appropriate language tags for syntax highlighting.
|
||||
- Include practical examples whenever possible.
|
||||
|
||||
### Linting and formatting
|
||||
|
||||
We use `prettier` to enforce a consistent style across our documentation. The
|
||||
`npm run preflight` command will check for any linting issues.
|
||||
|
||||
You can also run the linter and formatter separately:
|
||||
|
||||
- `npm run lint` - Check for linting issues
|
||||
- `npm run format` - Auto-format markdown files
|
||||
- `npm run lint:fix` - Auto-fix linting issues where possible
|
||||
|
||||
Please make sure your contributions are free of linting errors before submitting
|
||||
a pull request.
|
||||
|
||||
### Before you submit
|
||||
|
||||
Before submitting your documentation pull request, please:
|
||||
|
||||
1. Run `npm run preflight` to ensure all checks pass.
|
||||
2. Review your changes for clarity and accuracy.
|
||||
3. Check that all links work correctly.
|
||||
4. Ensure any code examples are tested and functional.
|
||||
5. Sign the
|
||||
[Contributor License Agreement (CLA)](https://cla.developers.google.com/) if
|
||||
you haven't already.
|
||||
|
||||
### Need help?
|
||||
|
||||
If you have questions about contributing documentation:
|
||||
|
||||
- Check our [FAQ](/docs/faq.md).
|
||||
- Review existing documentation for examples.
|
||||
- Open [an issue](https://github.com/google-gemini/gemini-cli/issues) to discuss
|
||||
your proposed changes.
|
||||
- Reach out to the maintainers.
|
||||
|
||||
We appreciate your contributions to making Gemini CLI documentation better!
|
||||
|
||||
+4
-4
@@ -40,11 +40,11 @@ ENV PATH=$PATH:/usr/local/share/npm-global/bin
|
||||
USER node
|
||||
|
||||
# install gemini-cli and clean up
|
||||
COPY packages/cli/dist/google-gemini-cli-*.tgz /usr/local/share/npm-global/gemini-cli.tgz
|
||||
COPY packages/core/dist/google-gemini-cli-core-*.tgz /usr/local/share/npm-global/gemini-core.tgz
|
||||
RUN npm install -g /usr/local/share/npm-global/gemini-cli.tgz /usr/local/share/npm-global/gemini-core.tgz \
|
||||
COPY packages/cli/dist/google-gemini-cli-*.tgz /tmp/gemini-cli.tgz
|
||||
COPY packages/core/dist/google-gemini-cli-core-*.tgz /tmp/gemini-core.tgz
|
||||
RUN npm install -g /tmp/gemini-cli.tgz /tmp/gemini-core.tgz \
|
||||
&& npm cache clean --force \
|
||||
&& rm -f /usr/local/share/npm-global/gemini-{cli,core}.tgz
|
||||
&& rm -f /tmp/gemini-{cli,core}.tgz
|
||||
|
||||
# default entrypoint when none specified
|
||||
CMD ["gemini"]
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
## Building and running
|
||||
|
||||
Before submitting any changes, it is crucial to validate them by running the full preflight check. This command will build the repository, run all tests, check for type errors, and lint the code.
|
||||
Before submitting any changes, it is crucial to validate them by running the
|
||||
full preflight check. This command will build the repository, run all tests,
|
||||
check for type errors, and lint the code.
|
||||
|
||||
To run the full suite of checks, execute the following command:
|
||||
|
||||
@@ -8,33 +10,62 @@ To run the full suite of checks, execute the following command:
|
||||
npm run preflight
|
||||
```
|
||||
|
||||
This single command ensures that your changes meet all the quality gates of the project. While you can run the individual steps (`build`, `test`, `typecheck`, `lint`) separately, it is highly recommended to use `npm run preflight` to ensure a comprehensive validation.
|
||||
This single command ensures that your changes meet all the quality gates of the
|
||||
project. While you can run the individual steps (`build`, `test`, `typecheck`,
|
||||
`lint`) separately, it is highly recommended to use `npm run preflight` to
|
||||
ensure a comprehensive validation.
|
||||
|
||||
## Running Tests in Workspaces\*\*: To run a specific test file within a
|
||||
|
||||
workspace, use the command:
|
||||
`npm test -w <workspace-name> -- <path/to/test-file.test.ts>`. **CRITICAL**: The
|
||||
`<path/to/test-file.test.ts>` MUST be relative to the workspace directory root,
|
||||
NOT the project root.
|
||||
|
||||
- _Example (Core package)_:
|
||||
`npm test -w @google/gemini-cli-core -- src/routing/modelRouterService.test.ts`
|
||||
- _Common workspaces_: `@google/gemini-cli`, `@google/gemini-cli-core`.
|
||||
|
||||
## Writing Tests
|
||||
|
||||
This project uses **Vitest** as its primary testing framework. When writing tests, aim to follow existing patterns. Key conventions include:
|
||||
This project uses **Vitest** as its primary testing framework. When writing
|
||||
tests, aim to follow existing patterns. Key conventions include:
|
||||
|
||||
### Test Structure and Framework
|
||||
|
||||
- **Framework**: All tests are written using Vitest (`describe`, `it`, `expect`, `vi`).
|
||||
- **File Location**: Test files (`*.test.ts` for logic, `*.test.tsx` for React components) are co-located with the source files they test.
|
||||
- **Framework**: All tests are written using Vitest (`describe`, `it`, `expect`,
|
||||
`vi`).
|
||||
- **File Location**: Test files (`*.test.ts` for logic, `*.test.tsx` for React
|
||||
components) are co-located with the source files they test.
|
||||
- **Configuration**: Test environments are defined in `vitest.config.ts` files.
|
||||
- **Setup/Teardown**: Use `beforeEach` and `afterEach`. Commonly, `vi.resetAllMocks()` is called in `beforeEach` and `vi.restoreAllMocks()` in `afterEach`.
|
||||
- **Setup/Teardown**: Use `beforeEach` and `afterEach`. Commonly,
|
||||
`vi.resetAllMocks()` is called in `beforeEach` and `vi.restoreAllMocks()` in
|
||||
`afterEach`.
|
||||
|
||||
### Mocking (`vi` from Vitest)
|
||||
|
||||
- **ES Modules**: Mock with `vi.mock('module-name', async (importOriginal) => { ... })`. Use `importOriginal` for selective mocking.
|
||||
- _Example_: `vi.mock('os', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, homedir: vi.fn() }; });`
|
||||
- **Mocking Order**: For critical dependencies (e.g., `os`, `fs`) that affect module-level constants, place `vi.mock` at the _very top_ of the test file, before other imports.
|
||||
- **Hoisting**: Use `const myMock = vi.hoisted(() => vi.fn());` if a mock function needs to be defined before its use in a `vi.mock` factory.
|
||||
- **Mock Functions**: Create with `vi.fn()`. Define behavior with `mockImplementation()`, `mockResolvedValue()`, or `mockRejectedValue()`.
|
||||
- **Spying**: Use `vi.spyOn(object, 'methodName')`. Restore spies with `mockRestore()` in `afterEach`.
|
||||
- **ES Modules**: Mock with
|
||||
`vi.mock('module-name', async (importOriginal) => { ... })`. Use
|
||||
`importOriginal` for selective mocking.
|
||||
- _Example_:
|
||||
`vi.mock('os', async (importOriginal) => { const actual = await importOriginal(); return { ...actual, homedir: vi.fn() }; });`
|
||||
- **Mocking Order**: For critical dependencies (e.g., `os`, `fs`) that affect
|
||||
module-level constants, place `vi.mock` at the _very top_ of the test file,
|
||||
before other imports.
|
||||
- **Hoisting**: Use `const myMock = vi.hoisted(() => vi.fn());` if a mock
|
||||
function needs to be defined before its use in a `vi.mock` factory.
|
||||
- **Mock Functions**: Create with `vi.fn()`. Define behavior with
|
||||
`mockImplementation()`, `mockResolvedValue()`, or `mockRejectedValue()`.
|
||||
- **Spying**: Use `vi.spyOn(object, 'methodName')`. Restore spies with
|
||||
`mockRestore()` in `afterEach`.
|
||||
|
||||
### Commonly Mocked Modules
|
||||
|
||||
- **Node.js built-ins**: `fs`, `fs/promises`, `os` (especially `os.homedir()`), `path`, `child_process` (`execSync`, `spawn`).
|
||||
- **Node.js built-ins**: `fs`, `fs/promises`, `os` (especially `os.homedir()`),
|
||||
`path`, `child_process` (`execSync`, `spawn`).
|
||||
- **External SDKs**: `@google/genai`, `@modelcontextprotocol/sdk`.
|
||||
- **Internal Project Modules**: Dependencies from other project packages are often mocked.
|
||||
- **Internal Project Modules**: Dependencies from other project packages are
|
||||
often mocked.
|
||||
|
||||
### React Component Testing (CLI UI - Ink)
|
||||
|
||||
@@ -46,13 +77,16 @@ This project uses **Vitest** as its primary testing framework. When writing test
|
||||
### Asynchronous Testing
|
||||
|
||||
- Use `async/await`.
|
||||
- For timers, use `vi.useFakeTimers()`, `vi.advanceTimersByTimeAsync()`, `vi.runAllTimersAsync()`.
|
||||
- For timers, use `vi.useFakeTimers()`, `vi.advanceTimersByTimeAsync()`,
|
||||
`vi.runAllTimersAsync()`.
|
||||
- Test promise rejections with `await expect(promise).rejects.toThrow(...)`.
|
||||
|
||||
### General Guidance
|
||||
|
||||
- When adding tests, first examine existing tests to understand and conform to established conventions.
|
||||
- Pay close attention to the mocks at the top of existing test files; they reveal critical dependencies and how they are managed in a test environment.
|
||||
- When adding tests, first examine existing tests to understand and conform to
|
||||
established conventions.
|
||||
- Pay close attention to the mocks at the top of existing test files; they
|
||||
reveal critical dependencies and how they are managed in a test environment.
|
||||
|
||||
## Git Repo
|
||||
|
||||
@@ -60,42 +94,101 @@ The main branch for this project is called "main"
|
||||
|
||||
## JavaScript/TypeScript
|
||||
|
||||
When contributing to this React, Node, and TypeScript codebase, please prioritize the use of plain JavaScript objects with accompanying TypeScript interface or type declarations over JavaScript class syntax. This approach offers significant advantages, especially concerning interoperability with React and overall code maintainability.
|
||||
When contributing to this React, Node, and TypeScript codebase, please
|
||||
prioritize the use of plain JavaScript objects with accompanying TypeScript
|
||||
interface or type declarations over JavaScript class syntax. This approach
|
||||
offers significant advantages, especially concerning interoperability with React
|
||||
and overall code maintainability.
|
||||
|
||||
### Preferring Plain Objects over Classes
|
||||
|
||||
JavaScript classes, by their nature, are designed to encapsulate internal state and behavior. While this can be useful in some object-oriented paradigms, it often introduces unnecessary complexity and friction when working with React's component-based architecture. Here's why plain objects are preferred:
|
||||
JavaScript classes, by their nature, are designed to encapsulate internal state
|
||||
and behavior. While this can be useful in some object-oriented paradigms, it
|
||||
often introduces unnecessary complexity and friction when working with React's
|
||||
component-based architecture. Here's why plain objects are preferred:
|
||||
|
||||
- Seamless React Integration: React components thrive on explicit props and state management. Classes' tendency to store internal state directly within instances can make prop and state propagation harder to reason about and maintain. Plain objects, on the other hand, are inherently immutable (when used thoughtfully) and can be easily passed as props, simplifying data flow and reducing unexpected side effects.
|
||||
- Seamless React Integration: React components thrive on explicit props and
|
||||
state management. Classes' tendency to store internal state directly within
|
||||
instances can make prop and state propagation harder to reason about and
|
||||
maintain. Plain objects, on the other hand, are inherently immutable (when
|
||||
used thoughtfully) and can be easily passed as props, simplifying data flow
|
||||
and reducing unexpected side effects.
|
||||
|
||||
- Reduced Boilerplate and Increased Conciseness: Classes often promote the use of constructors, this binding, getters, setters, and other boilerplate that can unnecessarily bloat code. TypeScript interface and type declarations provide powerful static type checking without the runtime overhead or verbosity of class definitions. This allows for more succinct and readable code, aligning with JavaScript's strengths in functional programming.
|
||||
- Reduced Boilerplate and Increased Conciseness: Classes often promote the use
|
||||
of constructors, this binding, getters, setters, and other boilerplate that
|
||||
can unnecessarily bloat code. TypeScript interface and type declarations
|
||||
provide powerful static type checking without the runtime overhead or
|
||||
verbosity of class definitions. This allows for more succinct and readable
|
||||
code, aligning with JavaScript's strengths in functional programming.
|
||||
|
||||
- Enhanced Readability and Predictability: Plain objects, especially when their structure is clearly defined by TypeScript interfaces, are often easier to read and understand. Their properties are directly accessible, and there's no hidden internal state or complex inheritance chains to navigate. This predictability leads to fewer bugs and a more maintainable codebase.
|
||||
- Enhanced Readability and Predictability: Plain objects, especially when their
|
||||
structure is clearly defined by TypeScript interfaces, are often easier to
|
||||
read and understand. Their properties are directly accessible, and there's no
|
||||
hidden internal state or complex inheritance chains to navigate. This
|
||||
predictability leads to fewer bugs and a more maintainable codebase.
|
||||
|
||||
- Simplified Immutability: While not strictly enforced, plain objects encourage an immutable approach to data. When you need to modify an object, you typically create a new one with the desired changes, rather than mutating the original. This pattern aligns perfectly with React's reconciliation process and helps prevent subtle bugs related to shared mutable state.
|
||||
- Simplified Immutability: While not strictly enforced, plain objects encourage
|
||||
an immutable approach to data. When you need to modify an object, you
|
||||
typically create a new one with the desired changes, rather than mutating the
|
||||
original. This pattern aligns perfectly with React's reconciliation process
|
||||
and helps prevent subtle bugs related to shared mutable state.
|
||||
|
||||
- Better Serialization and Deserialization: Plain JavaScript objects are naturally easy to serialize to JSON and deserialize back, which is a common requirement in web development (e.g., for API communication or local storage). Classes, with their methods and prototypes, can complicate this process.
|
||||
- Better Serialization and Deserialization: Plain JavaScript objects are
|
||||
naturally easy to serialize to JSON and deserialize back, which is a common
|
||||
requirement in web development (e.g., for API communication or local storage).
|
||||
Classes, with their methods and prototypes, can complicate this process.
|
||||
|
||||
### Embracing ES Module Syntax for Encapsulation
|
||||
|
||||
Rather than relying on Java-esque private or public class members, which can be verbose and sometimes limit flexibility, we strongly prefer leveraging ES module syntax (`import`/`export`) for encapsulating private and public APIs.
|
||||
Rather than relying on Java-esque private or public class members, which can be
|
||||
verbose and sometimes limit flexibility, we strongly prefer leveraging ES module
|
||||
syntax (`import`/`export`) for encapsulating private and public APIs.
|
||||
|
||||
- Clearer Public API Definition: With ES modules, anything that is exported is part of the public API of that module, while anything not exported is inherently private to that module. This provides a very clear and explicit way to define what parts of your code are meant to be consumed by other modules.
|
||||
- Clearer Public API Definition: With ES modules, anything that is exported is
|
||||
part of the public API of that module, while anything not exported is
|
||||
inherently private to that module. This provides a very clear and explicit way
|
||||
to define what parts of your code are meant to be consumed by other modules.
|
||||
|
||||
- Enhanced Testability (Without Exposing Internals): By default, unexported functions or variables are not accessible from outside the module. This encourages you to test the public API of your modules, rather than their internal implementation details. If you find yourself needing to spy on or stub an unexported function for testing purposes, it's often a "code smell" indicating that the function might be a good candidate for extraction into its own separate, testable module with a well-defined public API. This promotes a more robust and maintainable testing strategy.
|
||||
- Enhanced Testability (Without Exposing Internals): By default, unexported
|
||||
functions or variables are not accessible from outside the module. This
|
||||
encourages you to test the public API of your modules, rather than their
|
||||
internal implementation details. If you find yourself needing to spy on or
|
||||
stub an unexported function for testing purposes, it's often a "code smell"
|
||||
indicating that the function might be a good candidate for extraction into its
|
||||
own separate, testable module with a well-defined public API. This promotes a
|
||||
more robust and maintainable testing strategy.
|
||||
|
||||
- Reduced Coupling: Explicitly defined module boundaries through import/export help reduce coupling between different parts of your codebase. This makes it easier to refactor, debug, and understand individual components in isolation.
|
||||
- Reduced Coupling: Explicitly defined module boundaries through import/export
|
||||
help reduce coupling between different parts of your codebase. This makes it
|
||||
easier to refactor, debug, and understand individual components in isolation.
|
||||
|
||||
### Avoiding `any` Types and Type Assertions; Preferring `unknown`
|
||||
|
||||
TypeScript's power lies in its ability to provide static type checking, catching potential errors before your code runs. To fully leverage this, it's crucial to avoid the `any` type and be judicious with type assertions.
|
||||
TypeScript's power lies in its ability to provide static type checking, catching
|
||||
potential errors before your code runs. To fully leverage this, it's crucial to
|
||||
avoid the `any` type and be judicious with type assertions.
|
||||
|
||||
- **The Dangers of `any`**: Using any effectively opts out of TypeScript's type checking for that particular variable or expression. While it might seem convenient in the short term, it introduces significant risks:
|
||||
- **Loss of Type Safety**: You lose all the benefits of type checking, making it easy to introduce runtime errors that TypeScript would otherwise have caught.
|
||||
- **Reduced Readability and Maintainability**: Code with `any` types is harder to understand and maintain, as the expected type of data is no longer explicitly defined.
|
||||
- **Masking Underlying Issues**: Often, the need for any indicates a deeper problem in the design of your code or the way you're interacting with external libraries. It's a sign that you might need to refine your types or refactor your code.
|
||||
- **The Dangers of `any`**: Using any effectively opts out of TypeScript's type
|
||||
checking for that particular variable or expression. While it might seem
|
||||
convenient in the short term, it introduces significant risks:
|
||||
- **Loss of Type Safety**: You lose all the benefits of type checking, making
|
||||
it easy to introduce runtime errors that TypeScript would otherwise have
|
||||
caught.
|
||||
- **Reduced Readability and Maintainability**: Code with `any` types is harder
|
||||
to understand and maintain, as the expected type of data is no longer
|
||||
explicitly defined.
|
||||
- **Masking Underlying Issues**: Often, the need for any indicates a deeper
|
||||
problem in the design of your code or the way you're interacting with
|
||||
external libraries. It's a sign that you might need to refine your types or
|
||||
refactor your code.
|
||||
|
||||
- **Preferring `unknown` over `any`**: When you absolutely cannot determine the type of a value at compile time, and you're tempted to reach for any, consider using unknown instead. unknown is a type-safe counterpart to any. While a variable of type unknown can hold any value, you must perform type narrowing (e.g., using typeof or instanceof checks, or a type assertion) before you can perform any operations on it. This forces you to handle the unknown type explicitly, preventing accidental runtime errors.
|
||||
- **Preferring `unknown` over `any`**: When you absolutely cannot determine the
|
||||
type of a value at compile time, and you're tempted to reach for any, consider
|
||||
using unknown instead. unknown is a type-safe counterpart to any. While a
|
||||
variable of type unknown can hold any value, you must perform type narrowing
|
||||
(e.g., using typeof or instanceof checks, or a type assertion) before you can
|
||||
perform any operations on it. This forces you to handle the unknown type
|
||||
explicitly, preventing accidental runtime errors.
|
||||
|
||||
```ts
|
||||
function processValue(value: unknown) {
|
||||
@@ -111,9 +204,23 @@ TypeScript's power lies in its ability to provide static type checking, catching
|
||||
}
|
||||
```
|
||||
|
||||
- **Type Assertions (`as Type`) - Use with Caution**: Type assertions tell the TypeScript compiler, "Trust me, I know what I'm doing; this is definitely of this type." While there are legitimate use cases (e.g., when dealing with external libraries that don't have perfect type definitions, or when you have more information than the compiler), they should be used sparingly and with extreme caution.
|
||||
- **Bypassing Type Checking**: Like `any`, type assertions bypass TypeScript's safety checks. If your assertion is incorrect, you introduce a runtime error that TypeScript would not have warned you about.
|
||||
- **Code Smell in Testing**: A common scenario where `any` or type assertions might be tempting is when trying to test "private" implementation details (e.g., spying on or stubbing an unexported function within a module). This is a strong indication of a "code smell" in your testing strategy and potentially your code structure. Instead of trying to force access to private internals, consider whether those internal details should be refactored into a separate module with a well-defined public API. This makes them inherently testable without compromising encapsulation.
|
||||
- **Type Assertions (`as Type`) - Use with Caution**: Type assertions tell the
|
||||
TypeScript compiler, "Trust me, I know what I'm doing; this is definitely of
|
||||
this type." While there are legitimate use cases (e.g., when dealing with
|
||||
external libraries that don't have perfect type definitions, or when you have
|
||||
more information than the compiler), they should be used sparingly and with
|
||||
extreme caution.
|
||||
- **Bypassing Type Checking**: Like `any`, type assertions bypass TypeScript's
|
||||
safety checks. If your assertion is incorrect, you introduce a runtime error
|
||||
that TypeScript would not have warned you about.
|
||||
- **Code Smell in Testing**: A common scenario where `any` or type assertions
|
||||
might be tempting is when trying to test "private" implementation details
|
||||
(e.g., spying on or stubbing an unexported function within a module). This
|
||||
is a strong indication of a "code smell" in your testing strategy and
|
||||
potentially your code structure. Instead of trying to force access to
|
||||
private internals, consider whether those internal details should be
|
||||
refactored into a separate module with a well-defined public API. This makes
|
||||
them inherently testable without compromising encapsulation.
|
||||
|
||||
### Type narrowing `switch` clauses
|
||||
|
||||
@@ -125,54 +232,137 @@ This helper method can be found in `packages/cli/src/utils/checks.ts`
|
||||
|
||||
### Embracing JavaScript's Array Operators
|
||||
|
||||
To further enhance code cleanliness and promote safe functional programming practices, leverage JavaScript's rich set of array operators as much as possible. Methods like `.map()`, `.filter()`, `.reduce()`, `.slice()`, `.sort()`, and others are incredibly powerful for transforming and manipulating data collections in an immutable and declarative way.
|
||||
To further enhance code cleanliness and promote safe functional programming
|
||||
practices, leverage JavaScript's rich set of array operators as much as
|
||||
possible. Methods like `.map()`, `.filter()`, `.reduce()`, `.slice()`,
|
||||
`.sort()`, and others are incredibly powerful for transforming and manipulating
|
||||
data collections in an immutable and declarative way.
|
||||
|
||||
Using these operators:
|
||||
|
||||
- Promotes Immutability: Most array operators return new arrays, leaving the original array untouched. This functional approach helps prevent unintended side effects and makes your code more predictable.
|
||||
- Improves Readability: Chaining array operators often lead to more concise and expressive code than traditional for loops or imperative logic. The intent of the operation is clear at a glance.
|
||||
- Facilitates Functional Programming: These operators are cornerstones of functional programming, encouraging the creation of pure functions that take inputs and produce outputs without causing side effects. This paradigm is highly beneficial for writing robust and testable code that pairs well with React.
|
||||
- Promotes Immutability: Most array operators return new arrays, leaving the
|
||||
original array untouched. This functional approach helps prevent unintended
|
||||
side effects and makes your code more predictable.
|
||||
- Improves Readability: Chaining array operators often lead to more concise and
|
||||
expressive code than traditional for loops or imperative logic. The intent of
|
||||
the operation is clear at a glance.
|
||||
- Facilitates Functional Programming: These operators are cornerstones of
|
||||
functional programming, encouraging the creation of pure functions that take
|
||||
inputs and produce outputs without causing side effects. This paradigm is
|
||||
highly beneficial for writing robust and testable code that pairs well with
|
||||
React.
|
||||
|
||||
By consistently applying these principles, we can maintain a codebase that is not only efficient and performant but also a joy to work with, both now and in the future.
|
||||
By consistently applying these principles, we can maintain a codebase that is
|
||||
not only efficient and performant but also a joy to work with, both now and in
|
||||
the future.
|
||||
|
||||
## React (mirrored and adjusted from [react-mcp-server](https://github.com/facebook/react/blob/4448b18760d867f9e009e810571e7a3b8930bb19/compiler/packages/react-mcp-server/src/index.ts#L376C1-L441C94))
|
||||
|
||||
### Role
|
||||
|
||||
You are a React assistant that helps users write more efficient and optimizable React code. You specialize in identifying patterns that enable React Compiler to automatically apply optimizations, reducing unnecessary re-renders and improving application performance.
|
||||
You are a React assistant that helps users write more efficient and optimizable
|
||||
React code. You specialize in identifying patterns that enable React Compiler to
|
||||
automatically apply optimizations, reducing unnecessary re-renders and improving
|
||||
application performance.
|
||||
|
||||
### Follow these guidelines in all code you produce and suggest
|
||||
|
||||
Use functional components with Hooks: Do not generate class components or use old lifecycle methods. Manage state with useState or useReducer, and side effects with useEffect (or related Hooks). Always prefer functions and Hooks for any new component logic.
|
||||
Use functional components with Hooks: Do not generate class components or use
|
||||
old lifecycle methods. Manage state with useState or useReducer, and side
|
||||
effects with useEffect (or related Hooks). Always prefer functions and Hooks for
|
||||
any new component logic.
|
||||
|
||||
Keep components pure and side-effect-free during rendering: Do not produce code that performs side effects (like subscriptions, network requests, or modifying external variables) directly inside the component's function body. Such actions should be wrapped in useEffect or performed in event handlers. Ensure your render logic is a pure function of props and state.
|
||||
Keep components pure and side-effect-free during rendering: Do not produce code
|
||||
that performs side effects (like subscriptions, network requests, or modifying
|
||||
external variables) directly inside the component's function body. Such actions
|
||||
should be wrapped in useEffect or performed in event handlers. Ensure your
|
||||
render logic is a pure function of props and state.
|
||||
|
||||
Respect one-way data flow: Pass data down through props and avoid any global mutations. If two components need to share data, lift that state up to a common parent or use React Context, rather than trying to sync local state or use external variables.
|
||||
Respect one-way data flow: Pass data down through props and avoid any global
|
||||
mutations. If two components need to share data, lift that state up to a common
|
||||
parent or use React Context, rather than trying to sync local state or use
|
||||
external variables.
|
||||
|
||||
Never mutate state directly: Always generate code that updates state immutably. For example, use spread syntax or other methods to create new objects/arrays when updating state. Do not use assignments like state.someValue = ... or array mutations like array.push() on state variables. Use the state setter (setState from useState, etc.) to update state.
|
||||
Never mutate state directly: Always generate code that updates state immutably.
|
||||
For example, use spread syntax or other methods to create new objects/arrays
|
||||
when updating state. Do not use assignments like state.someValue = ... or array
|
||||
mutations like array.push() on state variables. Use the state setter (setState
|
||||
from useState, etc.) to update state.
|
||||
|
||||
Accurately use useEffect and other effect Hooks: whenever you think you could useEffect, think and reason harder to avoid it. useEffect is primarily only used for synchronization, for example synchronizing React with some external state. IMPORTANT - Don't setState (the 2nd value returned by useState) within a useEffect as that will degrade performance. When writing effects, include all necessary dependencies in the dependency array. Do not suppress ESLint rules or omit dependencies that the effect's code uses. Structure the effect callbacks to handle changing values properly (e.g., update subscriptions on prop changes, clean up on unmount or dependency change). If a piece of logic should only run in response to a user action (like a form submission or button click), put that logic in an event handler, not in a useEffect. Where possible, useEffects should return a cleanup function.
|
||||
Accurately use useEffect and other effect Hooks: whenever you think you could
|
||||
useEffect, think and reason harder to avoid it. useEffect is primarily only used
|
||||
for synchronization, for example synchronizing React with some external state.
|
||||
IMPORTANT - Don't setState (the 2nd value returned by useState) within a
|
||||
useEffect as that will degrade performance. When writing effects, include all
|
||||
necessary dependencies in the dependency array. Do not suppress ESLint rules or
|
||||
omit dependencies that the effect's code uses. Structure the effect callbacks to
|
||||
handle changing values properly (e.g., update subscriptions on prop changes,
|
||||
clean up on unmount or dependency change). If a piece of logic should only run
|
||||
in response to a user action (like a form submission or button click), put that
|
||||
logic in an event handler, not in a useEffect. Where possible, useEffects should
|
||||
return a cleanup function.
|
||||
|
||||
Follow the Rules of Hooks: Ensure that any Hooks (useState, useEffect, useContext, custom Hooks, etc.) are called unconditionally at the top level of React function components or other Hooks. Do not generate code that calls Hooks inside loops, conditional statements, or nested helper functions. Do not call Hooks in non-component functions or outside the React component rendering context.
|
||||
Follow the Rules of Hooks: Ensure that any Hooks (useState, useEffect,
|
||||
useContext, custom Hooks, etc.) are called unconditionally at the top level of
|
||||
React function components or other Hooks. Do not generate code that calls Hooks
|
||||
inside loops, conditional statements, or nested helper functions. Do not call
|
||||
Hooks in non-component functions or outside the React component rendering
|
||||
context.
|
||||
|
||||
Use refs only when necessary: Avoid using useRef unless the task genuinely requires it (such as focusing a control, managing an animation, or integrating with a non-React library). Do not use refs to store application state that should be reactive. If you do use refs, never write to or read from ref.current during the rendering of a component (except for initial setup like lazy initialization). Any ref usage should not affect the rendered output directly.
|
||||
Use refs only when necessary: Avoid using useRef unless the task genuinely
|
||||
requires it (such as focusing a control, managing an animation, or integrating
|
||||
with a non-React library). Do not use refs to store application state that
|
||||
should be reactive. If you do use refs, never write to or read from ref.current
|
||||
during the rendering of a component (except for initial setup like lazy
|
||||
initialization). Any ref usage should not affect the rendered output directly.
|
||||
|
||||
Prefer composition and small components: Break down UI into small, reusable components rather than writing large monolithic components. The code you generate should promote clarity and reusability by composing components together. Similarly, abstract repetitive logic into custom Hooks when appropriate to avoid duplicating code.
|
||||
Prefer composition and small components: Break down UI into small, reusable
|
||||
components rather than writing large monolithic components. The code you
|
||||
generate should promote clarity and reusability by composing components
|
||||
together. Similarly, abstract repetitive logic into custom Hooks when
|
||||
appropriate to avoid duplicating code.
|
||||
|
||||
Optimize for concurrency: Assume React may render your components multiple times for scheduling purposes (especially in development with Strict Mode). Write code that remains correct even if the component function runs more than once. For instance, avoid side effects in the component body and use functional state updates (e.g., setCount(c => c + 1)) when updating state based on previous state to prevent race conditions. Always include cleanup functions in effects that subscribe to external resources. Don't write useEffects for "do this when this changes" side effects. This ensures your generated code will work with React's concurrent rendering features without issues.
|
||||
Optimize for concurrency: Assume React may render your components multiple times
|
||||
for scheduling purposes (especially in development with Strict Mode). Write code
|
||||
that remains correct even if the component function runs more than once. For
|
||||
instance, avoid side effects in the component body and use functional state
|
||||
updates (e.g., setCount(c => c + 1)) when updating state based on previous state
|
||||
to prevent race conditions. Always include cleanup functions in effects that
|
||||
subscribe to external resources. Don't write useEffects for "do this when this
|
||||
changes" side effects. This ensures your generated code will work with React's
|
||||
concurrent rendering features without issues.
|
||||
|
||||
Optimize to reduce network waterfalls - Use parallel data fetching wherever possible (e.g., start multiple requests at once rather than one after another). Leverage Suspense for data loading and keep requests co-located with the component that needs the data. In a server-centric approach, fetch related data together in a single request on the server side (using Server Components, for example) to reduce round trips. Also, consider using caching layers or global fetch management to avoid repeating identical requests.
|
||||
Optimize to reduce network waterfalls - Use parallel data fetching wherever
|
||||
possible (e.g., start multiple requests at once rather than one after another).
|
||||
Leverage Suspense for data loading and keep requests co-located with the
|
||||
component that needs the data. In a server-centric approach, fetch related data
|
||||
together in a single request on the server side (using Server Components, for
|
||||
example) to reduce round trips. Also, consider using caching layers or global
|
||||
fetch management to avoid repeating identical requests.
|
||||
|
||||
Rely on React Compiler - useMemo, useCallback, and React.memo can be omitted if React Compiler is enabled. Avoid premature optimization with manual memoization. Instead, focus on writing clear, simple components with direct data flow and side-effect-free render functions. Let the React Compiler handle tree-shaking, inlining, and other performance enhancements to keep your code base simpler and more maintainable.
|
||||
Rely on React Compiler - useMemo, useCallback, and React.memo can be omitted if
|
||||
React Compiler is enabled. Avoid premature optimization with manual memoization.
|
||||
Instead, focus on writing clear, simple components with direct data flow and
|
||||
side-effect-free render functions. Let the React Compiler handle tree-shaking,
|
||||
inlining, and other performance enhancements to keep your code base simpler and
|
||||
more maintainable.
|
||||
|
||||
Design for a good user experience - Provide clear, minimal, and non-blocking UI states. When data is loading, show lightweight placeholders (e.g., skeleton screens) rather than intrusive spinners everywhere. Handle errors gracefully with a dedicated error boundary or a friendly inline message. Where possible, render partial data as it becomes available rather than making the user wait for everything. Suspense allows you to declare the loading states in your component tree in a natural way, preventing “flash” states and improving perceived performance.
|
||||
Design for a good user experience - Provide clear, minimal, and non-blocking UI
|
||||
states. When data is loading, show lightweight placeholders (e.g., skeleton
|
||||
screens) rather than intrusive spinners everywhere. Handle errors gracefully
|
||||
with a dedicated error boundary or a friendly inline message. Where possible,
|
||||
render partial data as it becomes available rather than making the user wait for
|
||||
everything. Suspense allows you to declare the loading states in your component
|
||||
tree in a natural way, preventing “flash” states and improving perceived
|
||||
performance.
|
||||
|
||||
### Process
|
||||
|
||||
1. Analyze the user's code for optimization opportunities:
|
||||
- Check for React anti-patterns that prevent compiler optimization
|
||||
- Look for component structure issues that limit compiler effectiveness
|
||||
- Think about each suggestion you are making and consult React docs for best practices
|
||||
- Think about each suggestion you are making and consult React docs for best
|
||||
practices
|
||||
|
||||
2. Provide actionable guidance:
|
||||
- Explain specific code changes with clear reasoning
|
||||
@@ -184,10 +374,47 @@ Design for a good user experience - Provide clear, minimal, and non-blocking UI
|
||||
- State updates should be structured to enable granular updates
|
||||
- Side effects should be isolated and dependencies clearly defined
|
||||
|
||||
## Documentation guidelines
|
||||
|
||||
When working in the `/docs` directory, follow the guidelines in this section:
|
||||
|
||||
- **Role:** You are an expert technical writer and AI assistant for contributors
|
||||
to Gemini CLI. Produce professional, accurate, and consistent documentation to
|
||||
guide users of Gemini CLI.
|
||||
- **Technical Accuracy:** Do not invent facts, commands, code, API names, or
|
||||
output. All technical information specific to Gemini CLI must be based on code
|
||||
found within this directory and its subdirectories.
|
||||
- **Style Authority:** Your source for writing guidance and style is the
|
||||
"Documentation contribution process" section in the root directory's
|
||||
`CONTRIBUTING.md` file, as well as any guidelines provided this section.
|
||||
- **Information Architecture Consideration:** Before proposing documentation
|
||||
changes, consider the information architecture. If a change adds significant
|
||||
new content to existing documents, evaluate if creating a new, more focused
|
||||
page or changes to `sidebar.json` would provide a better user experience.
|
||||
- **Proactive User Consideration:** The user experience should be a primary
|
||||
concern when making changes to documentation. Aim to fill gaps in existing
|
||||
knowledge whenever possible while keeping documentation concise and easy for
|
||||
users to understand. If changes might hinder user understanding or
|
||||
accessibility, proactively raise these concerns and propose alternatives.
|
||||
|
||||
## Comments policy
|
||||
|
||||
Only write high-value comments if at all. Avoid talking to the user through comments.
|
||||
Only write high-value comments if at all. Avoid talking to the user through
|
||||
comments.
|
||||
|
||||
## General style requirements
|
||||
## Logging and Error Handling
|
||||
|
||||
Use hyphens instead of underscores in flag names (e.g. `my-flag` instead of `my_flag`).
|
||||
- **Avoid Console Statements:** Do not use `console.log`, `console.error`, or
|
||||
similar methods directly.
|
||||
- **Non-User-Facing Logs:** For developer-facing debug messages, use
|
||||
`debugLogger` (from `@google/gemini-cli-core`).
|
||||
- **User-Facing Feedback:** To surface errors or warnings to the user, use
|
||||
`coreEvents.emitFeedback` (from `@google/gemini-cli-core`).
|
||||
|
||||
## General requirements
|
||||
|
||||
- If there is something you do not understand or is ambiguous, seek confirmation
|
||||
or clarification from the user before making changes based on assumptions.
|
||||
- Use hyphens instead of underscores in flag names (e.g. `my-flag` instead of
|
||||
`my_flag`).
|
||||
- Always refer to Gemini CLI as `Gemini CLI`, never `the Gemini CLI`.
|
||||
|
||||
@@ -1,32 +1,46 @@
|
||||
# Gemini CLI
|
||||
|
||||
[](https://github.com/google-gemini/gemini-cli/actions/workflows/ci.yml)
|
||||
[](https://github.com/google-gemini/gemini-cli/actions/workflows/e2e.yml)
|
||||
[](https://github.com/google-gemini/gemini-cli/actions/workflows/chained_e2e.yml)
|
||||
[](https://www.npmjs.com/package/@google/gemini-cli)
|
||||
[](https://github.com/google-gemini/gemini-cli/blob/main/LICENSE)
|
||||
[](https://codewiki.google/github.com/google-gemini/gemini-cli)
|
||||
|
||||

|
||||
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly into your terminal. It provides lightweight access to Gemini, giving you the most direct path from your prompt to our model.
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
|
||||
into your terminal. It provides lightweight access to Gemini, giving you the
|
||||
most direct path from your prompt to our model.
|
||||
|
||||
Learn all about Gemini CLI in our [documentation](https://geminicli.com/docs/).
|
||||
|
||||
## 🚀 Why Gemini CLI?
|
||||
|
||||
- **🎯 Free tier**: 60 requests/min and 1,000 requests/day with personal Google account
|
||||
- **🧠 Powerful Gemini 2.5 Pro**: Access to 1M token context window
|
||||
- **🔧 Built-in tools**: Google Search grounding, file operations, shell commands, web fetching
|
||||
- **🔌 Extensible**: MCP (Model Context Protocol) support for custom integrations
|
||||
- **💻 Terminal-first**: Designed for developers who live in the command line
|
||||
- **🛡️ Open source**: Apache 2.0 licensed
|
||||
- **🎯 Free tier**: 60 requests/min and 1,000 requests/day with personal Google
|
||||
account.
|
||||
- **🧠 Powerful Gemini 3 models**: Access to improved reasoning and 1M token
|
||||
context window.
|
||||
- **🔧 Built-in tools**: Google Search grounding, file operations, shell
|
||||
commands, web fetching.
|
||||
- **🔌 Extensible**: MCP (Model Context Protocol) support for custom
|
||||
integrations.
|
||||
- **💻 Terminal-first**: Designed for developers who live in the command line.
|
||||
- **🛡️ Open source**: Apache 2.0 licensed.
|
||||
|
||||
## 📦 Installation
|
||||
|
||||
### Pre-requisites before installation
|
||||
|
||||
- Node.js version 20 or higher
|
||||
- macOS, Linux, or Windows
|
||||
|
||||
### Quick Install
|
||||
|
||||
#### Run instantly with npx
|
||||
|
||||
```bash
|
||||
# Using npx (no installation required)
|
||||
npx https://github.com/google-gemini/gemini-cli
|
||||
npx @google/gemini-cli
|
||||
```
|
||||
|
||||
#### Install globally with npm
|
||||
@@ -41,18 +55,15 @@ npm install -g @google/gemini-cli
|
||||
brew install gemini-cli
|
||||
```
|
||||
|
||||
#### System Requirements
|
||||
|
||||
- Node.js version 20 or higher
|
||||
- macOS, Linux, or Windows
|
||||
|
||||
## Release Cadence and Tags
|
||||
|
||||
See [Releases](./docs/releases.md) for more details.
|
||||
|
||||
### Preview
|
||||
|
||||
New preview releases will be published each week at UTC 2359 on Tuesdays. These releases will not have been fully vetted and may contain regressions or other outstanding issues. Please help us test and install with `preview` tag.
|
||||
New preview releases will be published each week at UTC 2359 on Tuesdays. These
|
||||
releases will not have been fully vetted and may contain regressions or other
|
||||
outstanding issues. Please help us test and install with `preview` tag.
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@preview
|
||||
@@ -60,7 +71,9 @@ npm install -g @google/gemini-cli@preview
|
||||
|
||||
### Stable
|
||||
|
||||
- New stable releases will be published each week at UTC 2000 on Tuesdays, this will be the full promotion of last week's `preview` release + any bug fixes and validations. Use `latest` tag.
|
||||
- New stable releases will be published each week at UTC 2000 on Tuesdays, this
|
||||
will be the full promotion of last week's `preview` release + any bug fixes
|
||||
and validations. Use `latest` tag.
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@latest
|
||||
@@ -68,7 +81,9 @@ npm install -g @google/gemini-cli@latest
|
||||
|
||||
### Nightly
|
||||
|
||||
- New releases will be published each week at UTC 0000 each day, This will be all changes from the main branch as represented at time of release. It should be assumed there are pending validations and issues. Use `nightly` tag.
|
||||
- New releases will be published each day at UTC 0000. This will be all changes
|
||||
from the main branch as represented at time of release. It should be assumed
|
||||
there are pending validations and issues. Use `nightly` tag.
|
||||
|
||||
```bash
|
||||
npm install -g @google/gemini-cli@nightly
|
||||
@@ -84,24 +99,33 @@ npm install -g @google/gemini-cli@nightly
|
||||
|
||||
### Automation & Integration
|
||||
|
||||
- Automate operational tasks like querying pull requests or handling complex rebases
|
||||
- Use MCP servers to connect new capabilities, including [media generation with Imagen, Veo or Lyria](https://github.com/GoogleCloudPlatform/vertex-ai-creative-studio/tree/main/experiments/mcp-genmedia)
|
||||
- Automate operational tasks like querying pull requests or handling complex
|
||||
rebases
|
||||
- Use MCP servers to connect new capabilities, including
|
||||
[media generation with Imagen, Veo or Lyria](https://github.com/GoogleCloudPlatform/vertex-ai-creative-studio/tree/main/experiments/mcp-genmedia)
|
||||
- Run non-interactively in scripts for workflow automation
|
||||
|
||||
### Advanced Capabilities
|
||||
|
||||
- Ground your queries with built-in [Google Search](https://ai.google.dev/gemini-api/docs/grounding) for real-time information
|
||||
- Ground your queries with built-in
|
||||
[Google Search](https://ai.google.dev/gemini-api/docs/grounding) for real-time
|
||||
information
|
||||
- Conversation checkpointing to save and resume complex sessions
|
||||
- Custom context files (GEMINI.md) to tailor behavior for your projects
|
||||
|
||||
### GitHub Integration
|
||||
|
||||
Integrate Gemini CLI directly into your GitHub workflows with [**Gemini CLI GitHub Action**](https://github.com/google-github-actions/run-gemini-cli):
|
||||
Integrate Gemini CLI directly into your GitHub workflows with
|
||||
[**Gemini CLI GitHub Action**](https://github.com/google-github-actions/run-gemini-cli):
|
||||
|
||||
- **Pull Request Reviews**: Automated code review with contextual feedback and suggestions
|
||||
- **Issue Triage**: Automated labeling and prioritization of GitHub issues based on content analysis
|
||||
- **On-demand Assistance**: Mention `@gemini-cli` in issues and pull requests for help with debugging, explanations, or task delegation
|
||||
- **Custom Workflows**: Build automated, scheduled and on-demand workflows tailored to your team's needs
|
||||
- **Pull Request Reviews**: Automated code review with contextual feedback and
|
||||
suggestions
|
||||
- **Issue Triage**: Automated labeling and prioritization of GitHub issues based
|
||||
on content analysis
|
||||
- **On-demand Assistance**: Mention `@gemini-cli` in issues and pull requests
|
||||
for help with debugging, explanations, or task delegation
|
||||
- **Custom Workflows**: Build automated, scheduled and on-demand workflows
|
||||
tailored to your team's needs
|
||||
|
||||
## 🔐 Authentication Options
|
||||
|
||||
@@ -109,12 +133,15 @@ Choose the authentication method that best fits your needs:
|
||||
|
||||
### Option 1: Login with Google (OAuth login using your Google Account)
|
||||
|
||||
**✨ Best for:** Individual developers as well as anyone who has a Gemini Code Assist License. (see [quota limits and terms of service](https://cloud.google.com/gemini/docs/quotas) for details)
|
||||
**✨ Best for:** Individual developers as well as anyone who has a Gemini Code
|
||||
Assist License. (see
|
||||
[quota limits and terms of service](https://cloud.google.com/gemini/docs/quotas)
|
||||
for details)
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Free tier**: 60 requests/min and 1,000 requests/day
|
||||
- **Gemini 2.5 Pro** with 1M token context window
|
||||
- **Gemini 3 models** with 1M token context window
|
||||
- **No API key management** - just sign in with your Google account
|
||||
- **Automatic updates** to latest models
|
||||
|
||||
@@ -128,7 +155,7 @@ gemini
|
||||
|
||||
```bash
|
||||
# Set your Google Cloud Project
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_NAME"
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
gemini
|
||||
```
|
||||
|
||||
@@ -138,7 +165,7 @@ gemini
|
||||
|
||||
**Benefits:**
|
||||
|
||||
- **Free tier**: 100 requests/day with Gemini 2.5 Pro
|
||||
- **Free tier**: 1000 requests/day with Gemini 3 (mix of flash and pro)
|
||||
- **Model selection**: Choose specific Gemini models
|
||||
- **Usage-based billing**: Upgrade for higher limits when needed
|
||||
|
||||
@@ -165,7 +192,8 @@ export GOOGLE_GENAI_USE_VERTEXAI=true
|
||||
gemini
|
||||
```
|
||||
|
||||
For Google Workspace accounts and other authentication methods, see the [authentication guide](./docs/cli/authentication.md).
|
||||
For Google Workspace accounts and other authentication methods, see the
|
||||
[authentication guide](./docs/get-started/authentication.md).
|
||||
|
||||
## 🚀 Getting Started
|
||||
|
||||
@@ -204,6 +232,13 @@ the `--output-format json` flag to get structured output:
|
||||
gemini -p "Explain the architecture of this codebase" --output-format json
|
||||
```
|
||||
|
||||
For real-time event streaming (useful for monitoring long-running operations),
|
||||
use `--output-format stream-json` to get newline-delimited JSON events:
|
||||
|
||||
```bash
|
||||
gemini -p "Run tests and deploy" --output-format stream-json
|
||||
```
|
||||
|
||||
### Quick Examples
|
||||
|
||||
#### Start a new project
|
||||
@@ -227,17 +262,26 @@ gemini
|
||||
|
||||
### Getting Started
|
||||
|
||||
- [**Quickstart Guide**](./docs/cli/index.md) - Get up and running quickly
|
||||
- [**Authentication Setup**](./docs/cli/authentication.md) - Detailed auth configuration
|
||||
- [**Configuration Guide**](./docs/cli/configuration.md) - Settings and customization
|
||||
- [**Keyboard Shortcuts**](./docs/keyboard-shortcuts.md) - Productivity tips
|
||||
- [**Quickstart Guide**](./docs/get-started/index.md) - Get up and running
|
||||
quickly.
|
||||
- [**Authentication Setup**](./docs/get-started/authentication.md) - Detailed
|
||||
auth configuration.
|
||||
- [**Configuration Guide**](./docs/get-started/configuration.md) - Settings and
|
||||
customization.
|
||||
- [**Keyboard Shortcuts**](./docs/cli/keyboard-shortcuts.md) - Productivity
|
||||
tips.
|
||||
|
||||
### Core Features
|
||||
|
||||
- [**Commands Reference**](./docs/cli/commands.md) - All slash commands (`/help`, `/chat`, `/mcp`, etc.)
|
||||
- [**Checkpointing**](./docs/checkpointing.md) - Save and resume conversations
|
||||
- [**Memory Management**](./docs/tools/memory.md) - Using GEMINI.md context files
|
||||
- [**Token Caching**](./docs/cli/token-caching.md) - Optimize token usage
|
||||
- [**Commands Reference**](./docs/cli/commands.md) - All slash commands
|
||||
(`/help`, `/chat`, etc).
|
||||
- [**Custom Commands**](./docs/cli/custom-commands.md) - Create your own
|
||||
reusable commands.
|
||||
- [**Context Files (GEMINI.md)**](./docs/cli/gemini-md.md) - Provide persistent
|
||||
context to Gemini CLI.
|
||||
- [**Checkpointing**](./docs/cli/checkpointing.md) - Save and resume
|
||||
conversations.
|
||||
- [**Token Caching**](./docs/cli/token-caching.md) - Optimize token usage.
|
||||
|
||||
### Tools & Extensions
|
||||
|
||||
@@ -245,35 +289,39 @@ gemini
|
||||
- [File System Operations](./docs/tools/file-system.md)
|
||||
- [Shell Commands](./docs/tools/shell.md)
|
||||
- [Web Fetch & Search](./docs/tools/web-fetch.md)
|
||||
- [Multi-file Operations](./docs/tools/multi-file.md)
|
||||
- [**MCP Server Integration**](./docs/tools/mcp-server.md) - Extend with custom tools
|
||||
- [**Custom Extensions**](./docs/extension.md) - Build your own commands
|
||||
- [**MCP Server Integration**](./docs/tools/mcp-server.md) - Extend with custom
|
||||
tools.
|
||||
- [**Custom Extensions**](./docs/extensions/index.md) - Build and share your own
|
||||
commands.
|
||||
|
||||
### Advanced Topics
|
||||
|
||||
- [**Architecture Overview**](./docs/architecture.md) - How Gemini CLI works
|
||||
- [**IDE Integration**](./docs/ide-integration.md) - VS Code companion
|
||||
- [**Sandboxing & Security**](./docs/sandbox.md) - Safe execution environments
|
||||
- [**Enterprise Deployment**](./docs/deployment.md) - Docker, system-wide config
|
||||
- [**Telemetry & Monitoring**](./docs/telemetry.md) - Usage tracking
|
||||
- [**Tools API Development**](./docs/core/tools-api.md) - Create custom tools
|
||||
|
||||
### Configuration & Customization
|
||||
|
||||
- [**Settings Reference**](./docs/cli/configuration.md) - All configuration options
|
||||
- [**Theme Customization**](./docs/cli/themes.md) - Visual customization
|
||||
- [**.gemini Directory**](./docs/gemini-ignore.md) - Project-specific settings
|
||||
- [**Environment Variables**](./docs/cli/configuration.md#environment-variables)
|
||||
- [**Headless Mode (Scripting)**](./docs/cli/headless.md) - Use Gemini CLI in
|
||||
automated workflows.
|
||||
- [**Architecture Overview**](./docs/architecture.md) - How Gemini CLI works.
|
||||
- [**IDE Integration**](./docs/ide-integration/index.md) - VS Code companion.
|
||||
- [**Sandboxing & Security**](./docs/cli/sandbox.md) - Safe execution
|
||||
environments.
|
||||
- [**Trusted Folders**](./docs/cli/trusted-folders.md) - Control execution
|
||||
policies by folder.
|
||||
- [**Enterprise Guide**](./docs/cli/enterprise.md) - Deploy and manage in a
|
||||
corporate environment.
|
||||
- [**Telemetry & Monitoring**](./docs/cli/telemetry.md) - Usage tracking.
|
||||
- [**Tools API Development**](./docs/core/tools-api.md) - Create custom tools.
|
||||
- [**Local development**](./docs/local-development.md) - Local development
|
||||
tooling.
|
||||
|
||||
### Troubleshooting & Support
|
||||
|
||||
- [**Troubleshooting Guide**](./docs/troubleshooting.md) - Common issues and solutions
|
||||
- [**FAQ**](./docs/troubleshooting.md#frequently-asked-questions) - Quick answers
|
||||
- Use `/bug` command to report issues directly from the CLI
|
||||
- [**Troubleshooting Guide**](./docs/troubleshooting.md) - Common issues and
|
||||
solutions.
|
||||
- [**FAQ**](./docs/faq.md) - Frequently asked questions.
|
||||
- Use `/bug` command to report issues directly from the CLI.
|
||||
|
||||
### Using MCP Servers
|
||||
|
||||
Configure MCP servers in `~/.gemini/settings.json` to extend Gemini CLI with custom tools:
|
||||
Configure MCP servers in `~/.gemini/settings.json` to extend Gemini CLI with
|
||||
custom tools:
|
||||
|
||||
```text
|
||||
> @github List my open pull requests
|
||||
@@ -281,31 +329,39 @@ Configure MCP servers in `~/.gemini/settings.json` to extend Gemini CLI with cus
|
||||
> @database Run a query to find inactive users
|
||||
```
|
||||
|
||||
See the [MCP Server Integration guide](./docs/tools/mcp-server.md) for setup instructions.
|
||||
See the [MCP Server Integration guide](./docs/tools/mcp-server.md) for setup
|
||||
instructions.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
We welcome contributions! Gemini CLI is fully open source (Apache 2.0), and we encourage the community to:
|
||||
We welcome contributions! Gemini CLI is fully open source (Apache 2.0), and we
|
||||
encourage the community to:
|
||||
|
||||
- Report bugs and suggest features
|
||||
- Improve documentation
|
||||
- Submit code improvements
|
||||
- Share your MCP servers and extensions
|
||||
- Report bugs and suggest features.
|
||||
- Improve documentation.
|
||||
- Submit code improvements.
|
||||
- Share your MCP servers and extensions.
|
||||
|
||||
See our [Contributing Guide](./CONTRIBUTING.md) for development setup, coding standards, and how to submit pull requests.
|
||||
See our [Contributing Guide](./CONTRIBUTING.md) for development setup, coding
|
||||
standards, and how to submit pull requests.
|
||||
|
||||
Check our [Official Roadmap](https://github.com/orgs/google-gemini/projects/11/) for planned features and priorities.
|
||||
Check our [Official Roadmap](https://github.com/orgs/google-gemini/projects/11)
|
||||
for planned features and priorities.
|
||||
|
||||
## 📖 Resources
|
||||
|
||||
- **[Official Roadmap](./ROADMAP.md)** - See what's coming next
|
||||
- **[NPM Package](https://www.npmjs.com/package/@google/gemini-cli)** - Package registry
|
||||
- **[GitHub Issues](https://github.com/google-gemini/gemini-cli/issues)** - Report bugs or request features
|
||||
- **[Security Advisories](https://github.com/google-gemini/gemini-cli/security/advisories)** - Security updates
|
||||
- **[Official Roadmap](./ROADMAP.md)** - See what's coming next.
|
||||
- **[Changelog](./docs/changelogs/index.md)** - See recent notable updates.
|
||||
- **[NPM Package](https://www.npmjs.com/package/@google/gemini-cli)** - Package
|
||||
registry.
|
||||
- **[GitHub Issues](https://github.com/google-gemini/gemini-cli/issues)** -
|
||||
Report bugs or request features.
|
||||
- **[Security Advisories](https://github.com/google-gemini/gemini-cli/security/advisories)** -
|
||||
Security updates.
|
||||
|
||||
### Uninstall
|
||||
|
||||
See the [Uninstall Guide](docs/Uninstall.md) for removal instructions.
|
||||
See the [Uninstall Guide](docs/cli/uninstall.md) for removal instructions.
|
||||
|
||||
## 📄 Legal
|
||||
|
||||
|
||||
+79
-29
@@ -1,63 +1,113 @@
|
||||
# Gemini CLI Roadmap
|
||||
|
||||
The [Official Gemini CLI Roadmap](https://github.com/orgs/google-gemini/projects/11/)
|
||||
The
|
||||
[Official Gemini CLI Roadmap](https://github.com/orgs/google-gemini/projects/11/)
|
||||
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly into your terminal. It provides lightweight access to Gemini, giving you the most direct path from your prompt to our model.
|
||||
Gemini CLI is an open-source AI agent that brings the power of Gemini directly
|
||||
into your terminal. It provides lightweight access to Gemini, giving you the
|
||||
most direct path from your prompt to our model.
|
||||
|
||||
This document outlines our approach to the Gemini CLI roadmap. Here, you'll find our guiding principles and a breakdown of the key areas we are
|
||||
focused on for development. Our roadmap is not a static list but a dynamic set of priorities that are tracked live in our GitHub Issues.
|
||||
This document outlines our approach to the Gemini CLI roadmap. Here, you'll find
|
||||
our guiding principles and a breakdown of the key areas we are focused on for
|
||||
development. Our roadmap is not a static list but a dynamic set of priorities
|
||||
that are tracked live in our GitHub Issues.
|
||||
|
||||
As an [Apache 2.0 open source project](https://github.com/google-gemini/gemini-cli?tab=Apache-2.0-1-ov-file#readme), we appreciate and welcome [public contributions](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md), and will give first priority to those contributions aligned with our roadmap. If you want to propose a new feature or change to our roadmap, please start by [opening an issue for discussion](https://github.com/google-gemini/gemini-cli/issues/new/choose).
|
||||
As an
|
||||
[Apache 2.0 open source project](https://github.com/google-gemini/gemini-cli?tab=Apache-2.0-1-ov-file#readme),
|
||||
we appreciate and welcome
|
||||
[public contributions](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md),
|
||||
and will give first priority to those contributions aligned with our roadmap. If
|
||||
you want to propose a new feature or change to our roadmap, please start by
|
||||
[opening an issue for discussion](https://github.com/google-gemini/gemini-cli/issues/new/choose).
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This roadmap represents our current thinking and is for informational purposes only. It is not a commitment or a guarantee of future delivery. The development, release, and timing of any features are subject to change, and we may update the roadmap based on community discussions as well as when our priorities evolve.
|
||||
This roadmap represents our current thinking and is for informational purposes
|
||||
only. It is not a commitment or a guarantee of future delivery. The development,
|
||||
release, and timing of any features are subject to change, and we may update the
|
||||
roadmap based on community discussions as well as when our priorities evolve.
|
||||
|
||||
## Guiding Principles
|
||||
|
||||
Our development is guided by the following principles:
|
||||
|
||||
- **Power & Simplicity:** Deliver access to state-of-the-art Gemini models with an intuitive and easy-to-use lightweight command-line interface.
|
||||
- **Extensibility:** An adaptable agent to help you with a variety of use cases and environments along with the ability to run these agents anywhere.
|
||||
- **Intelligent:** Gemini CLI should be reliably ranked among the best agentic tools as measured by benchmarks like SWE Bench, Terminal Bench, and CSAT.
|
||||
- **Free and Open Source:** Foster a thriving open source community where cost isn’t a barrier to personal use, and PRs get merged quickly. This means resolving and closing issues, pull requests, and discussion posts quickly.
|
||||
- **Power & Simplicity:** Deliver access to state-of-the-art Gemini models with
|
||||
an intuitive and easy-to-use lightweight command-line interface.
|
||||
- **Extensibility:** An adaptable agent to help you with a variety of use cases
|
||||
and environments along with the ability to run these agents anywhere.
|
||||
- **Intelligent:** Gemini CLI should be reliably ranked among the best agentic
|
||||
tools as measured by benchmarks like SWE Bench, Terminal Bench, and CSAT.
|
||||
- **Free and Open Source:** Foster a thriving open source community where cost
|
||||
isn’t a barrier to personal use, and PRs get merged quickly. This means
|
||||
resolving and closing issues, pull requests, and discussion posts quickly.
|
||||
|
||||
## How the Roadmap Works
|
||||
|
||||
Our roadmap is managed directly through GitHub Issues. See our entry point Roadmap Issue [here](https://github.com/google-gemini/gemini-cli/issues/4191). This approach allows for transparency and gives you a direct way to learn more or get involved with any specific initiative. All our roadmap items will be tagged as Type:`Feature` and Label:`maintainer` for features we are actively working on, or Type:`Task` and Label:`maintainer` for a more detailed list of tasks.
|
||||
Our roadmap is managed directly through GitHub Issues. See our entry point
|
||||
Roadmap Issue [here](https://github.com/google-gemini/gemini-cli/issues/4191).
|
||||
This approach allows for transparency and gives you a direct way to learn more
|
||||
or get involved with any specific initiative. All our roadmap items will be
|
||||
tagged as Type:`Feature` and Label:`maintainer` for features we are actively
|
||||
working on, or Type:`Task` and Label:`maintainer` for a more detailed list of
|
||||
tasks.
|
||||
|
||||
Issues are organized to provide key information at a glance:
|
||||
|
||||
- **Target Quarter:** `Milestone` denotes the anticipated delivery timeline.
|
||||
- **Feature Area:** Labels such as `area/model` or `area/tooling` categorize the work.
|
||||
- **Feature Area:** Labels such as `area/model` or `area/tooling` categorize the
|
||||
work.
|
||||
- **Issue Type:** _Workstream_ => _Epics_ => _Features_ => _Tasks|Bugs_
|
||||
|
||||
To see what we're working on, you can filter our issues by these dimensions. See all our items [here](https://github.com/orgs/google-gemini/projects/11/views/19)
|
||||
To see what we're working on, you can filter our issues by these dimensions. See
|
||||
all our items [here](https://github.com/orgs/google-gemini/projects/11/views/19)
|
||||
|
||||
## Focus Areas
|
||||
|
||||
To better organize our efforts, we categorize our work into several key feature areas. These labels are used on our GitHub Issues to help you filter and
|
||||
find initiatives that interest you.
|
||||
To better organize our efforts, we categorize our work into several key feature
|
||||
areas. These labels are used on our GitHub Issues to help you filter and find
|
||||
initiatives that interest you.
|
||||
|
||||
- **Authentication:** Secure user access via API keys, Gemini Code Assist login, etc.
|
||||
- **Model:** Support new Gemini models, multi-modality, local execution, and performance tuning.
|
||||
- **User Experience:** Improve the CLI's usability, performance, interactive features, and documentation.
|
||||
- **Authentication:** Secure user access via API keys, Gemini Code Assist login,
|
||||
etc.
|
||||
- **Model:** Support new Gemini models, multi-modality, local execution, and
|
||||
performance tuning.
|
||||
- **User Experience:** Improve the CLI's usability, performance, interactive
|
||||
features, and documentation.
|
||||
- **Tooling:** Built-in tools and the MCP ecosystem.
|
||||
- **Core:** Core functionality of the CLI
|
||||
- **Extensibility:** Bringing Gemini CLI to other surfaces e.g. GitHub.
|
||||
- **Contribution:** Improve the contribution process via test automation and CI/CD pipeline enhancements.
|
||||
- **Platform:** Manage installation, OS support, and the underlying CLI framework.
|
||||
- **Quality:** Focus on testing, reliability, performance, and overall product quality.
|
||||
- **Background Agents:** Enable long-running, autonomous tasks and proactive assistance.
|
||||
- **Contribution:** Improve the contribution process via test automation and
|
||||
CI/CD pipeline enhancements.
|
||||
- **Platform:** Manage installation, OS support, and the underlying CLI
|
||||
framework.
|
||||
- **Quality:** Focus on testing, reliability, performance, and overall product
|
||||
quality.
|
||||
- **Background Agents:** Enable long-running, autonomous tasks and proactive
|
||||
assistance.
|
||||
- **Security and Privacy:** For all things related to security and privacy
|
||||
|
||||
## How to Contribute
|
||||
|
||||
Gemini CLI is an open-source project, and we welcome contributions from the community! Whether you're a developer, a designer, or just an enthusiastic user you can find our [Community Guidelines here](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md) to learn how to get started. There are many ways to get involved:
|
||||
Gemini CLI is an open-source project, and we welcome contributions from the
|
||||
community! Whether you're a developer, a designer, or just an enthusiastic user
|
||||
you can find our
|
||||
[Community Guidelines here](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md)
|
||||
to learn how to get started. There are many ways to get involved:
|
||||
|
||||
- **Roadmap:** Please review and find areas in our [roadmap](https://github.com/google-gemini/gemini-cli/issues/4191) that you would like to contribute to. Contributions based on this will be easiest to integrate with.
|
||||
- **Report Bugs:** If you find an issue, please create a [bug](https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml) with as much detail as possible. If you believe it is a critical breaking issue preventing direct CLI usage, please tag it as `priority/p0`.
|
||||
- **Suggest Features:** Have a great idea? We'd love to hear it! Open a [feature request](https://github.com/google-gemini/gemini-cli/issues/new?template=feature_request.yml).
|
||||
- **Contribute Code:** Check out our [CONTRIBUTING.md](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md) file for guidelines on how to submit pull requests. We have a list of "good first issues" for new contributors.
|
||||
- **Write Documentation:** Help us improve our documentation, tutorials, and examples.
|
||||
We are excited about the future of Gemini CLI and look forward to building it with you!
|
||||
- **Roadmap:** Please review and find areas in our
|
||||
[roadmap](https://github.com/google-gemini/gemini-cli/issues/4191) that you
|
||||
would like to contribute to. Contributions based on this will be easiest to
|
||||
integrate with.
|
||||
- **Report Bugs:** If you find an issue, please create a
|
||||
[bug](https://github.com/google-gemini/gemini-cli/issues/new?template=bug_report.yml)
|
||||
with as much detail as possible. If you believe it is a critical breaking
|
||||
issue preventing direct CLI usage, please tag it as `priority/p0`.
|
||||
- **Suggest Features:** Have a great idea? We'd love to hear it! Open a
|
||||
[feature request](https://github.com/google-gemini/gemini-cli/issues/new?template=feature_request.yml).
|
||||
- **Contribute Code:** Check out our
|
||||
[CONTRIBUTING.md](https://github.com/google-gemini/gemini-cli/blob/main/CONTRIBUTING.md)
|
||||
file for guidelines on how to submit pull requests. We have a list of "good
|
||||
first issues" for new contributors.
|
||||
- **Write Documentation:** Help us improve our documentation, tutorials, and
|
||||
examples. We are excited about the future of Gemini CLI and look forward to
|
||||
building it with you!
|
||||
|
||||
+2
-1
@@ -5,4 +5,5 @@ We use g.co/vulnz for our intake, and do coordination and disclosure here on
|
||||
GitHub (including using GitHub Security Advisory). The Google Security Team will
|
||||
respond within 5 working days of your report on g.co/vulnz.
|
||||
|
||||
[GitHub Security Advisory]: https://github.com/google-gemini/gemini-cli/security/advisories
|
||||
[GitHub Security Advisory]:
|
||||
https://github.com/google-gemini/gemini-cli/security/advisories
|
||||
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../CONTRIBUTING.md
|
||||
+49
-23
@@ -4,19 +4,25 @@ This document provides a high-level overview of the Gemini CLI's architecture.
|
||||
|
||||
## Core components
|
||||
|
||||
The Gemini CLI is primarily composed of two main packages, along with a suite of tools that can be used by the system in the course of handling command-line input:
|
||||
The Gemini CLI is primarily composed of two main packages, along with a suite of
|
||||
tools that can be used by the system in the course of handling command-line
|
||||
input:
|
||||
|
||||
1. **CLI package (`packages/cli`):**
|
||||
- **Purpose:** This contains the user-facing portion of the Gemini CLI, such as handling the initial user input, presenting the final output, and managing the overall user experience.
|
||||
- **Purpose:** This contains the user-facing portion of the Gemini CLI, such
|
||||
as handling the initial user input, presenting the final output, and
|
||||
managing the overall user experience.
|
||||
- **Key functions contained in the package:**
|
||||
- [Input processing](./cli/commands.md)
|
||||
- [Input processing](/docs/cli/commands)
|
||||
- History management
|
||||
- Display rendering
|
||||
- [Theme and UI customization](./cli/themes.md)
|
||||
- [CLI configuration settings](./cli/configuration.md)
|
||||
- [Theme and UI customization](/docs/cli/themes)
|
||||
- [CLI configuration settings](/docs/get-started/configuration)
|
||||
|
||||
2. **Core package (`packages/core`):**
|
||||
- **Purpose:** This acts as the backend for the Gemini CLI. It receives requests sent from `packages/cli`, orchestrates interactions with the Gemini API, and manages the execution of available tools.
|
||||
- **Purpose:** This acts as the backend for the Gemini CLI. It receives
|
||||
requests sent from `packages/cli`, orchestrates interactions with the
|
||||
Gemini API, and manages the execution of available tools.
|
||||
- **Key functions contained in the package:**
|
||||
- API client for communicating with the Google Gemini API
|
||||
- Prompt construction and management
|
||||
@@ -25,30 +31,50 @@ The Gemini CLI is primarily composed of two main packages, along with a suite of
|
||||
- Server-side configuration
|
||||
|
||||
3. **Tools (`packages/core/src/tools/`):**
|
||||
- **Purpose:** These are individual modules that extend the capabilities of the Gemini model, allowing it to interact with the local environment (e.g., file system, shell commands, web fetching).
|
||||
- **Interaction:** `packages/core` invokes these tools based on requests from the Gemini model.
|
||||
- **Purpose:** These are individual modules that extend the capabilities of
|
||||
the Gemini model, allowing it to interact with the local environment
|
||||
(e.g., file system, shell commands, web fetching).
|
||||
- **Interaction:** `packages/core` invokes these tools based on requests
|
||||
from the Gemini model.
|
||||
|
||||
## Interaction Flow
|
||||
## Interaction flow
|
||||
|
||||
A typical interaction with the Gemini CLI follows this flow:
|
||||
|
||||
1. **User input:** The user types a prompt or command into the terminal, which is managed by `packages/cli`.
|
||||
2. **Request to core:** `packages/cli` sends the user's input to `packages/core`.
|
||||
1. **User input:** The user types a prompt or command into the terminal, which
|
||||
is managed by `packages/cli`.
|
||||
2. **Request to core:** `packages/cli` sends the user's input to
|
||||
`packages/core`.
|
||||
3. **Request processed:** The core package:
|
||||
- Constructs an appropriate prompt for the Gemini API, possibly including conversation history and available tool definitions.
|
||||
- Constructs an appropriate prompt for the Gemini API, possibly including
|
||||
conversation history and available tool definitions.
|
||||
- Sends the prompt to the Gemini API.
|
||||
4. **Gemini API response:** The Gemini API processes the prompt and returns a response. This response might be a direct answer or a request to use one of the available tools.
|
||||
4. **Gemini API response:** The Gemini API processes the prompt and returns a
|
||||
response. This response might be a direct answer or a request to use one of
|
||||
the available tools.
|
||||
5. **Tool execution (if applicable):**
|
||||
- When the Gemini API requests a tool, the core package prepares to execute it.
|
||||
- If the requested tool can modify the file system or execute shell commands, the user is first given details of the tool and its arguments, and the user must approve the execution.
|
||||
- Read-only operations, such as reading files, might not require explicit user confirmation to proceed.
|
||||
- Once confirmed, or if confirmation is not required, the core package executes the relevant action within the relevant tool, and the result is sent back to the Gemini API by the core package.
|
||||
- When the Gemini API requests a tool, the core package prepares to execute
|
||||
it.
|
||||
- If the requested tool can modify the file system or execute shell
|
||||
commands, the user is first given details of the tool and its arguments,
|
||||
and the user must approve the execution.
|
||||
- Read-only operations, such as reading files, might not require explicit
|
||||
user confirmation to proceed.
|
||||
- Once confirmed, or if confirmation is not required, the core package
|
||||
executes the relevant action within the relevant tool, and the result is
|
||||
sent back to the Gemini API by the core package.
|
||||
- The Gemini API processes the tool result and generates a final response.
|
||||
6. **Response to CLI:** The core package sends the final response back to the CLI package.
|
||||
7. **Display to user:** The CLI package formats and displays the response to the user in the terminal.
|
||||
6. **Response to CLI:** The core package sends the final response back to the
|
||||
CLI package.
|
||||
7. **Display to user:** The CLI package formats and displays the response to
|
||||
the user in the terminal.
|
||||
|
||||
## Key Design Principles
|
||||
## Key design principles
|
||||
|
||||
- **Modularity:** Separating the CLI (frontend) from the Core (backend) allows for independent development and potential future extensions (e.g., different frontends for the same backend).
|
||||
- **Extensibility:** The tool system is designed to be extensible, allowing new capabilities to be added.
|
||||
- **User experience:** The CLI focuses on providing a rich and interactive terminal experience.
|
||||
- **Modularity:** Separating the CLI (frontend) from the Core (backend) allows
|
||||
for independent development and potential future extensions (e.g., different
|
||||
frontends for the same backend).
|
||||
- **Extensibility:** The tool system is designed to be extensible, allowing new
|
||||
capabilities to be added.
|
||||
- **User experience:** The CLI focuses on providing a rich and interactive
|
||||
terminal experience.
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 350 KiB After Width: | Height: | Size: 60 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 110 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 54 KiB |
@@ -0,0 +1,639 @@
|
||||
# Gemini CLI release notes
|
||||
|
||||
Gemini CLI has three major release channels: nightly, preview, and stable. For
|
||||
most users, we recommend the stable release.
|
||||
|
||||
On this page, you can find information regarding the current releases and
|
||||
announcements from each release.
|
||||
|
||||
For the full changelog, refer to
|
||||
[Releases - google-gemini/gemini-cli](https://github.com/google-gemini/gemini-cli/releases)
|
||||
on GitHub.
|
||||
|
||||
## Current releases
|
||||
|
||||
| Release channel | Notes |
|
||||
| :-------------------- | :---------------------------------------------- |
|
||||
| Nightly | Nightly release with the most recent changes. |
|
||||
| [Preview](preview.md) | Experimental features ready for early feedback. |
|
||||
| [Stable](latest.md) | Stable, recommended for general use. |
|
||||
|
||||
## Announcements: v0.23.0 - 2026-01-07
|
||||
|
||||
- 🎉 **Experimental Agent Skills Support in Preview:** Gemini CLI now supports
|
||||
[Agent Skills](https://agentskills.io/home) in our preview builds. This is an
|
||||
early preview where we’re looking for feedback!
|
||||
- Install Preview: `npm install -g @google/gemini-cli@preview`
|
||||
- Enable in `/settings`
|
||||
- Docs:
|
||||
[https://geminicli.com/docs/cli/skills/](https://geminicli.com/docs/cli/skills/)
|
||||
- **Gemini CLI wrapped:** Run `npx gemini-wrapped` to visualize your usage
|
||||
stats, top models, languages, and more!
|
||||
- **Windows clipboard image support:** Windows users can now paste images
|
||||
directly from their clipboard into the CLI using `Alt`+`V`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/13997) by
|
||||
[@sgeraldes](https://github.com/sgeraldes))
|
||||
- **Terminal background color detection:** Automatically optimizes your
|
||||
terminal's background color to select compatible themes and provide
|
||||
accessibility warnings.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/15132) by
|
||||
[@jacob314](https://github.com/jacob314))
|
||||
- **Session logout:** Use the new `/logout` command to instantly clear
|
||||
credentials and reset your authentication state for seamless account
|
||||
switching. ([pr](https://github.com/google-gemini/gemini-cli/pull/13383) by
|
||||
[@CN-Scars](https://github.com/CN-Scars))
|
||||
|
||||
## Announcements: v0.22.0 - 2025-12-22
|
||||
|
||||
- 🎉**Free Tier + Gemini 3:** Free tier users now all have access to Gemini 3
|
||||
Pro & Flash. Enable in `/settings` by toggling "Preview Features" to `true`.
|
||||
- 🎉**Gemini CLI + Colab:** Gemini CLI is now pre-installed. Can be used
|
||||
headlessly in notebook cells or interactively in the built-in terminal
|
||||
([pic](https://imgur.com/a/G0Tn7vi))
|
||||
- 🎉**Gemini CLI Extensions:**
|
||||
- **Conductor:** Planning++, Gemini works with you to build out a detailed
|
||||
plan, pull in extra details as needed, ultimately to give the LLM guardrails
|
||||
with artifacts. Measure twice, implement once!
|
||||
|
||||
`gemini extensions install https://github.com/gemini-cli-extensions/conductor`
|
||||
|
||||
Blog:
|
||||
[https://developers.googleblog.com/conductor-introducing-context-driven-development-for-gemini-cli/](https://developers.googleblog.com/conductor-introducing-context-driven-development-for-gemini-cli/)
|
||||
|
||||
- **Endor Labs:** Perform code analysis, vulnerability scanning, and
|
||||
dependency checks using natural language.
|
||||
|
||||
`gemini extensions install https://github.com/endorlabs/gemini-extension`
|
||||
|
||||
## Announcements: v0.21.0 - 2025-12-15
|
||||
|
||||
- **⚡️⚡️⚡️ Gemini 3 Flash + Gemini CLI:** Better, faster and cheaper than 2.5
|
||||
Pro - and in some scenarios better than 3 Pro! For paid tiers + free tier
|
||||
users who were on the wait list enable **Preview Features** in `/settings.`
|
||||
- For more information:
|
||||
[Gemini 3 Flash is now available in Gemini CLI](https://developers.googleblog.com/gemini-3-flash-is-now-available-in-gemini-cli/).
|
||||
- 🎉 Gemini CLI Extensions:
|
||||
- Rill: Utilize natural language to analyze Rill data, enabling the
|
||||
exploration of metrics and trends without the need for manual queries.
|
||||
`gemini extensions install https://github.com/rilldata/rill-gemini-extension`
|
||||
- Browserbase: Interact with web pages, take screenshots, extract information,
|
||||
and perform automated actions with atomic precision.
|
||||
`gemini extensions install https://github.com/browserbase/mcp-server-browserbase`
|
||||
- Quota Visibility: The `/stats` command now displays quota information for all
|
||||
available models, including those not used in the current session. (@sehoon38)
|
||||
- Fuzzy Setting Search: Users can now quickly find settings using fuzzy search
|
||||
within the settings dialog. (@sehoon38)
|
||||
- MCP Resource Support: Users can now discover, view, and search through
|
||||
resources using the @ command. (@MrLesk)
|
||||
- Auto-execute Simple Slash Commands: Simple slash commands are now executed
|
||||
immediately on enter. (@jackwotherspoon)
|
||||
|
||||
## Announcements: v0.20.0 - 2025-12-01
|
||||
|
||||
- **Multi-file Drag & Drop:** Users can now drag and drop multiple files into
|
||||
the terminal, and the CLI will automatically prefix each valid path with `@`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/14832) by
|
||||
[@jackwotherspoon](https://github.com/jackwotherspoon))
|
||||
- **Persistent "Always Allow" Policies:** Users can now save "Always Allow"
|
||||
decisions for tool executions, with granular control over specific shell
|
||||
commands and multi-cloud platform tools.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/14737) by
|
||||
[@allenhutchison](https://github.com/allenhutchison))
|
||||
|
||||
## Announcements: v0.19.0 - 2025-11-24
|
||||
|
||||
- 🎉 **New extensions:**
|
||||
- **Eleven Labs:** Create, play, manage your audio play tracks with the Eleven
|
||||
Labs Gemini CLI extension:
|
||||
`gemini extensions install https://github.com/elevenlabs/elevenlabs-mcp`
|
||||
- **Zed integration:** Users can now leverage Gemini 3 within the Zed
|
||||
integration after enabling "Preview Features" in their CLI’s `/settings`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/13398) by
|
||||
[@benbrandt](https://github.com/benbrandt))
|
||||
- **Interactive shell:**
|
||||
- **Click-to-Focus:** When "Use Alternate Buffer" setting is enabled, users
|
||||
can click within the embedded shell output to focus it for input.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/13341) by
|
||||
[@galz10](https://github.com/galz10))
|
||||
- **Loading phrase:** Clearly indicates when the interactive shell is awaiting
|
||||
user input. ([vid](https://imgur.com/a/kjK8bUK),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/12535) by
|
||||
[@jackwotherspoon](https://github.com/jackwotherspoon))
|
||||
|
||||
## Announcements: v0.18.0 - 2025-11-17
|
||||
|
||||
- 🎉 **New extensions:**
|
||||
- **Google Workspace**: Integrate Gemini CLI with your Workspace data. Write
|
||||
docs, build slides, chat with others or even get your calc on in sheets:
|
||||
`gemini extensions install https://github.com/gemini-cli-extensions/workspace`
|
||||
- Blog:
|
||||
[https://allen.hutchison.org/2025/11/19/bringing-the-office-to-the-terminal/](https://allen.hutchison.org/2025/11/19/bringing-the-office-to-the-terminal/)
|
||||
- **Redis:** Manage and search data in Redis with natural language:
|
||||
`gemini extensions install https://github.com/redis/mcp-redis`
|
||||
- **Anomalo:** Query your data warehouse table metadata and quality status
|
||||
through commands and natural language:
|
||||
`gemini extensions install https://github.com/datagravity-ai/anomalo-gemini-extension`
|
||||
- **Experimental permission improvements:** We are now experimenting with a new
|
||||
policy engine in Gemini CLI. This allows users and administrators to create
|
||||
fine-grained policy for tool calls. Currently behind a flag. See
|
||||
[policy engine documentation](../core/policy-engine.md) for more information.
|
||||
- Blog:
|
||||
[https://allen.hutchison.org/2025/11/26/the-guardrails-of-autonomy/](https://allen.hutchison.org/2025/11/26/the-guardrails-of-autonomy/)
|
||||
- **Gemini 3 support for paid:** Gemini 3 support has been rolled out to all API
|
||||
key, Google AI Pro or Google AI Ultra (for individuals, not businesses) and
|
||||
Gemini Code Assist Enterprise users. Enable it via `/settings` and toggling on
|
||||
**Preview Features**.
|
||||
- **Updated UI rollback:** We’ve temporarily rolled back our updated UI to give
|
||||
it more time to bake. This means for a time you won’t have embedded scrolling
|
||||
or mouse support. You can re-enable with `/settings` -> **Use Alternate Screen
|
||||
Buffer** -> `true`.
|
||||
- **Model in history:** Users can now toggle in `/settings` to display model in
|
||||
their chat history. ([gif](https://imgur.com/a/uEmNKnQ),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/13034) by
|
||||
[@scidomino](https://github.com/scidomino))
|
||||
- **Multi-uninstall:** Users can now uninstall multiple extensions with a single
|
||||
command. ([pic](https://imgur.com/a/9Dtq8u2),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/13016) by
|
||||
[@JayadityaGit](https://github.com/JayadityaGit))
|
||||
|
||||
## Announcements: v0.16.0 - 2025-11-10
|
||||
|
||||
- **Gemini 3 + Gemini CLI:** launch 🚀🚀🚀
|
||||
- **Data Commons Gemini CLI Extension** - A new Data Commons Gemini CLI
|
||||
extension that lets you query open-source statistical data from
|
||||
datacommons.org. **To get started, you'll need a Data Commons API key and uv
|
||||
installed**. These and other details to get you started with the extension can
|
||||
be found at
|
||||
[https://github.com/gemini-cli-extensions/datacommons](https://github.com/gemini-cli-extensions/datacommons).
|
||||
|
||||
## Announcements: v0.15.0 - 2025-11-03
|
||||
|
||||
- **🎉 Seamless scrollable UI and mouse support:** We’ve given Gemini CLI a
|
||||
major facelift to make your terminal experience smoother and much more
|
||||
polished. You now get a flicker-free display with sticky headers that keep
|
||||
important context visible and a stable input prompt that doesn't jump around.
|
||||
We even added mouse support so you can click right where you need to type!
|
||||
([gif](https://imgur.com/a/O6qc7bx),
|
||||
[@jacob314](https://github.com/jacob314)).
|
||||
- **Announcement:**
|
||||
[https://developers.googleblog.com/en/making-the-terminal-beautiful-one-pixel-at-a-time/](https://developers.googleblog.com/en/making-the-terminal-beautiful-one-pixel-at-a-time/)
|
||||
- **🎉 New partner extensions:**
|
||||
- **Arize:** Seamlessly instrument AI applications with Arize AX and grant
|
||||
direct access to Arize support:
|
||||
|
||||
`gemini extensions install https://github.com/Arize-ai/arize-tracing-assistant`
|
||||
|
||||
- **Chronosphere:** Retrieve logs, metrics, traces, events, and specific
|
||||
entities:
|
||||
|
||||
`gemini extensions install https://github.com/chronosphereio/chronosphere-mcp`
|
||||
|
||||
- **Transmit:** Comprehensive context, validation, and automated fixes for
|
||||
creating production-ready authentication and identity workflows:
|
||||
|
||||
`gemini extensions install https://github.com/TransmitSecurity/transmit-security-journey-builder`
|
||||
|
||||
- **Todo planning:** Complex questions now get broken down into todo lists that
|
||||
the model can manage and check off. ([gif](https://imgur.com/a/EGDfNlZ),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/12905) by
|
||||
[@anj-s](https://github.com/anj-s))
|
||||
- **Disable GitHub extensions:** Users can now prevent the installation and
|
||||
loading of extensions from GitHub.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/12838) by
|
||||
[@kevinjwang1](https://github.com/kevinjwang1)).
|
||||
- **Extensions restart:** Users can now explicitly restart extensions using the
|
||||
`/extensions restart` command.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/12739) by
|
||||
[@jakemac53](https://github.com/jakemac53)).
|
||||
- **Better Angular support:** Angular workflows should now be more seamless
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10252) by
|
||||
[@MarkTechson](https://github.com/MarkTechson)).
|
||||
- **Validate command:** Users can now check that local extensions are formatted
|
||||
correctly. ([pr](https://github.com/google-gemini/gemini-cli/pull/12186) by
|
||||
[@kevinjwang1](https://github.com/kevinjwang1)).
|
||||
|
||||
## Announcements: v0.12.0 - 2025-10-27
|
||||
|
||||

|
||||
|
||||
- **🎉 New partner extensions:**
|
||||
- **🤗 Hugging Face extension:** Access the Hugging Face hub.
|
||||
([gif](https://drive.google.com/file/d/1LEzIuSH6_igFXq96_tWev11svBNyPJEB/view?usp=sharing&resourcekey=0-LtPTzR1woh-rxGtfPzjjfg))
|
||||
|
||||
`gemini extensions install https://github.com/huggingface/hf-mcp-server`
|
||||
|
||||
- **Monday.com extension**: Analyze your sprints, update your task boards,
|
||||
etc.
|
||||
([gif](https://drive.google.com/file/d/1cO0g6kY1odiBIrZTaqu5ZakaGZaZgpQv/view?usp=sharing&resourcekey=0-xEr67SIjXmAXRe1PKy7Jlw))
|
||||
|
||||
`gemini extensions install https://github.com/mondaycom/mcp`
|
||||
|
||||
- **Data Commons extension:** Query public datasets or ground responses on
|
||||
data from Data Commons
|
||||
([gif](https://drive.google.com/file/d/1cuj-B-vmUkeJnoBXrO_Y1CuqphYc6p-O/view?usp=sharing&resourcekey=0-0adXCXDQEd91ZZW63HbW-Q)).
|
||||
|
||||
`gemini extensions install https://github.com/gemini-cli-extensions/datacommons`
|
||||
|
||||
- **Model selection:** Choose the Gemini model for your session with `/model`.
|
||||
([pic](https://imgur.com/a/ABFcWWw),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/8940) by
|
||||
[@abhipatel12](https://github.com/abhipatel12)).
|
||||
- **Model routing:** Gemini CLI will now intelligently pick the best model for
|
||||
the task. Simple queries will be sent to Flash while complex analytical or
|
||||
creative tasks will still use the power of Pro. This ensures your quota will
|
||||
last for a longer period of time. You can always opt-out of this via `/model`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/9262) by
|
||||
[@abhipatel12](https://github.com/abhipatel12)).
|
||||
- Discussion:
|
||||
[https://github.com/google-gemini/gemini-cli/discussions/12375](https://github.com/google-gemini/gemini-cli/discussions/12375)
|
||||
- **Codebase investigator subagent:** We now have a new built-in subagent that
|
||||
will explore your workspace and resolve relevant information to improve
|
||||
overall performance.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/9988) by
|
||||
[@abhipatel12](https://github.com/abhipatel12),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/10282) by
|
||||
[@silviojr](https://github.com/silviojr)).
|
||||
- Enable, disable, or limit turns in `/settings`, plus advanced configs in
|
||||
`settings.json` ([pic](https://imgur.com/a/yJiggNO),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/10844) by
|
||||
[@silviojr](https://github.com/silviojr)).
|
||||
- **Explore extensions with `/extension`:** Users can now open the extensions
|
||||
page in their default browser directly from the CLI using the `/extension`
|
||||
explore command. ([pr](https://github.com/google-gemini/gemini-cli/pull/11846)
|
||||
by [@JayadityaGit](https://github.com/JayadityaGit)).
|
||||
- **Configurable compression:** Users can modify the compression threshold in
|
||||
`/settings`. The default has been made more proactive
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/12317) by
|
||||
[@scidomino](https://github.com/scidomino)).
|
||||
- **API key authentication:** Users can now securely enter and store their
|
||||
Gemini API key via a new dialog, eliminating the need for environment
|
||||
variables and repeated entry.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/11760) by
|
||||
[@galz10](https://github.com/galz10)).
|
||||
- **Sequential approval:** Users can now approve multiple tool calls
|
||||
sequentially during execution.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/11593) by
|
||||
[@joshualitt](https://github.com/joshualitt)).
|
||||
|
||||
## Announcements: v0.11.0 - 2025-10-20
|
||||
|
||||

|
||||
|
||||
- 🎉 **Gemini CLI Jules Extension:** Use Gemini CLI to orchestrate Jules. Spawn
|
||||
remote workers, delegate tedious tasks, or check in on running jobs!
|
||||
- Install:
|
||||
`gemini extensions install https://github.com/gemini-cli-extensions/jules`
|
||||
- Announcement:
|
||||
[https://developers.googleblog.com/en/introducing-the-jules-extension-for-gemini-cli/](https://developers.googleblog.com/en/introducing-the-jules-extension-for-gemini-cli/)
|
||||
- **Stream JSON output:** Stream real-time JSONL events with
|
||||
`--output-format stream-json` to monitor AI agent progress when run
|
||||
headlessly. ([gif](https://imgur.com/a/0UCE81X),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/10883) by
|
||||
[@anj-s](https://github.com/anj-s))
|
||||
- **Markdown toggle:** Users can now switch between rendered and raw markdown
|
||||
display using `alt+m `or` ctrl+m`. ([gif](https://imgur.com/a/lDNdLqr),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/10383) by
|
||||
[@srivatsj](https://github.com/srivatsj))
|
||||
- **Queued message editing:** Users can now quickly edit queued messages by
|
||||
pressing the up arrow key when the input is empty.
|
||||
([gif](https://imgur.com/a/ioRslLd),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/10392) by
|
||||
[@akhil29](https://github.com/akhil29))
|
||||
- **JSON web fetch**: Non-HTML content like JSON APIs or raw source code are now
|
||||
properly shown to the model (previously only supported HTML)
|
||||
([gif](https://imgur.com/a/Q58U4qJ),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/11284) by
|
||||
[@abhipatel12](https://github.com/abhipatel12))
|
||||
- **Non-interactive MCP commands:** Users can now run MCP slash commands in
|
||||
non-interactive mode `gemini "/some-mcp-prompt"`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10194) by
|
||||
[@capachino](https://github.com/capachino))
|
||||
- **Removal of deprecated flags:** We’ve finally removed a number of deprecated
|
||||
flags to cleanup Gemini CLI’s invocation profile:
|
||||
- `--all-files` / `-a` in favor of `@` from within Gemini CLI.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/11228) by
|
||||
[@allenhutchison](https://github.com/allenhutchison))
|
||||
- `--telemetry-*` flags in favor of
|
||||
[environment variables](https://github.com/google-gemini/gemini-cli/pull/11318)
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/11318) by
|
||||
[@allenhutchison](https://github.com/allenhutchison))
|
||||
|
||||
## Announcements: v0.10.0 - 2025-10-13
|
||||
|
||||
- **Polish:** The team has been heads down bug fixing and investing heavily into
|
||||
polishing existing flows, tools, and interactions.
|
||||
- **Interactive Shell Tool calling:** Gemini CLI can now also execute
|
||||
interactive tools if needed
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/11225) by
|
||||
[@galz10](https://github.com/galz10)).
|
||||
- **Alt+Key support:** Enables broader support for Alt+Key keyboard shortcuts
|
||||
across different terminals.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10767) by
|
||||
[@srivatsj](https://github.com/srivatsj)).
|
||||
- **Telemetry Diff stats:** Track line changes made by the model and user during
|
||||
file operations via OTEL.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10819) by
|
||||
[@jerop](https://github.com/jerop)).
|
||||
|
||||
## Announcements: v0.9.0 - 2025-10-06
|
||||
|
||||
- 🎉 **Interactive Shell:** Run interactive commands like `vim`, `rebase -i`, or
|
||||
even `gemini` 😎 directly in Gemini CLI:
|
||||
- Blog:
|
||||
[https://developers.googleblog.com/en/say-hello-to-a-new-level-of-interactivity-in-gemini-cli/](https://developers.googleblog.com/en/say-hello-to-a-new-level-of-interactivity-in-gemini-cli/)
|
||||
- **Install pre-release extensions:** Install the latest `--pre-release`
|
||||
versions of extensions. Used for when an extension’s release hasn’t been
|
||||
marked as "latest".
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10752) by
|
||||
[@jakemac53](https://github.com/jakemac53))
|
||||
- **Simplified extension creation:** Create a new, empty extension. Templates
|
||||
are no longer required.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10629) by
|
||||
[@chrstnb](https://github.com/chrstnb))
|
||||
- **OpenTelemetry GenAI metrics:** Aligns telemetry with industry-standard
|
||||
semantic conventions for improved interoperability.
|
||||
([spec](https://opentelemetry.io/docs/concepts/semantic-conventions/),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/10343) by
|
||||
[@jerop](https://github.com/jerop))
|
||||
- **List memory files:** Quickly find the location of your long-term memory
|
||||
files with `/memory list`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/10108) by
|
||||
[@sgnagnarella](https://github.com/sgnagnarella))
|
||||
|
||||
## Announcements: v0.8.0 - 2025-09-29
|
||||
|
||||
- 🎉 **Announcing Gemini CLI Extensions** 🎉
|
||||
- Completely customize your Gemini CLI experience to fit your workflow.
|
||||
- Build and share your own Gemini CLI extensions with the world.
|
||||
- Launching with a growing catalog of community, partner, and Google-built
|
||||
extensions.
|
||||
- Check extensions from
|
||||
[key launch partners](https://github.com/google-gemini/gemini-cli/discussions/10718).
|
||||
- Easy install:
|
||||
- `gemini extensions install <github url|folder path>`
|
||||
- Easy management:
|
||||
- `gemini extensions install|uninstall|link`
|
||||
- `gemini extensions enable|disable`
|
||||
- `gemini extensions list|update|new`
|
||||
- Or use commands while running with `/extensions list|update`.
|
||||
- Everything you need to know:
|
||||
[Now open for building: Introducing Gemini CLI extensions](https://blog.google/technology/developers/gemini-cli-extensions/).
|
||||
- 🎉 **Our New Home Page & Better Documentation** 🎉
|
||||
- Check out our new home page for better getting started material, reference
|
||||
documentation, extensions and more!
|
||||
- _Homepage:_ [https://geminicli.com](https://geminicli.com)
|
||||
- ‼️*NEW documentation:*
|
||||
[https://geminicli.com/docs](https://geminicli.com/docs) (Have any
|
||||
[suggestions](https://github.com/google-gemini/gemini-cli/discussions/8722)?)
|
||||
- _Extensions:_
|
||||
[https://geminicli.com/extensions](https://geminicli.com/extensions)
|
||||
- **Non-Interactive Allowed Tools:** `--allowed-tools` will now also work in
|
||||
non-interactive mode.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/9114) by
|
||||
[@mistergarrison](https://github.com/mistergarrison))
|
||||
- **Terminal Title Status:** See the CLI's real-time status and thoughts
|
||||
directly in the terminal window's title by setting `showStatusInTitle: true`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/4386) by
|
||||
[@Fridayxiao](https://github.com/Fridayxiao))
|
||||
- **Small features, polish, reliability & bug fixes:** A large amount of
|
||||
changes, smaller features, UI updates, reliability and bug fixes + general
|
||||
polish made it in this week!
|
||||
|
||||
## Announcements: v0.7.0 - 2025-09-22
|
||||
|
||||
- 🎉**Build your own Gemini CLI IDE plugin:** We've published a spec for
|
||||
creating IDE plugins to enable rich context-aware experiences and native
|
||||
in-editor diffing in your IDE of choice.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8479) by
|
||||
[@skeshive](https://github.com/skeshive))
|
||||
- 🎉 **Gemini CLI extensions**
|
||||
- **Flutter:** An early version to help you create, build, test, and run
|
||||
Flutter apps with Gemini CLI
|
||||
([extension](https://github.com/gemini-cli-extensions/flutter))
|
||||
- **nanobanana:** Integrate nanobanana into Gemini CLI
|
||||
([extension](https://github.com/gemini-cli-extensions/nanobanana))
|
||||
- **Telemetry config via environment:** Manage telemetry settings using
|
||||
environment variables for a more flexible setup.
|
||||
([docs](https://github.com/google-gemini/gemini-cli/blob/main/docs/cli/telemetry.md#configuration),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/9113) by
|
||||
[@jerop](https://github.com/jerop))
|
||||
- **Experimental todos:** Track and display progress on complex tasks with a
|
||||
managed checklist. Off by default but can be enabled via
|
||||
`"useWriteTodos": true`
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8761) by
|
||||
[@anj-s](https://github.com/anj-s))
|
||||
- **Share chat support for tools:** Using `/chat share` will now also render
|
||||
function calls and responses in the final markdown file.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8693) by
|
||||
[@rramkumar1](https://github.com/rramkumar1))
|
||||
- **Citations:** Now enabled for all users
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8570) by
|
||||
[@scidomino](https://github.com/scidomino))
|
||||
- **Custom commands in Headless Mode:** Run custom slash commands directly from
|
||||
the command line in non-interactive mode: `gemini "/joke Chuck Norris"`
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8305) by
|
||||
[@capachino](https://github.com/capachino))
|
||||
- **Small features, polish, reliability & bug fixes:** A large amount of
|
||||
changes, smaller features, UI updates, reliability and bug fixes + general
|
||||
polish made it in this week!
|
||||
|
||||
## Announcements: v0.6.0 - 2025-09-15
|
||||
|
||||
- 🎉 **Higher limits for Google AI Pro and Ultra subscribers:** We’re psyched to
|
||||
finally announce that Google AI Pro and AI Ultra subscribers now get access to
|
||||
significantly higher 2.5 quota limits for Gemini CLI!
|
||||
- **Announcement:**
|
||||
[https://blog.google/technology/developers/gemini-cli-code-assist-higher-limits/](https://blog.google/technology/developers/gemini-cli-code-assist-higher-limits/)
|
||||
- 🎉**Gemini CLI Databases and BigQuery Extensions:** Connect Gemini CLI to all
|
||||
of your cloud data with Gemini CLI.
|
||||
- Announcement and how to get started with each of the below extensions:
|
||||
[https://cloud.google.com/blog/products/databases/gemini-cli-extensions-for-google-data-cloud?e=48754805](https://cloud.google.com/blog/products/databases/gemini-cli-extensions-for-google-data-cloud?e=48754805)
|
||||
- **AlloyDB:** Interact, manage and observe AlloyDB for PostgreSQL databases
|
||||
([manage](https://github.com/gemini-cli-extensions/alloydb#configuration),
|
||||
[observe](https://github.com/gemini-cli-extensions/alloydb-observability#configuration))
|
||||
- **BigQuery:** Connect and query your BigQuery datasets or utilize a
|
||||
sub-agent for contextual insights
|
||||
([query](https://github.com/gemini-cli-extensions/bigquery-data-analytics#configuration),
|
||||
[sub-agent](https://github.com/gemini-cli-extensions/bigquery-conversational-analytics))
|
||||
- **Cloud SQL:** Interact, manage and observe Cloud SQL for PostgreSQL
|
||||
([manage](https://github.com/gemini-cli-extensions/cloud-sql-postgresql#configuration),[ observe](https://github.com/gemini-cli-extensions/cloud-sql-postgresql-observability#configuration)),
|
||||
Cloud SQL for MySQL
|
||||
([manage](https://github.com/gemini-cli-extensions/cloud-sql-mysql#configuration),[ observe](https://github.com/gemini-cli-extensions/cloud-sql-mysql-observability#configuration))
|
||||
and Cloud SQL for SQL Server
|
||||
([manage](https://github.com/gemini-cli-extensions/cloud-sql-sqlserver#configuration),[ observe](https://github.com/gemini-cli-extensions/cloud-sql-sqlserver-observability#configuration))
|
||||
databases.
|
||||
- **Dataplex:** Discover, manage, and govern data and AI artifacts
|
||||
([extension](https://github.com/gemini-cli-extensions/dataplex#configuration))
|
||||
- **Firestore:** Interact with Firestore databases, collections and documents
|
||||
([extension](https://github.com/gemini-cli-extensions/firestore-native#configuration))
|
||||
- **Looker:** Query data, run Looks and create dashboards
|
||||
([extension](https://github.com/gemini-cli-extensions/looker#configuration))
|
||||
- **MySQL:** Interact with MySQL databases
|
||||
([extension](https://github.com/gemini-cli-extensions/mysql#configuration))
|
||||
- **Postgres:** Interact with PostgreSQL databases
|
||||
([extension](https://github.com/gemini-cli-extensions/postgres#configuration))
|
||||
- **Spanner:** Interact with Spanner databases
|
||||
([extension](https://github.com/gemini-cli-extensions/spanner#configuration))
|
||||
- **SQL Server:** Interact with SQL Server databases
|
||||
([extension](https://github.com/gemini-cli-extensions/sql-server#configuration))
|
||||
- **MCP Toolbox:** Configure and load custom tools for more than 30+ data
|
||||
sources
|
||||
([extension](https://github.com/gemini-cli-extensions/mcp-toolbox#configuration))
|
||||
- **JSON output mode:** Have Gemini CLI output JSON with `--output-format json`
|
||||
when invoked headlessly for easy parsing and post-processing. Includes
|
||||
response, stats and errors.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8119) by
|
||||
[@jerop](https://github.com/jerop))
|
||||
- **Keybinding triggered approvals:** When you use shortcuts (`shift+y` or
|
||||
`shift+tab`) to activate YOLO/auto-edit modes any pending confirmation dialogs
|
||||
will now approve. ([pr](https://github.com/google-gemini/gemini-cli/pull/6665)
|
||||
by [@bulkypanda](https://github.com/bulkypanda))
|
||||
- **Chat sharing:** Convert the current conversation to a Markdown or JSON file
|
||||
with _/chat share <file.md|file.json>_
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8139) by
|
||||
[@rramkumar1](https://github.com/rramkumar1))
|
||||
- **Prompt search:** Search your prompt history using `ctrl+r`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/5539) by
|
||||
[@Aisha630](https://github.com/Aisha630))
|
||||
- **Input undo/redo:** Recover accidentally deleted text in the input prompt
|
||||
using `ctrl+z` (undo) and `ctrl+shift+z` (redo).
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/4625) by
|
||||
[@masiafrest](https://github.com/masiafrest))
|
||||
- **Loop detection confirmation:** When loops are detected you are now presented
|
||||
with a dialog to disable detection for the current session.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8231) by
|
||||
[@SandyTao520](https://github.com/SandyTao520))
|
||||
- **Direct to Google Cloud Telemetry:** Directly send telemetry to Google Cloud
|
||||
for a simpler and more streamlined setup.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/8541) by
|
||||
[@jerop](https://github.com/jerop))
|
||||
- **Visual Mode Indicator Revamp:** ‘shell’, 'accept edits' and 'yolo' modes now
|
||||
have colors to match their impact / usage. Input box now also updates.
|
||||
([shell](https://imgur.com/a/DovpVF1),
|
||||
[accept-edits](https://imgur.com/a/33KDz3J),
|
||||
[yolo](https://imgur.com/a/tbFwIWp),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/8200) by
|
||||
[@miguelsolorio](https://github.com/miguelsolorio))
|
||||
- **Small features, polish, reliability & bug fixes:** A large amount of
|
||||
changes, smaller features, UI updates, reliability and bug fixes + general
|
||||
polish made it in this week!
|
||||
|
||||
## Announcements: v0.5.0 - 2025-09-08
|
||||
|
||||
- 🎉**FastMCP + Gemini CLI**🎉: Quickly install and manage your Gemini CLI MCP
|
||||
servers with FastMCP ([video](https://imgur.com/a/m8QdCPh),
|
||||
[pr](https://github.com/jlowin/fastmcp/pull/1709) by
|
||||
[@jackwotherspoon](https://github.com/jackwotherspoon)**)**
|
||||
- Getting started:
|
||||
[https://gofastmcp.com/integrations/gemini-cli](https://gofastmcp.com/integrations/gemini-cli)
|
||||
- **Positional Prompt for Non-Interactive:** Seamlessly invoke Gemini CLI
|
||||
headlessly via `gemini "Hello"`. Synonymous with passing `-p`.
|
||||
([gif](https://imgur.com/a/hcBznpB),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/7668) by
|
||||
[@allenhutchison](https://github.com/allenhutchison))
|
||||
- **Experimental Tool output truncation:** Enable truncating shell tool outputs
|
||||
and saving full output to a file by setting
|
||||
`"enableToolOutputTruncation": true `([pr](https://github.com/google-gemini/gemini-cli/pull/8039)
|
||||
by [@SandyTao520](https://github.com/SandyTao520))
|
||||
- **Edit Tool improvements:** Gemini CLI’s ability to edit files should now be
|
||||
far more capable. ([pr](https://github.com/google-gemini/gemini-cli/pull/7679)
|
||||
by [@silviojr](https://github.com/silviojr))
|
||||
- **Custom witty messages:** The feature you’ve all been waiting for…
|
||||
Personalized witty loading messages via
|
||||
`"ui": { "customWittyPhrases": ["YOLO"]}` in `settings.json`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/7641) by
|
||||
[@JayadityaGit](https://github.com/JayadityaGit))
|
||||
- **Nested .gitignore File Handling:** Nested `.gitignore` files are now
|
||||
respected. ([pr](https://github.com/google-gemini/gemini-cli/pull/7645) by
|
||||
[@gsquared94](https://github.com/gsquared94))
|
||||
- **Enforced authentication:** System administrators can now mandate a specific
|
||||
authentication method via
|
||||
`"enforcedAuthType": "oauth-personal|gemini-api-key|…"`in `settings.json`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/6564) by
|
||||
[@chrstnb](https://github.com/chrstnb))
|
||||
- **A2A development-tool extension:** An RFC for an Agent2Agent
|
||||
([A2A](https://a2a-protocol.org/latest/)) powered extension for developer tool
|
||||
use cases.
|
||||
([feedback](https://github.com/google-gemini/gemini-cli/discussions/7822),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/7817) by
|
||||
[@skeshive](https://github.com/skeshive))
|
||||
- **Hands on Codelab:
|
||||
**[https://codelabs.developers.google.com/gemini-cli-hands-on](https://codelabs.developers.google.com/gemini-cli-hands-on)
|
||||
- **Small features, polish, reliability & bug fixes:** A large amount of
|
||||
changes, smaller features, UI updates, reliability and bug fixes + general
|
||||
polish made it in this week!
|
||||
|
||||
## Announcements: v0.4.0 - 2025-09-01
|
||||
|
||||
- 🎉**Gemini CLI CloudRun and Security Integrations**🎉: Automate app deployment
|
||||
and security analysis with CloudRun and Security extension integrations. Once
|
||||
installed deploy your app to the cloud with `/deploy` and find and fix
|
||||
security vulnerabilities with `/security:analyze`.
|
||||
- Announcement and how to get started:
|
||||
[https://cloud.google.com/blog/products/ai-machine-learning/automate-app-deployment-and-security-analysis-with-new-gemini-cli-extensions](https://cloud.google.com/blog/products/ai-machine-learning/automate-app-deployment-and-security-analysis-with-new-gemini-cli-extensions)
|
||||
- **Experimental**
|
||||
- **Edit Tool:** Give our new edit tool a try by setting
|
||||
`"useSmartEdit": true` in `settings.json`!
|
||||
([feedback](https://github.com/google-gemini/gemini-cli/discussions/7758),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/6823) by
|
||||
[@silviojr](https://github.com/silviojr))
|
||||
- **Model talking to itself fix:** We’ve removed a model workaround that would
|
||||
encourage Gemini CLI to continue conversations on your behalf. This may be
|
||||
disruptive and can be disabled via `"skipNextSpeakerCheck": false` in your
|
||||
`settings.json`
|
||||
([feedback](https://github.com/google-gemini/gemini-cli/discussions/6666),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/7614) by
|
||||
[@SandyTao520](https://github.com/SandyTao520))
|
||||
- **Prompt completion:** Get real-time AI suggestions to complete your prompts
|
||||
as you type. Enable it with `"general": { "enablePromptCompletion": true }`
|
||||
and share your feedback!
|
||||
([gif](https://miro.medium.com/v2/resize:fit:2000/format:webp/1*hvegW7YXOg6N_beUWhTdxA.gif),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/4691) by
|
||||
[@3ks](https://github.com/3ks))
|
||||
- **Footer visibility configuration:** Customize the CLI's footer look and feel
|
||||
in `settings.json`
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/7419) by
|
||||
[@miguelsolorio](https://github.com/miguelsolorio))
|
||||
- `hideCWD`: hide current working directory.
|
||||
- `hideSandboxStatus`: hide sandbox status.
|
||||
- `hideModelInfo`: hide current model information.
|
||||
- `hideContextSummary`: hide request context summary.
|
||||
- **Citations:** For enterprise Code Assist licenses users will now see
|
||||
citations in their responses by default. Enable this yourself with
|
||||
`"showCitations": true`
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/7350) by
|
||||
[@scidomino](https://github.com/scidomino))
|
||||
- **Pro Quota Dialog:** Handle daily Pro model usage limits with an interactive
|
||||
dialog that lets you immediately switch auth or fallback.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/7094) by
|
||||
[@JayadityaGit](https://github.com/JayadityaGit))
|
||||
- **Custom commands @:** Embed local file or directory content directly into
|
||||
your custom command prompts using `@{path}` syntax
|
||||
([gif](https://miro.medium.com/v2/resize:fit:2000/format:webp/1*GosBAo2SjMfFffAnzT7ZMg.gif),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/6716) by
|
||||
[@abhipatel12](https://github.com/abhipatel12))
|
||||
- **2.5 Flash Lite support:** You can now use the `gemini-2.5-flash-lite` model
|
||||
for Gemini CLI via `gemini -m …`.
|
||||
([gif](https://miro.medium.com/v2/resize:fit:2000/format:webp/1*P4SKwnrsyBuULoHrFqsFKQ.gif),
|
||||
[pr](https://github.com/google-gemini/gemini-cli/pull/4652) by
|
||||
[@psinha40898](https://github.com/psinha40898))
|
||||
- **CLI streamlining:** We have deprecated a number of command line arguments in
|
||||
favor of `settings.json` alternatives. We will remove these arguments in a
|
||||
future release. See the PR for the full list of deprecations.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/7360) by
|
||||
[@allenhutchison](https://github.com/allenhutchison))
|
||||
- **JSON session summary:** Track and save detailed CLI session statistics to a
|
||||
JSON file for performance analysis with `--session-summary <path>`
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/7347) by
|
||||
[@leehagoodjames](https://github.com/leehagoodjames))
|
||||
- **Robust keyboard handling:** More reliable and consistent behavior for arrow
|
||||
keys, special keys (Home, End, etc.), and modifier combinations across various
|
||||
terminals. ([pr](https://github.com/google-gemini/gemini-cli/pull/7118) by
|
||||
[@deepankarsharma](https://github.com/deepankarsharma))
|
||||
- **MCP loading indicator:** Provides visual feedback during CLI initialization
|
||||
when connecting to multiple servers.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/6923) by
|
||||
[@swissspidy](https://github.com/swissspidy))
|
||||
- **Small features, polish, reliability & bug fixes:** A large amount of
|
||||
changes, smaller features, UI updates, reliability and bug fixes + general
|
||||
polish made it in this week!
|
||||
@@ -0,0 +1,165 @@
|
||||
# Latest stable release: v0.23.0
|
||||
|
||||
Released: January 6, 2026
|
||||
|
||||
For most users, our latest stable release is the recommended release. Install
|
||||
the latest stable version with:
|
||||
|
||||
```
|
||||
npm install -g @google/gemini-cli
|
||||
```
|
||||
|
||||
## Highlights
|
||||
|
||||
- **Gemini CLI wrapped:** Run `npx gemini-wrapped` to visualize your usage
|
||||
stats, top models, languages, and more!
|
||||
- **Windows clipboard image support:** Windows users can now paste images
|
||||
directly from their clipboard into the CLI using `Alt`+`V`.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/13997) by
|
||||
[@sgeraldes](https://github.com/sgeraldes))
|
||||
- **Terminal background color detection:** Automatically optimizes your
|
||||
terminal's background color to select compatible themes and provide
|
||||
accessibility warnings.
|
||||
([pr](https://github.com/google-gemini/gemini-cli/pull/15132) by
|
||||
[@jacob314](https://github.com/jacob314))
|
||||
- **Session logout:** Use the new `/logout` command to instantly clear
|
||||
credentials and reset your authentication state for seamless account
|
||||
switching. ([pr](https://github.com/google-gemini/gemini-cli/pull/13383) by
|
||||
[@CN-Scars](https://github.com/CN-Scars))
|
||||
|
||||
## What's changed
|
||||
|
||||
- Code assist service metrics. by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15024
|
||||
- chore/release: bump version to 0.21.0-nightly.20251216.bb0c0d8ee by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15121
|
||||
- Docs by @Roaimkhan in https://github.com/google-gemini/gemini-cli/pull/15103
|
||||
- Use official ACP SDK and support HTTP/SSE based MCP servers by @SteffenDE in
|
||||
https://github.com/google-gemini/gemini-cli/pull/13856
|
||||
- Remove foreground for themes other than shades of purple and holiday. by
|
||||
@jacob314 in https://github.com/google-gemini/gemini-cli/pull/14606
|
||||
- chore: remove repo specific tips by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15164
|
||||
- chore: remove user query from footer in debug mode by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15169
|
||||
- Disallow unnecessary awaits. by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15172
|
||||
- Add one to the padding in settings dialog to avoid flicker. by @jacob314 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15173
|
||||
- feat(core): introduce remote agent infrastructure and rename local executor by
|
||||
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/15110
|
||||
- feat(cli): Add `/auth logout` command to clear credentials and auth state by
|
||||
@CN-Scars in https://github.com/google-gemini/gemini-cli/pull/13383
|
||||
- (fix) Automated pr labeller by @DaanVersavel in
|
||||
https://github.com/google-gemini/gemini-cli/pull/14885
|
||||
- feat: launch Gemini 3 Flash in Gemini CLI ⚡️⚡️⚡️ by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15196
|
||||
- Refactor: Migrate console.error in ripGrep.ts to debugLogger by @Adib234 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15201
|
||||
- chore: update a2a-js to 0.3.7 by @adamfweidman in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15197
|
||||
- chore(core): remove redundant isModelAvailabilityServiceEnabled toggle and
|
||||
clean up dead code by @adamfweidman in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15207
|
||||
- feat(core): Late resolve `GenerateContentConfig`s and reduce mutation. by
|
||||
@joshualitt in https://github.com/google-gemini/gemini-cli/pull/14920
|
||||
- Respect previewFeatures value from the remote flag if undefined by @sehoon38
|
||||
in https://github.com/google-gemini/gemini-cli/pull/15214
|
||||
- feat(ui): add Windows clipboard image support and Alt+V paste workaround by
|
||||
@jacob314 in https://github.com/google-gemini/gemini-cli/pull/15218
|
||||
- chore(core): remove legacy fallback flags and migrate loop detection by
|
||||
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/15213
|
||||
- fix(ui): Prevent eager slash command completion hiding sibling commands by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15224
|
||||
- Docs: Update Changelog for Dec 17, 2025 by @jkcinouye in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15204
|
||||
- Code Assist backend telemetry for user accept/reject of suggestions by
|
||||
@gundermanc in https://github.com/google-gemini/gemini-cli/pull/15206
|
||||
- fix(cli): correct initial history length handling for chat commands by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15223
|
||||
- chore/release: bump version to 0.21.0-nightly.20251218.739c02bd6 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15231
|
||||
- Change detailed model stats to use a new shared Table class to resolve
|
||||
robustness issues. by @jacob314 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15208
|
||||
- feat: add agent toml parser by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15112
|
||||
- Add core tool that adds all context from the core package. by @jacob314 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15238
|
||||
- (docs): Add reference section to hooks documentation by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15159
|
||||
- feat(hooks): add support for friendly names and descriptions by @abhipatel12
|
||||
in https://github.com/google-gemini/gemini-cli/pull/15174
|
||||
- feat: Detect background color by @jacob314 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15132
|
||||
- add 3.0 to allowed sensitive keywords by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15276
|
||||
- feat: Pass additional environment variables to shell execution by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15160
|
||||
- Remove unused code by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15290
|
||||
- Handle all 429 as retryableQuotaError by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15288
|
||||
- Remove unnecessary dependencies by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15291
|
||||
- fix: prevent infinite loop in prompt completion on error by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/14548
|
||||
- fix(ui): show command suggestions even on perfect match and sort them by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15287
|
||||
- feat(hooks): reduce log verbosity and improve error reporting in UI by
|
||||
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15297
|
||||
- feat: simplify tool confirmation labels for better UX by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15296
|
||||
- chore/release: bump version to 0.21.0-nightly.20251219.70696e364 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15301
|
||||
- feat(core): Implement JIT context memory loading and UI sync by @SandyTao520
|
||||
in https://github.com/google-gemini/gemini-cli/pull/14469
|
||||
- feat(ui): Put "Allow for all future sessions" behind a setting off by default.
|
||||
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/15322
|
||||
- fix(cli):change the placeholder of input during the shell mode by
|
||||
@JayadityaGit in https://github.com/google-gemini/gemini-cli/pull/15135
|
||||
- Validate OAuth resource parameter matches MCP server URL by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15289
|
||||
- docs(cli): add System Prompt Override (GEMINI_SYSTEM_MD) by @ashmod in
|
||||
https://github.com/google-gemini/gemini-cli/pull/9515
|
||||
- more robust command parsing logs by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15339
|
||||
- Introspection agent demo by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15232
|
||||
- fix(core): sanitize hook command expansion and prevent injection by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15343
|
||||
- fix(folder trust): add validation for trusted folder level by @adamfweidman in
|
||||
https://github.com/google-gemini/gemini-cli/pull/12215
|
||||
- fix(cli): fix right border overflow in trust dialogs by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15350
|
||||
- fix(policy): fix bug where accepting-edits continued after it was turned off
|
||||
by @jacob314 in https://github.com/google-gemini/gemini-cli/pull/15351
|
||||
- fix: prevent infinite relaunch loop when --resume fails (#14941) by @Ying-xi
|
||||
in https://github.com/google-gemini/gemini-cli/pull/14951
|
||||
- chore/release: bump version to 0.21.0-nightly.20251220.41a1a3eed by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15352
|
||||
- feat(telemetry): add clearcut logging for hooks by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15405
|
||||
- fix(core): Add `.geminiignore` support to SearchText tool by @xyrolle in
|
||||
https://github.com/google-gemini/gemini-cli/pull/13763
|
||||
- fix(patch): cherry-pick 0843d9a to release/v0.23.0-preview.0-pr-15443 to patch
|
||||
version v0.23.0-preview.0 and create version 0.23.0-preview.1 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15445
|
||||
- fix(patch): cherry-pick 9cdb267 to release/v0.23.0-preview.1-pr-15494 to patch
|
||||
version v0.23.0-preview.1 and create version 0.23.0-preview.2 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15592
|
||||
- fix(patch): cherry-pick 37be162 to release/v0.23.0-preview.2-pr-15601 to patch
|
||||
version v0.23.0-preview.2 and create version 0.23.0-preview.3 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15603
|
||||
- fix(patch): cherry-pick 07e597d to release/v0.23.0-preview.3-pr-15684
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15734
|
||||
- fix(patch): cherry-pick c31f053 to release/v0.23.0-preview.4-pr-16004 to patch
|
||||
version v0.23.0-preview.4 and create version 0.23.0-preview.5 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/16027
|
||||
- fix(patch): cherry-pick 788bb04 to release/v0.23.0-preview.5-pr-15817
|
||||
[CONFLICTS] by @gemini-cli-robot in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16038
|
||||
|
||||
**Full changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.22.5...v0.23.0
|
||||
@@ -0,0 +1,224 @@
|
||||
# Preview release: Release v0.24.0-preview.0
|
||||
|
||||
Released: January 6, 2026
|
||||
|
||||
Our preview release includes the latest, new, and experimental features. This
|
||||
release may not be as stable as our [latest weekly release](latest.md).
|
||||
|
||||
To install the preview release:
|
||||
|
||||
```
|
||||
npm install -g @google/gemini-cli@preview
|
||||
```
|
||||
|
||||
## What's changed
|
||||
|
||||
- chore(core): refactor model resolution and cleanup fallback logic by
|
||||
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/15228
|
||||
- Add Folder Trust Support To Hooks by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15325
|
||||
- Record timestamp with code assist metrics. by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15439
|
||||
- feat(policy): implement dynamic mode-aware policy evaluation by @abhipatel12
|
||||
in https://github.com/google-gemini/gemini-cli/pull/15307
|
||||
- fix(core): use debugLogger.debug for startup profiler logs by @NTaylorMullen
|
||||
in https://github.com/google-gemini/gemini-cli/pull/15443
|
||||
- feat(ui): Add security warning and improve layout for Hooks list by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15440
|
||||
- fix #15369, prevent crash on unhandled EIO error in readStdin cleanup by
|
||||
@ElecTwix in https://github.com/google-gemini/gemini-cli/pull/15410
|
||||
- chore: improve error messages for --resume by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15360
|
||||
- chore: remove clipboard file by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15447
|
||||
- Implemented unified secrets sanitization and env. redaction options by
|
||||
@gundermanc in https://github.com/google-gemini/gemini-cli/pull/15348
|
||||
- feat: automatic `/model` persistence across Gemini CLI sessions by @niyasrad
|
||||
in https://github.com/google-gemini/gemini-cli/pull/13199
|
||||
- refactor(core): remove deprecated permission aliases from BeforeToolHookOutput
|
||||
by @StoyanD in https://github.com/google-gemini/gemini-cli/pull/14855
|
||||
- fix: add missing `type` field to MCPServerConfig by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15465
|
||||
- Make schema validation errors non-fatal by @jacob314 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15487
|
||||
- chore: limit MCP resources display to 10 by default by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15489
|
||||
- Add experimental in-CLI extension install and uninstall subcommands by
|
||||
@chrstnb in https://github.com/google-gemini/gemini-cli/pull/15178
|
||||
- feat: Add A2A Client Manager and tests by @adamfweidman in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15485
|
||||
- feat: terse transformations of image paths in text buffer by @psinha40898 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/4924
|
||||
- Security: Project-level hook warnings by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15470
|
||||
- Added modifyOtherKeys protocol support for tmux by @ved015 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15524
|
||||
- chore(core): fix comment typo by @Mapleeeeeeeeeee in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15558
|
||||
- feat: Show snowfall animation for holiday theme by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15494
|
||||
- do not persist the fallback model by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15483
|
||||
- Resolve unhandled promise rejection in ide-client.ts by @Adib234 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15587
|
||||
- fix(core): handle checkIsRepo failure in GitService.initialize by
|
||||
@Mapleeeeeeeeeee in https://github.com/google-gemini/gemini-cli/pull/15574
|
||||
- fix(cli): add enableShellOutputEfficiency to settings schema by
|
||||
@Mapleeeeeeeeeee in https://github.com/google-gemini/gemini-cli/pull/15560
|
||||
- Manual nightly version bump to 0.24.0-nightly.20251226.546baf993 by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15594
|
||||
- refactor(core): extract static concerns from CoreToolScheduler by @abhipatel12
|
||||
in https://github.com/google-gemini/gemini-cli/pull/15589
|
||||
- fix(core): enable granular shell command allowlisting in policy engine by
|
||||
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15601
|
||||
- chore/release: bump version to 0.24.0-nightly.20251227.37be16243 by
|
||||
@gemini-cli-robot in https://github.com/google-gemini/gemini-cli/pull/15612
|
||||
- refactor: deprecate legacy confirmation settings and enforce Policy Engine by
|
||||
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15626
|
||||
- Migrate console to coreEvents.emitFeedback or debugLogger by @Adib234 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15219
|
||||
- Exponential back-off retries for retryable error without a specified … by
|
||||
@sehoon38 in https://github.com/google-gemini/gemini-cli/pull/15684
|
||||
- feat(agents): add support for remote agents and multi-agent TOML files by
|
||||
@adamfweidman in https://github.com/google-gemini/gemini-cli/pull/15437
|
||||
- Update wittyPhrases.ts by @segyges in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15697
|
||||
- refactor(auth): Refactor non-interactive mode auth validation & refresh by
|
||||
@skeshive in https://github.com/google-gemini/gemini-cli/pull/15679
|
||||
- Revert "Update wittyPhrases.ts (#15697)" by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15719
|
||||
- fix(hooks): deduplicate agent hooks and add cross-platform integration tests
|
||||
by @abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15701
|
||||
- Implement support for tool input modification by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15492
|
||||
- Add instructions to the extensions update info notification by @chrstnb in
|
||||
https://github.com/google-gemini/gemini-cli/pull/14907
|
||||
- Add extension settings info to /extensions list by @chrstnb in
|
||||
https://github.com/google-gemini/gemini-cli/pull/14905
|
||||
- Agent Skills: Implement Core Skill Infrastructure & Tiered Discovery by
|
||||
@NTaylorMullen in https://github.com/google-gemini/gemini-cli/pull/15698
|
||||
- chore: remove cot style comments by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15735
|
||||
- feat(agents): Add remote agents to agent registry by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15711
|
||||
- feat(hooks): implement STOP_EXECUTION and enhance hook decision handling by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15685
|
||||
- Fix build issues caused by year-specific linter rule by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15780
|
||||
- fix(core): handle unhandled promise rejection in mcp-client-manager by
|
||||
@kamja44 in https://github.com/google-gemini/gemini-cli/pull/14701
|
||||
- log fallback mode by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15817
|
||||
- Agent Skills: Implement Autonomous Activation Tool & Context Injection by
|
||||
@NTaylorMullen in https://github.com/google-gemini/gemini-cli/pull/15725
|
||||
- fix(core): improve shell command with redirection detection by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15683
|
||||
- Add security docs by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15739
|
||||
- feat: add folder suggestions to `/dir add` command by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15724
|
||||
- Agent Skills: Implement Agent Integration and System Prompt Awareness by
|
||||
@NTaylorMullen in https://github.com/google-gemini/gemini-cli/pull/15728
|
||||
- chore: cleanup old smart edit settings by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15832
|
||||
- Agent Skills: Status Bar Integration for Skill Counts by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15741
|
||||
- fix(core): mock powershell output in shell-utils test by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15831
|
||||
- Agent Skills: Unify Representation & Centralize Loading by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15833
|
||||
- Unify shell security policy and remove legacy logic by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15770
|
||||
- feat(core): restore MessageBus optionality for soft migration (Phase 1) by
|
||||
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15774
|
||||
- feat(core): Standardize Tool and Agent Invocation constructors (Phase 2) by
|
||||
@abhipatel12 in https://github.com/google-gemini/gemini-cli/pull/15775
|
||||
- feat(core,cli): enforce mandatory MessageBus injection (Phase 3 Hard
|
||||
Migration) by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15776
|
||||
- Agent Skills: Extension Support & Security Disclosure by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15834
|
||||
- feat(hooks): implement granular stop and block behavior for agent hooks by
|
||||
@SandyTao520 in https://github.com/google-gemini/gemini-cli/pull/15824
|
||||
- Agent Skills: Add gemini skills CLI management command by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15837
|
||||
- refactor: consolidate EditTool and SmartEditTool by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15857
|
||||
- fix(cli): mock fs.readdir in consent tests for Windows compatibility by
|
||||
@NTaylorMullen in https://github.com/google-gemini/gemini-cli/pull/15904
|
||||
- refactor(core): Extract and integrate ToolExecutor by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15900
|
||||
- Fix terminal hang when user exits browser without logging in by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15748
|
||||
- fix: avoid SDK warning by not accessing .text getter in logging by @ved015 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15706
|
||||
- Make default settings apply by @devr0306 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15354
|
||||
- chore: rename smart-edit to edit by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15923
|
||||
- Opt-in to persist model from /model by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15820
|
||||
- fix: prevent /copy crash on Windows by skipping /dev/tty by @ManojINaik in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15657
|
||||
- Support context injection via SessionStart hook. by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15746
|
||||
- Fix order of preflight by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15941
|
||||
- Fix failing unit tests by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15940
|
||||
- fix(cli): resolve paste issue on Windows terminals. by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15932
|
||||
- Agent Skills: Implement /skills reload by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15865
|
||||
- Add setting to support OSC 52 paste by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15336
|
||||
- remove manual string when displaying manual model in the footer by @sehoon38
|
||||
in https://github.com/google-gemini/gemini-cli/pull/15967
|
||||
- fix(core): use correct interactive check for system prompt by @ppergame in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15020
|
||||
- Inform user of missing settings on extensions update by @chrstnb in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15944
|
||||
- feat(policy): allow 'modes' in user and admin policies by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15977
|
||||
- fix: default folder trust to untrusted for enhanced security by @galz10 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15943
|
||||
- Add description for each settings item in /settings by @sehoon38 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15936
|
||||
- Use GetOperation to poll for OnboardUser completion by @ishaanxgupta in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15827
|
||||
- Agent Skills: Add skill directory to WorkspaceContext upon activation by
|
||||
@NTaylorMullen in https://github.com/google-gemini/gemini-cli/pull/15870
|
||||
- Fix settings command fallback by @chrstnb in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15926
|
||||
- fix: writeTodo construction by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16014
|
||||
- properly disable keyboard modes on exit by @scidomino in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16006
|
||||
- Add workflow to label child issues for rollup by @bdmorgan in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16002
|
||||
- feat(ui): add visual indicators for hook execution by @abhipatel12 in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15408
|
||||
- fix: image token estimation by @jackwotherspoon in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16004
|
||||
- feat(hooks): Add a hooks.enabled setting. by @joshualitt in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15933
|
||||
- feat(admin): Introduce remote admin settings & implement
|
||||
secureModeEnabled/mcpEnabled by @skeshive in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15935
|
||||
- Remove trailing whitespace in yaml. by @joshualitt in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16036
|
||||
- feat(agents): add support for remote agents by @adamfweidman in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16013
|
||||
- fix: limit scheduled issue triage queries to prevent argument list too long
|
||||
error by @jerop in https://github.com/google-gemini/gemini-cli/pull/16021
|
||||
- ci(github-actions): triage all new issues automatically by @jerop in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16018
|
||||
- Fix test. by @gundermanc in
|
||||
https://github.com/google-gemini/gemini-cli/pull/16011
|
||||
- fix: hide broken skills object from settings dialog by @korade-krushna in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15766
|
||||
- Agent Skills: Initial Documentation & Tutorial by @NTaylorMullen in
|
||||
https://github.com/google-gemini/gemini-cli/pull/15869
|
||||
|
||||
**Full changelog**:
|
||||
https://github.com/google-gemini/gemini-cli/compare/v0.23.0-preview.6...v0.24.0-preview.0
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,77 +0,0 @@
|
||||
# Checkpointing
|
||||
|
||||
The Gemini CLI includes a Checkpointing feature that automatically saves a snapshot of your project's state before any file modifications are made by AI-powered tools. This allows you to safely experiment with and apply code changes, knowing you can instantly revert back to the state before the tool was run.
|
||||
|
||||
## How It Works
|
||||
|
||||
When you approve a tool that modifies the file system (like `write_file` or `replace`), the CLI automatically creates a "checkpoint." This checkpoint includes:
|
||||
|
||||
1. **A Git Snapshot:** A commit is made in a special, shadow Git repository located in your home directory (`~/.gemini/history/<project_hash>`). This snapshot captures the complete state of your project files at that moment. It does **not** interfere with your own project's Git repository.
|
||||
2. **Conversation History:** The entire conversation you've had with the agent up to that point is saved.
|
||||
3. **The Tool Call:** The specific tool call that was about to be executed is also stored.
|
||||
|
||||
If you want to undo the change or simply go back, you can use the `/restore` command. Restoring a checkpoint will:
|
||||
|
||||
- Revert all files in your project to the state captured in the snapshot.
|
||||
- Restore the conversation history in the CLI.
|
||||
- Re-propose the original tool call, allowing you to run it again, modify it, or simply ignore it.
|
||||
|
||||
All checkpoint data, including the Git snapshot and conversation history, is stored locally on your machine. The Git snapshot is stored in the shadow repository while the conversation history and tool calls are saved in a JSON file in your project's temporary directory, typically located at `~/.gemini/tmp/<project_hash>/checkpoints`.
|
||||
|
||||
## Enabling the Feature
|
||||
|
||||
The Checkpointing feature is disabled by default. To enable it, you can either use a command-line flag or edit your `settings.json` file.
|
||||
|
||||
### Using the Command-Line Flag
|
||||
|
||||
You can enable checkpointing for the current session by using the `--checkpointing` flag when starting the Gemini CLI:
|
||||
|
||||
```bash
|
||||
gemini --checkpointing
|
||||
```
|
||||
|
||||
### Using the `settings.json` File
|
||||
|
||||
To enable checkpointing by default for all sessions, you need to edit your `settings.json` file.
|
||||
|
||||
Add the following key to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"checkpointing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the `/restore` Command
|
||||
|
||||
Once enabled, checkpoints are created automatically. To manage them, you use the `/restore` command.
|
||||
|
||||
### List Available Checkpoints
|
||||
|
||||
To see a list of all saved checkpoints for the current project, simply run:
|
||||
|
||||
```
|
||||
/restore
|
||||
```
|
||||
|
||||
The CLI will display a list of available checkpoint files. These file names are typically composed of a timestamp, the name of the file being modified, and the name of the tool that was about to be run (e.g., `2025-06-22T10-00-00_000Z-my-file.txt-write_file`).
|
||||
|
||||
### Restore a Specific Checkpoint
|
||||
|
||||
To restore your project to a specific checkpoint, use the checkpoint file from the list:
|
||||
|
||||
```
|
||||
/restore <checkpoint_file>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
/restore 2025-06-22T10-00-00_000Z-my-file.txt-write_file
|
||||
```
|
||||
|
||||
After running the command, your files and conversation will be immediately restored to the state they were in when the checkpoint was created, and the original tool prompt will reappear.
|
||||
+2
-175
@@ -1,176 +1,3 @@
|
||||
# Authentication Setup
|
||||
# Authentication setup
|
||||
|
||||
The Gemini CLI requires you to authenticate with Google's AI services. On initial startup you'll need to configure **one** of the following authentication methods:
|
||||
|
||||
1. **Login with Google (Gemini Code Assist):**
|
||||
- Use this option to log in with your Google account.
|
||||
- During initial startup, Gemini CLI will direct you to a webpage for authentication. Once authenticated, your credentials will be cached locally so the web login can be skipped on subsequent runs.
|
||||
- Note that the web login must be done in a browser that can communicate with the machine Gemini CLI is being run from. (Specifically, the browser will be redirected to a localhost url that Gemini CLI will be listening on).
|
||||
- <a id="workspace-gca">Users may have to specify a GOOGLE_CLOUD_PROJECT if:</a>
|
||||
1. You have a Google Workspace account. Google Workspace is a paid service for businesses and organizations that provides a suite of productivity tools, including a custom email domain (e.g. your-name@your-company.com), enhanced security features, and administrative controls. These accounts are often managed by an employer or school.
|
||||
1. You have received a Gemini Code Assist license through the [Google Developer Program](https://developers.google.com/program/plans-and-pricing) (including qualified Google Developer Experts)
|
||||
1. You have been assigned a license to a current Gemini Code Assist standard or enterprise subscription.
|
||||
1. You are using the product outside the [supported regions](https://developers.google.com/gemini-code-assist/resources/available-locations) for free individual usage.
|
||||
1. You are a Google account holder under the age of 18
|
||||
- If you fall into one of these categories, you must first configure a Google Cloud Project ID to use, [enable the Gemini for Cloud API](https://cloud.google.com/gemini/docs/discover/set-up-gemini#enable-api) and [configure access permissions](https://cloud.google.com/gemini/docs/discover/set-up-gemini#grant-iam).
|
||||
|
||||
You can temporarily set the environment variable in your current shell session using the following command:
|
||||
|
||||
```bash
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
```
|
||||
- For repeated use, you can add the environment variable to your [.env file](#persisting-environment-variables-with-env-files) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
|
||||
|
||||
```bash
|
||||
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
2. **<a id="gemini-api-key"></a>Gemini API key:**
|
||||
- Obtain your API key from Google AI Studio: [https://aistudio.google.com/app/apikey](https://aistudio.google.com/app/apikey)
|
||||
- Set the `GEMINI_API_KEY` environment variable. In the following methods, replace `YOUR_GEMINI_API_KEY` with the API key you obtained from Google AI Studio:
|
||||
- You can temporarily set the environment variable in your current shell session using the following command:
|
||||
```bash
|
||||
export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"
|
||||
```
|
||||
- For repeated use, you can add the environment variable to your [.env file](#persisting-environment-variables-with-env-files).
|
||||
|
||||
- Alternatively you can export the API key from your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
|
||||
|
||||
```bash
|
||||
echo 'export GEMINI_API_KEY="YOUR_GEMINI_API_KEY"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
:warning: Be advised that when you export your API key inside your shell configuration file, any other process executed from the shell can read it.
|
||||
|
||||
3. **Vertex AI:**
|
||||
- **API Key:**
|
||||
- Obtain your Google Cloud API key: [Get an API Key](https://cloud.google.com/vertex-ai/generative-ai/docs/start/api-keys?usertype=newuser)
|
||||
- Set the `GOOGLE_API_KEY` environment variable. In the following methods, replace `YOUR_GOOGLE_API_KEY` with your Vertex AI API key:
|
||||
- You can temporarily set the environment variable in your current shell session using the following command:
|
||||
```bash
|
||||
export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"
|
||||
```
|
||||
- For repeated use, you can add the environment variable to your [.env file](#persisting-environment-variables-with-env-files) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following command adds the environment variable to a `~/.bashrc` file:
|
||||
|
||||
```bash
|
||||
echo 'export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
:warning: Be advised that when you export your API key inside your shell configuration file, any other process executed from the shell can read it.
|
||||
|
||||
> **Note:**
|
||||
> If you encounter an error like `"API keys are not supported by this API - Expected OAuth2 access token or other authentication credentials that assert a principal"`, it is likely that your organization has restricted the creation of service account API keys. In this case, please try the [service account JSON key](#service-account-json-key) method described below.
|
||||
|
||||
- **Application Default Credentials (ADC):**
|
||||
|
||||
> **Note:**
|
||||
> If you have previously set the `GOOGLE_API_KEY` or `GEMINI_API_KEY` environment variables, you must unset them to use Application Default Credentials.
|
||||
>
|
||||
> ```bash
|
||||
> unset GOOGLE_API_KEY GEMINI_API_KEY
|
||||
> ```
|
||||
- **Using `gcloud` (for local development):**
|
||||
- Ensure you have a Google Cloud project and have enabled the Vertex AI API.
|
||||
- Log in with your user credentials:
|
||||
```bash
|
||||
gcloud auth application-default login
|
||||
```
|
||||
For more information, see [Set up Application Default Credentials for Google Cloud](https://cloud.google.com/docs/authentication/provide-credentials-adc).
|
||||
- **<a id="service-account-json-key"></a>Using a Service Account (for applications or when service account API keys are restricted):**
|
||||
- If you are unable to create an API key due to [organization policies](https://cloud.google.com/vertex-ai/generative-ai/docs/start/api-keys?usertype=existinguser#expandable-2), or if you are running in a non-interactive environment, you can authenticate using a service account key.
|
||||
- [Create a service account and key](https://cloud.google.com/iam/docs/keys-create-delete), and download the JSON key file. The service account will need to be assigned the "Vertex AI User" role.
|
||||
- Set the `GOOGLE_APPLICATION_CREDENTIALS` environment variable to the absolute path of the JSON file.
|
||||
- You can temporarily set the environment variable in your current shell session:
|
||||
```bash
|
||||
export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"
|
||||
```
|
||||
- For repeated use, you can add the command to your shell's configuration file (e.g., `~/.bashrc`).
|
||||
```bash
|
||||
echo 'export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/keyfile.json"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
:warning: Be advised that when you export service account credentials inside your shell configuration file, any other process executed from the shell can read it.
|
||||
|
||||
- **Required Environment Variables for ADC:**
|
||||
- When using ADC (either with `gcloud` or a service account), you must also set the `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION` environment variables. In the following methods, replace `YOUR_PROJECT_ID` and `YOUR_PROJECT_LOCATION` with the relevant values for your project:
|
||||
- You can temporarily set these environment variables in your current shell session using the following commands:
|
||||
```bash
|
||||
export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"
|
||||
export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION" # e.g., us-central1
|
||||
```
|
||||
- For repeated use, you can add the environment variables to your [.env file](#persisting-environment-variables-with-env-files) or your shell's configuration file (like `~/.bashrc`, `~/.zshrc`, or `~/.profile`). For example, the following commands add the environment variables to a `~/.bashrc` file:
|
||||
```bash
|
||||
echo 'export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"' >> ~/.bashrc
|
||||
echo 'export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"' >> ~/.bashrc
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
4. **Cloud Shell:**
|
||||
- This option is only available when running in a Google Cloud Shell environment.
|
||||
- It automatically uses the credentials of the logged-in user in the Cloud Shell environment.
|
||||
- This is the default authentication method when running in Cloud Shell and no other method is configured.
|
||||
|
||||
:warning: Be advised that when you export your API key inside your shell configuration file, any other process executed from the shell can read it.
|
||||
|
||||
### Persisting Environment Variables with `.env` Files
|
||||
|
||||
You can create a **`.gemini/.env`** file in your project directory or in your home directory. Creating a plain **`.env`** file also works, but `.gemini/.env` is recommended to keep Gemini variables isolated from other tools.
|
||||
|
||||
**Important:** Some environment variables (like `DEBUG` and `DEBUG_MODE`) are automatically excluded from project `.env` files to prevent interference with gemini-cli behavior. Use `.gemini/.env` files for gemini-cli specific variables.
|
||||
|
||||
Gemini CLI automatically loads environment variables from the **first** `.env` file it finds, using the following search order:
|
||||
|
||||
1. Starting in the **current directory** and moving upward toward `/`, for each directory it checks:
|
||||
1. `.gemini/.env`
|
||||
2. `.env`
|
||||
2. If no file is found, it falls back to your **home directory**:
|
||||
- `~/.gemini/.env`
|
||||
- `~/.env`
|
||||
|
||||
> **Important:** The search stops at the **first** file encountered—variables are **not merged** across multiple files.
|
||||
|
||||
#### Examples
|
||||
|
||||
**Project-specific overrides** (take precedence when you are inside the project):
|
||||
|
||||
```bash
|
||||
mkdir -p .gemini
|
||||
echo 'GOOGLE_CLOUD_PROJECT="your-project-id"' >> .gemini/.env
|
||||
```
|
||||
|
||||
**User-wide settings** (available in every directory):
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.gemini
|
||||
cat >> ~/.gemini/.env <<'EOF'
|
||||
GOOGLE_CLOUD_PROJECT="your-project-id"
|
||||
GEMINI_API_KEY="your-gemini-api-key"
|
||||
EOF
|
||||
```
|
||||
|
||||
## Non-Interactive Mode / Headless Environments
|
||||
|
||||
When running the Gemini CLI in a non-interactive environment, you cannot use the interactive login flow.
|
||||
Instead, you must configure authentication using environment variables.
|
||||
|
||||
The CLI will automatically detect if it is running in a non-interactive terminal and will use one of the
|
||||
following authentication methods if available:
|
||||
|
||||
1. **Gemini API Key:**
|
||||
- Set the `GEMINI_API_KEY` environment variable.
|
||||
- The CLI will use this key to authenticate with the Gemini API.
|
||||
|
||||
2. **Vertex AI:**
|
||||
- Set the `GOOGLE_GENAI_USE_VERTEXAI=true` environment variable.
|
||||
- **Using an API Key:** Set the `GOOGLE_API_KEY` environment variable.
|
||||
- **Using Application Default Credentials (ADC):**
|
||||
- Run `gcloud auth application-default login` in your environment to configure ADC.
|
||||
- Ensure the `GOOGLE_CLOUD_PROJECT` and `GOOGLE_CLOUD_LOCATION` environment variables are set.
|
||||
|
||||
If none of these environment variables are set in a non-interactive session, the CLI will exit with an error.
|
||||
|
||||
For comprehensive guidance on using Gemini CLI programmatically and in
|
||||
automation workflows, see the [Headless Mode Guide](../headless.md).
|
||||
See: [Getting Started - Authentication Setup](../get-started/authentication.md).
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
# Checkpointing
|
||||
|
||||
The Gemini CLI includes a Checkpointing feature that automatically saves a
|
||||
snapshot of your project's state before any file modifications are made by
|
||||
AI-powered tools. This allows you to safely experiment with and apply code
|
||||
changes, knowing you can instantly revert back to the state before the tool was
|
||||
run.
|
||||
|
||||
## How it works
|
||||
|
||||
When you approve a tool that modifies the file system (like `write_file` or
|
||||
`replace`), the CLI automatically creates a "checkpoint." This checkpoint
|
||||
includes:
|
||||
|
||||
1. **A Git snapshot:** A commit is made in a special, shadow Git repository
|
||||
located in your home directory (`~/.gemini/history/<project_hash>`). This
|
||||
snapshot captures the complete state of your project files at that moment.
|
||||
It does **not** interfere with your own project's Git repository.
|
||||
2. **Conversation history:** The entire conversation you've had with the agent
|
||||
up to that point is saved.
|
||||
3. **The tool call:** The specific tool call that was about to be executed is
|
||||
also stored.
|
||||
|
||||
If you want to undo the change or simply go back, you can use the `/restore`
|
||||
command. Restoring a checkpoint will:
|
||||
|
||||
- Revert all files in your project to the state captured in the snapshot.
|
||||
- Restore the conversation history in the CLI.
|
||||
- Re-propose the original tool call, allowing you to run it again, modify it, or
|
||||
simply ignore it.
|
||||
|
||||
All checkpoint data, including the Git snapshot and conversation history, is
|
||||
stored locally on your machine. The Git snapshot is stored in the shadow
|
||||
repository while the conversation history and tool calls are saved in a JSON
|
||||
file in your project's temporary directory, typically located at
|
||||
`~/.gemini/tmp/<project_hash>/checkpoints`.
|
||||
|
||||
## Enabling the feature
|
||||
|
||||
The Checkpointing feature is disabled by default. To enable it, you need to edit
|
||||
your `settings.json` file.
|
||||
|
||||
> **Note:** The `--checkpointing` command-line flag was removed in version
|
||||
> 0.11.0. Checkpointing can now only be enabled through the `settings.json`
|
||||
> configuration file.
|
||||
|
||||
Add the following key to your `settings.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"checkpointing": {
|
||||
"enabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Using the `/restore` command
|
||||
|
||||
Once enabled, checkpoints are created automatically. To manage them, you use the
|
||||
`/restore` command.
|
||||
|
||||
### List available checkpoints
|
||||
|
||||
To see a list of all saved checkpoints for the current project, simply run:
|
||||
|
||||
```
|
||||
/restore
|
||||
```
|
||||
|
||||
The CLI will display a list of available checkpoint files. These file names are
|
||||
typically composed of a timestamp, the name of the file being modified, and the
|
||||
name of the tool that was about to be run (e.g.,
|
||||
`2025-06-22T10-00-00_000Z-my-file.txt-write_file`).
|
||||
|
||||
### Restore a specific checkpoint
|
||||
|
||||
To restore your project to a specific checkpoint, use the checkpoint file from
|
||||
the list:
|
||||
|
||||
```
|
||||
/restore <checkpoint_file>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```
|
||||
/restore 2025-06-22T10-00-00_000Z-my-file.txt-write_file
|
||||
```
|
||||
|
||||
After running the command, your files and conversation will be immediately
|
||||
restored to the state they were in when the checkpoint was created, and the
|
||||
original tool prompt will reappear.
|
||||
+254
-316
@@ -1,6 +1,8 @@
|
||||
# CLI Commands
|
||||
# CLI commands
|
||||
|
||||
Gemini CLI supports several built-in commands to help you manage your session, customize the interface, and control its behavior. These commands are prefixed with a forward slash (`/`), an at symbol (`@`), or an exclamation mark (`!`).
|
||||
Gemini CLI supports several built-in commands to help you manage your session,
|
||||
customize the interface, and control its behavior. These commands are prefixed
|
||||
with a forward slash (`/`), an at symbol (`@`), or an exclamation mark (`!`).
|
||||
|
||||
## Slash commands (`/`)
|
||||
|
||||
@@ -9,423 +11,352 @@ Slash commands provide meta-level control over the CLI itself.
|
||||
### Built-in Commands
|
||||
|
||||
- **`/bug`**
|
||||
- **Description:** File an issue about Gemini CLI. By default, the issue is filed within the GitHub repository for Gemini CLI. The string you enter after `/bug` will become the headline for the bug being filed. The default `/bug` behavior can be modified using the `advanced.bugCommand` setting in your `.gemini/settings.json` files.
|
||||
- **Description:** File an issue about Gemini CLI. By default, the issue is
|
||||
filed within the GitHub repository for Gemini CLI. The string you enter
|
||||
after `/bug` will become the headline for the bug being filed. The default
|
||||
`/bug` behavior can be modified using the `advanced.bugCommand` setting in
|
||||
your `.gemini/settings.json` files.
|
||||
|
||||
- **`/chat`**
|
||||
- **Description:** Save and resume conversation history for branching conversation state interactively, or resuming a previous state from a later session.
|
||||
- **Description:** Save and resume conversation history for branching
|
||||
conversation state interactively, or resuming a previous state from a later
|
||||
session.
|
||||
- **Sub-commands:**
|
||||
- **`save`**
|
||||
- **Description:** Saves the current conversation history. You must add a `<tag>` for identifying the conversation state.
|
||||
- **Description:** Saves the current conversation history. You must add a
|
||||
`<tag>` for identifying the conversation state.
|
||||
- **Usage:** `/chat save <tag>`
|
||||
- **Details on Checkpoint Location:** The default locations for saved chat checkpoints are:
|
||||
- **Details on checkpoint location:** The default locations for saved chat
|
||||
checkpoints are:
|
||||
- Linux/macOS: `~/.gemini/tmp/<project_hash>/`
|
||||
- Windows: `C:\Users\<YourUsername>\.gemini\tmp\<project_hash>\`
|
||||
- When you run `/chat list`, the CLI only scans these specific directories to find available checkpoints.
|
||||
- **Note:** These checkpoints are for manually saving and resuming conversation states. For automatic checkpoints created before file modifications, see the [Checkpointing documentation](../checkpointing.md).
|
||||
- **Behavior:** Chats are saved into a project-specific directory,
|
||||
determined by where you run the CLI. Consequently, saved chats are
|
||||
only accessible when working within that same project.
|
||||
- **Note:** These checkpoints are for manually saving and resuming
|
||||
conversation states. For automatic checkpoints created before file
|
||||
modifications, see the
|
||||
[Checkpointing documentation](../cli/checkpointing.md).
|
||||
- **`resume`**
|
||||
- **Description:** Resumes a conversation from a previous save.
|
||||
- **Usage:** `/chat resume <tag>`
|
||||
- **Note:** You can only resume chats that were saved within the current
|
||||
project. To resume a chat from a different project, you must run the
|
||||
Gemini CLI from that project's directory.
|
||||
- **`list`**
|
||||
- **Description:** Lists available tags for chat state resumption.
|
||||
- **Note:** This command only lists chats saved within the current
|
||||
project. Because chat history is project-scoped, chats saved in other
|
||||
project directories will not be displayed.
|
||||
- **`delete`**
|
||||
- **Description:** Deletes a saved conversation checkpoint.
|
||||
- **Usage:** `/chat delete <tag>`
|
||||
- **`share`**
|
||||
- **Description** Writes the current conversation to a provided Markdown or JSON file.
|
||||
- **Usage** `/chat share file.md` or `/chat share file.json`. If no filename is provided, then the CLI will generate one.
|
||||
- **Description** Writes the current conversation to a provided Markdown
|
||||
or JSON file.
|
||||
- **Usage** `/chat share file.md` or `/chat share file.json`. If no
|
||||
filename is provided, then the CLI will generate one.
|
||||
|
||||
- **`/clear`**
|
||||
- **Description:** Clear the terminal screen, including the visible session history and scrollback within the CLI. The underlying session data (for history recall) might be preserved depending on the exact implementation, but the visual display is cleared.
|
||||
- **Keyboard shortcut:** Press **Ctrl+L** at any time to perform a clear action.
|
||||
- **Description:** Clear the terminal screen, including the visible session
|
||||
history and scrollback within the CLI. The underlying session data (for
|
||||
history recall) might be preserved depending on the exact implementation,
|
||||
but the visual display is cleared.
|
||||
- **Keyboard shortcut:** Press **Ctrl+L** at any time to perform a clear
|
||||
action.
|
||||
|
||||
- **`/compress`**
|
||||
- **Description:** Replace the entire chat context with a summary. This saves on tokens used for future tasks while retaining a high level summary of what has happened.
|
||||
- **Description:** Replace the entire chat context with a summary. This saves
|
||||
on tokens used for future tasks while retaining a high level summary of what
|
||||
has happened.
|
||||
|
||||
- **`/copy`**
|
||||
- **Description:** Copies the last output produced by Gemini CLI to your clipboard, for easy sharing or reuse.
|
||||
- **Note:** This command requires platform-specific clipboard tools to be installed.
|
||||
- On Linux, it requires `xclip` or `xsel`. You can typically install them using your system's package manager.
|
||||
- On macOS, it requires `pbcopy`, and on Windows, it requires `clip`. These tools are typically pre-installed on their respective systems.
|
||||
- **Description:** Copies the last output produced by Gemini CLI to your
|
||||
clipboard, for easy sharing or reuse.
|
||||
- **Behavior:**
|
||||
- Local sessions use system clipboard tools (pbcopy/xclip/clip).
|
||||
- Remote sessions (SSH/WSL) use OSC 52 and require terminal support.
|
||||
- **Note:** This command requires platform-specific clipboard tools to be
|
||||
installed.
|
||||
- On Linux, it requires `xclip` or `xsel`. You can typically install them
|
||||
using your system's package manager.
|
||||
- On macOS, it requires `pbcopy`, and on Windows, it requires `clip`. These
|
||||
tools are typically pre-installed on their respective systems.
|
||||
|
||||
- **`/directory`** (or **`/dir`**)
|
||||
- **Description:** Manage workspace directories for multi-directory support.
|
||||
- **Sub-commands:**
|
||||
- **`add`**:
|
||||
- **Description:** Add a directory to the workspace. The path can be absolute or relative to the current working directory. Moreover, the reference from home directory is supported as well.
|
||||
- **Description:** Add a directory to the workspace. The path can be
|
||||
absolute or relative to the current working directory. Moreover, the
|
||||
reference from home directory is supported as well.
|
||||
- **Usage:** `/directory add <path1>,<path2>`
|
||||
- **Note:** Disabled in restrictive sandbox profiles. If you're using that, use `--include-directories` when starting the session instead.
|
||||
- **Note:** Disabled in restrictive sandbox profiles. If you're using
|
||||
that, use `--include-directories` when starting the session instead.
|
||||
- **`show`**:
|
||||
- **Description:** Display all directories added by `/directory add` and `--include-directories`.
|
||||
- **Description:** Display all directories added by `/directory add` and
|
||||
`--include-directories`.
|
||||
- **Usage:** `/directory show`
|
||||
|
||||
- **`/editor`**
|
||||
- **Description:** Open a dialog for selecting supported editors.
|
||||
|
||||
- **`/extensions`**
|
||||
- **Description:** Lists all active extensions in the current Gemini CLI session. See [Gemini CLI Extensions](../extension.md).
|
||||
- **Description:** Lists all active extensions in the current Gemini CLI
|
||||
session. See [Gemini CLI Extensions](../extensions/index.md).
|
||||
|
||||
- **`/help`** (or **`/?`**)
|
||||
- **Description:** Display help information about Gemini CLI, including available commands and their usage.
|
||||
- **Description:** Display help information about Gemini CLI, including
|
||||
available commands and their usage.
|
||||
|
||||
- **`/mcp`**
|
||||
- **Description:** List configured Model Context Protocol (MCP) servers, their connection status, server details, and available tools.
|
||||
- **Description:** Manage configured Model Context Protocol (MCP) servers.
|
||||
- **Sub-commands:**
|
||||
- **`desc`** or **`descriptions`**:
|
||||
- **Description:** Show detailed descriptions for MCP servers and tools.
|
||||
- **`nodesc`** or **`nodescriptions`**:
|
||||
- **Description:** Hide tool descriptions, showing only the tool names.
|
||||
- **`list`** or **`ls`**:
|
||||
- **Description:** List configured MCP servers and tools. This is the
|
||||
default action if no subcommand is specified.
|
||||
- **`desc`**
|
||||
- **Description:** List configured MCP servers and tools with
|
||||
descriptions.
|
||||
- **`schema`**:
|
||||
- **Description:** Show the full JSON schema for the tool's configured parameters.
|
||||
- **Keyboard Shortcut:** Press **Ctrl+T** at any time to toggle between showing and hiding tool descriptions.
|
||||
- **Description:** List configured MCP servers and tools with descriptions
|
||||
and schemas.
|
||||
- **`auth`**:
|
||||
- **Description:** Authenticate with an OAuth-enabled MCP server.
|
||||
- **Usage:** `/mcp auth <server-name>`
|
||||
- **Details:** If `<server-name>` is provided, it initiates the OAuth flow
|
||||
for that server. If no server name is provided, it lists all configured
|
||||
servers that support OAuth authentication.
|
||||
- **`refresh`**:
|
||||
- **Description:** Restarts all MCP servers and re-discovers their
|
||||
available tools.
|
||||
|
||||
- [**`/model`**](./model.md)
|
||||
- **Description:** Opens a dialog to choose your Gemini model.
|
||||
|
||||
- **`/memory`**
|
||||
- **Description:** Manage the AI's instructional context (hierarchical memory loaded from `GEMINI.md` files).
|
||||
- **Description:** Manage the AI's instructional context (hierarchical memory
|
||||
loaded from `GEMINI.md` files).
|
||||
- **Sub-commands:**
|
||||
- **`add`**:
|
||||
- **Description:** Adds the following text to the AI's memory. Usage: `/memory add <text to remember>`
|
||||
- **Description:** Adds the following text to the AI's memory. Usage:
|
||||
`/memory add <text to remember>`
|
||||
- **`show`**:
|
||||
- **Description:** Display the full, concatenated content of the current hierarchical memory that has been loaded from all `GEMINI.md` files. This lets you inspect the instructional context being provided to the Gemini model.
|
||||
- **Description:** Display the full, concatenated content of the current
|
||||
hierarchical memory that has been loaded from all `GEMINI.md` files.
|
||||
This lets you inspect the instructional context being provided to the
|
||||
Gemini model.
|
||||
- **`refresh`**:
|
||||
- **Description:** Reload the hierarchical instructional memory from all `GEMINI.md` files found in the configured locations (global, project/ancestors, and sub-directories). This command updates the model with the latest `GEMINI.md` content.
|
||||
- **Note:** For more details on how `GEMINI.md` files contribute to hierarchical memory, see the [CLI Configuration documentation](./configuration.md#4-geminimd-files-hierarchical-instructional-context).
|
||||
- **Description:** Reload the hierarchical instructional memory from all
|
||||
`GEMINI.md` files found in the configured locations (global,
|
||||
project/ancestors, and sub-directories). This command updates the model
|
||||
with the latest `GEMINI.md` content.
|
||||
- **`list`**:
|
||||
- **Description:** Lists the paths of the GEMINI.md files in use for
|
||||
hierarchical memory.
|
||||
- **Note:** For more details on how `GEMINI.md` files contribute to
|
||||
hierarchical memory, see the
|
||||
[CLI Configuration documentation](../get-started/configuration.md).
|
||||
|
||||
- **`/restore`**
|
||||
- **Description:** Restores the project files to the state they were in just before a tool was executed. This is particularly useful for undoing file edits made by a tool. If run without a tool call ID, it will list available checkpoints to restore from.
|
||||
- **Description:** Restores the project files to the state they were in just
|
||||
before a tool was executed. This is particularly useful for undoing file
|
||||
edits made by a tool. If run without a tool call ID, it will list available
|
||||
checkpoints to restore from.
|
||||
- **Usage:** `/restore [tool_call_id]`
|
||||
- **Note:** Only available if the CLI is invoked with the `--checkpointing` option or configured via [settings](./configuration.md). See [Checkpointing documentation](../checkpointing.md) for more details.
|
||||
- **Note:** Only available if checkpointing is configured via
|
||||
[settings](../get-started/configuration.md). See
|
||||
[Checkpointing documentation](../cli/checkpointing.md) for more details.
|
||||
|
||||
- **`/settings`**
|
||||
- **Description:** Open the settings editor to view and modify Gemini CLI settings.
|
||||
- **Details:** This command provides a user-friendly interface for changing settings that control the behavior and appearance of Gemini CLI. It is equivalent to manually editing the `.gemini/settings.json` file, but with validation and guidance to prevent errors.
|
||||
- **Usage:** Simply run `/settings` and the editor will open. You can then browse or search for specific settings, view their current values, and modify them as desired. Changes to some settings are applied immediately, while others require a restart.
|
||||
- [**`/rewind`**](./rewind.md)
|
||||
- **Description:** Browse and rewind previous interactions. Allows you to
|
||||
rewind the conversation, revert file changes, or both. Provides an
|
||||
interactive interface to select the exact point to rewind to.
|
||||
- **Keyboard shortcut:** Press **Esc** twice.
|
||||
|
||||
- **`/resume`**
|
||||
- **Description:** Browse and resume previous conversation sessions. Opens an
|
||||
interactive session browser where you can search, filter, and select from
|
||||
automatically saved conversations.
|
||||
- **Features:**
|
||||
- **Session Browser:** Interactive interface showing all saved sessions with
|
||||
timestamps, message counts, and first user message for context
|
||||
- **Search:** Use `/` to search through conversation content across all
|
||||
sessions
|
||||
- **Sorting:** Sort sessions by date or message count
|
||||
- **Management:** Delete unwanted sessions directly from the browser
|
||||
- **Resume:** Select any session to resume and continue the conversation
|
||||
- **Note:** All conversations are automatically saved as you chat - no manual
|
||||
saving required. See [Session Management](../cli/session-management.md) for
|
||||
complete details.
|
||||
|
||||
- [**`/settings`**](./settings.md)
|
||||
- **Description:** Open the settings editor to view and modify Gemini CLI
|
||||
settings.
|
||||
- **Details:** This command provides a user-friendly interface for changing
|
||||
settings that control the behavior and appearance of Gemini CLI. It is
|
||||
equivalent to manually editing the `.gemini/settings.json` file, but with
|
||||
validation and guidance to prevent errors. See the
|
||||
[settings documentation](./settings.md) for a full list of available
|
||||
settings.
|
||||
- **Usage:** Simply run `/settings` and the editor will open. You can then
|
||||
browse or search for specific settings, view their current values, and
|
||||
modify them as desired. Changes to some settings are applied immediately,
|
||||
while others require a restart.
|
||||
|
||||
- [**`/skills`**](./skills.md)
|
||||
- **Description:** (Experimental) Manage Agent Skills, which provide on-demand
|
||||
expertise and specialized workflows.
|
||||
- **Sub-commands:**
|
||||
- **`list`**:
|
||||
- **Description:** List all discovered skills and their current status
|
||||
(enabled/disabled).
|
||||
- **`enable`**:
|
||||
- **Description:** Enable a specific skill by name.
|
||||
- **Usage:** `/skills enable <name>`
|
||||
- **`disable`**:
|
||||
- **Description:** Disable a specific skill by name.
|
||||
- **Usage:** `/skills disable <name>`
|
||||
- **`reload`**:
|
||||
- **Description:** Refresh the list of discovered skills from all tiers
|
||||
(workspace, user, and extensions).
|
||||
|
||||
- **`/stats`**
|
||||
- **Description:** Display detailed statistics for the current Gemini CLI session, including token usage, cached token savings (when available), and session duration. Note: Cached token information is only displayed when cached tokens are being used, which occurs with API key authentication but not with OAuth authentication at this time.
|
||||
- **Description:** Display detailed statistics for the current Gemini CLI
|
||||
session, including token usage, cached token savings (when available), and
|
||||
session duration. Note: Cached token information is only displayed when
|
||||
cached tokens are being used, which occurs with API key authentication but
|
||||
not with OAuth authentication at this time.
|
||||
|
||||
- [**`/theme`**](./themes.md)
|
||||
- **Description:** Open a dialog that lets you change the visual theme of Gemini CLI.
|
||||
- **Description:** Open a dialog that lets you change the visual theme of
|
||||
Gemini CLI.
|
||||
|
||||
- **`/auth`**
|
||||
- **Description:** Open a dialog that lets you change the authentication method.
|
||||
- **Description:** Open a dialog that lets you change the authentication
|
||||
method.
|
||||
|
||||
- **`/about`**
|
||||
- **Description:** Show version info. Please share this information when filing issues.
|
||||
- **Description:** Show version info. Please share this information when
|
||||
filing issues.
|
||||
|
||||
- [**`/tools`**](../tools/index.md)
|
||||
- **Description:** Display a list of tools that are currently available within Gemini CLI.
|
||||
- **Description:** Display a list of tools that are currently available within
|
||||
Gemini CLI.
|
||||
- **Usage:** `/tools [desc]`
|
||||
- **Sub-commands:**
|
||||
- **`desc`** or **`descriptions`**:
|
||||
- **Description:** Show detailed descriptions of each tool, including each tool's name with its full description as provided to the model.
|
||||
- **Description:** Show detailed descriptions of each tool, including each
|
||||
tool's name with its full description as provided to the model.
|
||||
- **`nodesc`** or **`nodescriptions`**:
|
||||
- **Description:** Hide tool descriptions, showing only the tool names.
|
||||
|
||||
- **`/privacy`**
|
||||
- **Description:** Display the Privacy Notice and allow users to select whether they consent to the collection of their data for service improvement purposes.
|
||||
- **Description:** Display the Privacy Notice and allow users to select
|
||||
whether they consent to the collection of their data for service improvement
|
||||
purposes.
|
||||
|
||||
- **`/quit`** (or **`/exit`**)
|
||||
- **Description:** Exit Gemini CLI.
|
||||
|
||||
- **`/vim`**
|
||||
- **Description:** Toggle vim mode on or off. When vim mode is enabled, the input area supports vim-style navigation and editing commands in both NORMAL and INSERT modes.
|
||||
- **Description:** Toggle vim mode on or off. When vim mode is enabled, the
|
||||
input area supports vim-style navigation and editing commands in both NORMAL
|
||||
and INSERT modes.
|
||||
- **Features:**
|
||||
- **NORMAL mode:** Navigate with `h`, `j`, `k`, `l`; jump by words with `w`, `b`, `e`; go to line start/end with `0`, `$`, `^`; go to specific lines with `G` (or `gg` for first line)
|
||||
- **NORMAL mode:** Navigate with `h`, `j`, `k`, `l`; jump by words with `w`,
|
||||
`b`, `e`; go to line start/end with `0`, `$`, `^`; go to specific lines
|
||||
with `G` (or `gg` for first line)
|
||||
- **INSERT mode:** Standard text input with escape to return to NORMAL mode
|
||||
- **Editing commands:** Delete with `x`, change with `c`, insert with `i`, `a`, `o`, `O`; complex operations like `dd`, `cc`, `dw`, `cw`
|
||||
- **Editing commands:** Delete with `x`, change with `c`, insert with `i`,
|
||||
`a`, `o`, `O`; complex operations like `dd`, `cc`, `dw`, `cw`
|
||||
- **Count support:** Prefix commands with numbers (e.g., `3h`, `5w`, `10G`)
|
||||
- **Repeat last command:** Use `.` to repeat the last editing operation
|
||||
- **Persistent setting:** Vim mode preference is saved to `~/.gemini/settings.json` and restored between sessions
|
||||
- **Status indicator:** When enabled, shows `[NORMAL]` or `[INSERT]` in the footer
|
||||
- **Persistent setting:** Vim mode preference is saved to
|
||||
`~/.gemini/settings.json` and restored between sessions
|
||||
- **Status indicator:** When enabled, shows `[NORMAL]` or `[INSERT]` in the
|
||||
footer
|
||||
|
||||
- **`/init`**
|
||||
- **Description:** To help users easily create a `GEMINI.md` file, this command analyzes the current directory and generates a tailored context file, making it simpler for them to provide project-specific instructions to the Gemini agent.
|
||||
- **Description:** To help users easily create a `GEMINI.md` file, this
|
||||
command analyzes the current directory and generates a tailored context
|
||||
file, making it simpler for them to provide project-specific instructions to
|
||||
the Gemini agent.
|
||||
|
||||
### Custom Commands
|
||||
### Custom commands
|
||||
|
||||
For a quick start, see the [example](#example-a-pure-function-refactoring-command) below.
|
||||
Custom commands allow you to create personalized shortcuts for your most-used
|
||||
prompts. For detailed instructions on how to create, manage, and use them,
|
||||
please see the dedicated [Custom Commands documentation](./custom-commands.md).
|
||||
|
||||
Custom commands allow you to save and reuse your favorite or most frequently used prompts as personal shortcuts within Gemini CLI. You can create commands that are specific to a single project or commands that are available globally across all your projects, streamlining your workflow and ensuring consistency.
|
||||
|
||||
#### File Locations & Precedence
|
||||
|
||||
Gemini CLI discovers commands from two locations, loaded in a specific order:
|
||||
|
||||
1. **User Commands (Global):** Located in `~/.gemini/commands/`. These commands are available in any project you are working on.
|
||||
2. **Project Commands (Local):** Located in `<your-project-root>/.gemini/commands/`. These commands are specific to the current project and can be checked into version control to be shared with your team.
|
||||
|
||||
If a command in the project directory has the same name as a command in the user directory, the **project command will always be used.** This allows projects to override global commands with project-specific versions.
|
||||
|
||||
#### Naming and Namespacing
|
||||
|
||||
The name of a command is determined by its file path relative to its `commands` directory. Subdirectories are used to create namespaced commands, with the path separator (`/` or `\`) being converted to a colon (`:`).
|
||||
|
||||
- A file at `~/.gemini/commands/test.toml` becomes the command `/test`.
|
||||
- A file at `<project>/.gemini/commands/git/commit.toml` becomes the namespaced command `/git:commit`.
|
||||
|
||||
#### TOML File Format (v1)
|
||||
|
||||
Your command definition files must be written in the TOML format and use the `.toml` file extension.
|
||||
|
||||
##### Required Fields
|
||||
|
||||
- `prompt` (String): The prompt that will be sent to the Gemini model when the command is executed. This can be a single-line or multi-line string.
|
||||
|
||||
##### Optional Fields
|
||||
|
||||
- `description` (String): A brief, one-line description of what the command does. This text will be displayed next to your command in the `/help` menu. **If you omit this field, a generic description will be generated from the filename.**
|
||||
|
||||
#### Handling Arguments
|
||||
|
||||
Custom commands support two powerful methods for handling arguments. The CLI automatically chooses the correct method based on the content of your command's `prompt`.
|
||||
|
||||
##### 1. Context-Aware Injection with `{{args}}`
|
||||
|
||||
If your `prompt` contains the special placeholder `{{args}}`, the CLI will replace that placeholder with the text the user typed after the command name.
|
||||
|
||||
The behavior of this injection depends on where it is used:
|
||||
|
||||
**A. Raw Injection (Outside Shell Commands)**
|
||||
|
||||
When used in the main body of the prompt, the arguments are injected exactly as the user typed them.
|
||||
|
||||
**Example (`git/fix.toml`):**
|
||||
|
||||
```toml
|
||||
# Invoked via: /git:fix "Button is misaligned"
|
||||
|
||||
description = "Generates a fix for a given issue."
|
||||
prompt = "Please provide a code fix for the issue described here: {{args}}."
|
||||
```
|
||||
|
||||
The model receives: `Please provide a code fix for the issue described here: "Button is misaligned".`
|
||||
|
||||
**B. Using Arguments in Shell Commands (Inside `!{...}` Blocks)**
|
||||
|
||||
When you use `{{args}}` inside a shell injection block (`!{...}`), the arguments are automatically **shell-escaped** before replacement. This allows you to safely pass arguments to shell commands, ensuring the resulting command is syntactically correct and secure while preventing command injection vulnerabilities.
|
||||
|
||||
**Example (`/grep-code.toml`):**
|
||||
|
||||
```toml
|
||||
prompt = """
|
||||
Please summarize the findings for the pattern `{{args}}`.
|
||||
|
||||
Search Results:
|
||||
!{grep -r {{args}} .}
|
||||
"""
|
||||
```
|
||||
|
||||
When you run `/grep-code It's complicated`:
|
||||
|
||||
1. The CLI sees `{{args}}` used both outside and inside `!{...}`.
|
||||
2. Outside: The first `{{args}}` is replaced raw with `It's complicated`.
|
||||
3. Inside: The second `{{args}}` is replaced with the escaped version (e.g., on Linux: `"It's complicated"`).
|
||||
4. The command executed is `grep -r "It's complicated" .`.
|
||||
5. The CLI prompts you to confirm this exact, secure command before execution.
|
||||
6. The final prompt is sent.
|
||||
|
||||
##### 2. Default Argument Handling
|
||||
|
||||
If your `prompt` does **not** contain the special placeholder `{{args}}`, the CLI uses a default behavior for handling arguments.
|
||||
|
||||
If you provide arguments to the command (e.g., `/mycommand arg1`), the CLI will append the full command you typed to the end of the prompt, separated by two newlines. This allows the model to see both the original instructions and the specific arguments you just provided.
|
||||
|
||||
If you do **not** provide any arguments (e.g., `/mycommand`), the prompt is sent to the model exactly as it is, with nothing appended.
|
||||
|
||||
**Example (`changelog.toml`):**
|
||||
|
||||
This example shows how to create a robust command by defining a role for the model, explaining where to find the user's input, and specifying the expected format and behavior.
|
||||
|
||||
```toml
|
||||
# In: <project>/.gemini/commands/changelog.toml
|
||||
# Invoked via: /changelog 1.2.0 added "Support for default argument parsing."
|
||||
|
||||
description = "Adds a new entry to the project's CHANGELOG.md file."
|
||||
prompt = """
|
||||
# Task: Update Changelog
|
||||
|
||||
You are an expert maintainer of this software project. A user has invoked a command to add a new entry to the changelog.
|
||||
|
||||
**The user's raw command is appended below your instructions.**
|
||||
|
||||
Your task is to parse the `<version>`, `<change_type>`, and `<message>` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file.
|
||||
|
||||
## Expected Format
|
||||
The command follows this format: `/changelog <version> <type> <message>`
|
||||
- `<type>` must be one of: "added", "changed", "fixed", "removed".
|
||||
|
||||
## Behavior
|
||||
1. Read the `CHANGELOG.md` file.
|
||||
2. Find the section for the specified `<version>`.
|
||||
3. Add the `<message>` under the correct `<type>` heading.
|
||||
4. If the version or type section doesn't exist, create it.
|
||||
5. Adhere strictly to the "Keep a Changelog" format.
|
||||
"""
|
||||
```
|
||||
|
||||
When you run `/changelog 1.2.0 added "New feature"`, the final text sent to the model will be the original prompt followed by two newlines and the command you typed.
|
||||
|
||||
##### 3. Executing Shell Commands with `!{...}`
|
||||
|
||||
You can make your commands dynamic by executing shell commands directly within your `prompt` and injecting their output. This is ideal for gathering context from your local environment, like reading file content or checking the status of Git.
|
||||
|
||||
When a custom command attempts to execute a shell command, Gemini CLI will now prompt you for confirmation before proceeding. This is a security measure to ensure that only intended commands can be run.
|
||||
|
||||
**How It Works:**
|
||||
|
||||
1. **Inject Commands:** Use the `!{...}` syntax.
|
||||
2. **Argument Substitution:** If `{{args}}` is present inside the block, it is automatically shell-escaped (see [Context-Aware Injection](#1-context-aware-injection-with-args) above).
|
||||
3. **Robust Parsing:** The parser correctly handles complex shell commands that include nested braces, such as JSON payloads. **Note:** The content inside `!{...}` must have balanced braces (`{` and `}`). If you need to execute a command containing unbalanced braces, consider wrapping it in an external script file and calling the script within the `!{...}` block.
|
||||
4. **Security Check and Confirmation:** The CLI performs a security check on the final, resolved command (after arguments are escaped and substituted). A dialog will appear showing the exact command(s) to be executed.
|
||||
5. **Execution and Error Reporting:** The command is executed. If the command fails, the output injected into the prompt will include the error messages (stderr) followed by a status line, e.g., `[Shell command exited with code 1]`. This helps the model understand the context of the failure.
|
||||
|
||||
**Example (`git/commit.toml`):**
|
||||
|
||||
This command gets the staged git diff and uses it to ask the model to write a commit message.
|
||||
|
||||
````toml
|
||||
# In: <project>/.gemini/commands/git/commit.toml
|
||||
# Invoked via: /git:commit
|
||||
|
||||
description = "Generates a Git commit message based on staged changes."
|
||||
|
||||
# The prompt uses !{...} to execute the command and inject its output.
|
||||
prompt = """
|
||||
Please generate a Conventional Commit message based on the following git diff:
|
||||
|
||||
```diff
|
||||
!{git diff --staged}
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
````
|
||||
|
||||
When you run `/git:commit`, the CLI first executes `git diff --staged`, then replaces `!{git diff --staged}` with the output of that command before sending the final, complete prompt to the model.
|
||||
|
||||
##### 4. Injecting File Content with `@{...}`
|
||||
|
||||
You can directly embed the content of a file or a directory listing into your prompt using the `@{...}` syntax. This is useful for creating commands that operate on specific files.
|
||||
|
||||
**How It Works:**
|
||||
|
||||
- **File Injection**: `@{path/to/file.txt}` is replaced by the content of `file.txt`.
|
||||
- **Multimodal Support**: If the path points to a supported image (e.g., PNG, JPEG), PDF, audio, or video file, it will be correctly encoded and injected as multimodal input. Other binary files are handled gracefully and skipped.
|
||||
- **Directory Listing**: `@{path/to/dir}` is traversed and each file present within the directory and all subdirectories are inserted into the prompt. This respects `.gitignore` and `.geminiignore` if enabled.
|
||||
- **Workspace-Aware**: The command searches for the path in the current directory and any other workspace directories. Absolute paths are allowed if they are within the workspace.
|
||||
- **Processing Order**: File content injection with `@{...}` is processed _before_ shell commands (`!{...}`) and argument substitution (`{{args}}`).
|
||||
- **Parsing**: The parser requires the content inside `@{...}` (the path) to have balanced braces (`{` and `}`).
|
||||
|
||||
**Example (`review.toml`):**
|
||||
|
||||
This command injects the content of a _fixed_ best practices file (`docs/best-practices.md`) and uses the user's arguments to provide context for the review.
|
||||
|
||||
```toml
|
||||
# In: <project>/.gemini/commands/review.toml
|
||||
# Invoked via: /review FileCommandLoader.ts
|
||||
|
||||
description = "Reviews the provided context using a best practice guide."
|
||||
prompt = """
|
||||
You are an expert code reviewer.
|
||||
|
||||
Your task is to review {{args}}.
|
||||
|
||||
Use the following best practices when providing your review:
|
||||
|
||||
@{docs/best-practices.md}
|
||||
"""
|
||||
```
|
||||
|
||||
When you run `/review FileCommandLoader.ts`, the `@{docs/best-practices.md}` placeholder is replaced by the content of that file, and `{{args}}` is replaced by the text you provided, before the final prompt is sent to the model.
|
||||
|
||||
---
|
||||
|
||||
#### Example: A "Pure Function" Refactoring Command
|
||||
|
||||
Let's create a global command that asks the model to refactor a piece of code.
|
||||
|
||||
**1. Create the file and directories:**
|
||||
|
||||
First, ensure the user commands directory exists, then create a `refactor` subdirectory for organization and the final TOML file.
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.gemini/commands/refactor
|
||||
touch ~/.gemini/commands/refactor/pure.toml
|
||||
```
|
||||
|
||||
**2. Add the content to the file:**
|
||||
|
||||
Open `~/.gemini/commands/refactor/pure.toml` in your editor and add the following content. We are including the optional `description` for best practice.
|
||||
|
||||
```toml
|
||||
# In: ~/.gemini/commands/refactor/pure.toml
|
||||
# This command will be invoked via: /refactor:pure
|
||||
|
||||
description = "Asks the model to refactor the current context into a pure function."
|
||||
|
||||
prompt = """
|
||||
Please analyze the code I've provided in the current context.
|
||||
Refactor it into a pure function.
|
||||
|
||||
Your response should include:
|
||||
1. The refactored, pure function code block.
|
||||
2. A brief explanation of the key changes you made and why they contribute to purity.
|
||||
"""
|
||||
```
|
||||
|
||||
**3. Run the Command:**
|
||||
|
||||
That's it! You can now run your command in the CLI. First, you might add a file to the context, and then invoke your command:
|
||||
|
||||
```
|
||||
> @my-messy-function.js
|
||||
> /refactor:pure
|
||||
```
|
||||
|
||||
Gemini CLI will then execute the multi-line prompt defined in your TOML file.
|
||||
|
||||
## Input Prompt Shortcuts
|
||||
## Input prompt shortcuts
|
||||
|
||||
These shortcuts apply directly to the input prompt for text manipulation.
|
||||
|
||||
- **Undo:**
|
||||
- **Keyboard shortcut:** Press **Ctrl+z** to undo the last action in the input prompt.
|
||||
- **Keyboard shortcut:** Press **Ctrl+z** to undo the last action in the input
|
||||
prompt.
|
||||
|
||||
- **Redo:**
|
||||
- **Keyboard shortcut:** Press **Ctrl+Shift+Z** to redo the last undone action in the input prompt.
|
||||
- **Keyboard shortcut:** Press **Ctrl+Shift+Z** to redo the last undone action
|
||||
in the input prompt.
|
||||
|
||||
## At commands (`@`)
|
||||
|
||||
At commands are used to include the content of files or directories as part of your prompt to Gemini. These commands include git-aware filtering.
|
||||
At commands are used to include the content of files or directories as part of
|
||||
your prompt to Gemini. These commands include git-aware filtering.
|
||||
|
||||
- **`@<path_to_file_or_directory>`**
|
||||
- **Description:** Inject the content of the specified file or files into your current prompt. This is useful for asking questions about specific code, text, or collections of files.
|
||||
- **Description:** Inject the content of the specified file or files into your
|
||||
current prompt. This is useful for asking questions about specific code,
|
||||
text, or collections of files.
|
||||
- **Examples:**
|
||||
- `@path/to/your/file.txt Explain this text.`
|
||||
- `@src/my_project/ Summarize the code in this directory.`
|
||||
- `What is this file about? @README.md`
|
||||
- **Details:**
|
||||
- If a path to a single file is provided, the content of that file is read.
|
||||
- If a path to a directory is provided, the command attempts to read the content of files within that directory and any subdirectories.
|
||||
- Spaces in paths should be escaped with a backslash (e.g., `@My\ Documents/file.txt`).
|
||||
- The command uses the `read_many_files` tool internally. The content is fetched and then inserted into your query before being sent to the Gemini model.
|
||||
- **Git-aware filtering:** By default, git-ignored files (like `node_modules/`, `dist/`, `.env`, `.git/`) are excluded. This behavior can be changed via the `context.fileFiltering` settings.
|
||||
- **File types:** The command is intended for text-based files. While it might attempt to read any file, binary files or very large files might be skipped or truncated by the underlying `read_many_files` tool to ensure performance and relevance. The tool indicates if files were skipped.
|
||||
- **Output:** The CLI will show a tool call message indicating that `read_many_files` was used, along with a message detailing the status and the path(s) that were processed.
|
||||
- If a path to a directory is provided, the command attempts to read the
|
||||
content of files within that directory and any subdirectories.
|
||||
- Spaces in paths should be escaped with a backslash (e.g.,
|
||||
`@My\ Documents/file.txt`).
|
||||
- The command uses the `read_many_files` tool internally. The content is
|
||||
fetched and then inserted into your query before being sent to the Gemini
|
||||
model.
|
||||
- **Git-aware filtering:** By default, git-ignored files (like
|
||||
`node_modules/`, `dist/`, `.env`, `.git/`) are excluded. This behavior can
|
||||
be changed via the `context.fileFiltering` settings.
|
||||
- **File types:** The command is intended for text-based files. While it
|
||||
might attempt to read any file, binary files or very large files might be
|
||||
skipped or truncated by the underlying `read_many_files` tool to ensure
|
||||
performance and relevance. The tool indicates if files were skipped.
|
||||
- **Output:** The CLI will show a tool call message indicating that
|
||||
`read_many_files` was used, along with a message detailing the status and
|
||||
the path(s) that were processed.
|
||||
|
||||
- **`@` (Lone at symbol)**
|
||||
- **Description:** If you type a lone `@` symbol without a path, the query is passed as-is to the Gemini model. This might be useful if you are specifically talking _about_ the `@` symbol in your prompt.
|
||||
- **Description:** If you type a lone `@` symbol without a path, the query is
|
||||
passed as-is to the Gemini model. This might be useful if you are
|
||||
specifically talking _about_ the `@` symbol in your prompt.
|
||||
|
||||
### Error handling for `@` commands
|
||||
|
||||
- If the path specified after `@` is not found or is invalid, an error message will be displayed, and the query might not be sent to the Gemini model, or it will be sent without the file content.
|
||||
- If the `read_many_files` tool encounters an error (e.g., permission issues), this will also be reported.
|
||||
- If the path specified after `@` is not found or is invalid, an error message
|
||||
will be displayed, and the query might not be sent to the Gemini model, or it
|
||||
will be sent without the file content.
|
||||
- If the `read_many_files` tool encounters an error (e.g., permission issues),
|
||||
this will also be reported.
|
||||
|
||||
## Shell mode & passthrough commands (`!`)
|
||||
## Shell mode and passthrough commands (`!`)
|
||||
|
||||
The `!` prefix lets you interact with your system's shell directly from within Gemini CLI.
|
||||
The `!` prefix lets you interact with your system's shell directly from within
|
||||
Gemini CLI.
|
||||
|
||||
- **`!<shell_command>`**
|
||||
- **Description:** Execute the given `<shell_command>` using `bash` on Linux/macOS or `cmd.exe` on Windows. Any output or errors from the command are displayed in the terminal.
|
||||
- **Description:** Execute the given `<shell_command>` using `bash` on
|
||||
Linux/macOS or `powershell.exe -NoProfile -Command` on Windows (unless you
|
||||
override `ComSpec`). Any output or errors from the command are displayed in
|
||||
the terminal.
|
||||
- **Examples:**
|
||||
- `!ls -la` (executes `ls -la` and returns to Gemini CLI)
|
||||
- `!git status` (executes `git status` and returns to Gemini CLI)
|
||||
@@ -433,11 +364,18 @@ The `!` prefix lets you interact with your system's shell directly from within G
|
||||
- **`!` (Toggle shell mode)**
|
||||
- **Description:** Typing `!` on its own toggles shell mode.
|
||||
- **Entering shell mode:**
|
||||
- When active, shell mode uses a different coloring and a "Shell Mode Indicator".
|
||||
- While in shell mode, text you type is interpreted directly as a shell command.
|
||||
- When active, shell mode uses a different coloring and a "Shell Mode
|
||||
Indicator".
|
||||
- While in shell mode, text you type is interpreted directly as a shell
|
||||
command.
|
||||
- **Exiting shell mode:**
|
||||
- When exited, the UI reverts to its standard appearance and normal Gemini CLI behavior resumes.
|
||||
- When exited, the UI reverts to its standard appearance and normal Gemini
|
||||
CLI behavior resumes.
|
||||
|
||||
- **Caution for all `!` usage:** Commands you execute in shell mode have the same permissions and impact as if you ran them directly in your terminal.
|
||||
- **Caution for all `!` usage:** Commands you execute in shell mode have the
|
||||
same permissions and impact as if you ran them directly in your terminal.
|
||||
|
||||
- **Environment Variable:** When a command is executed via `!` or in shell mode, the `GEMINI_CLI=1` environment variable is set in the subprocess's environment. This allows scripts or tools to detect if they are being run from within the Gemini CLI.
|
||||
- **Environment variable:** When a command is executed via `!` or in shell mode,
|
||||
the `GEMINI_CLI=1` environment variable is set in the subprocess's
|
||||
environment. This allows scripts or tools to detect if they are being run from
|
||||
within the Gemini CLI.
|
||||
|
||||
@@ -1,634 +0,0 @@
|
||||
# Gemini CLI Configuration
|
||||
|
||||
**Note on New Configuration Format**
|
||||
|
||||
The format of the `settings.json` file has been updated to a new, more organized structure.
|
||||
|
||||
- The new format will be supported in the stable release starting **[09/10/25]**.
|
||||
- Automatic migration from the old format to the new format will begin on **[09/17/25]**.
|
||||
|
||||
For details on the previous format, please see the [v1 Configuration documentation](./configuration-v1.md).
|
||||
|
||||
Gemini CLI offers several ways to configure its behavior, including environment variables, command-line arguments, and settings files. This document outlines the different configuration methods and available settings.
|
||||
|
||||
## Configuration layers
|
||||
|
||||
Configuration is applied in the following order of precedence (lower numbers are overridden by higher numbers):
|
||||
|
||||
1. **Default values:** Hardcoded defaults within the application.
|
||||
2. **System defaults file:** System-wide default settings that can be overridden by other settings files.
|
||||
3. **User settings file:** Global settings for the current user.
|
||||
4. **Project settings file:** Project-specific settings.
|
||||
5. **System settings file:** System-wide settings that override all other settings files.
|
||||
6. **Environment variables:** System-wide or session-specific variables, potentially loaded from `.env` files.
|
||||
7. **Command-line arguments:** Values passed when launching the CLI.
|
||||
|
||||
## Settings files
|
||||
|
||||
Gemini CLI uses JSON settings files for persistent configuration. There are four locations for these files:
|
||||
|
||||
- **System defaults file:**
|
||||
- **Location:** `/etc/gemini-cli/system-defaults.json` (Linux), `C:\ProgramData\gemini-cli\system-defaults.json` (Windows) or `/Library/Application Support/GeminiCli/system-defaults.json` (macOS). The path can be overridden using the `GEMINI_CLI_SYSTEM_DEFAULTS_PATH` environment variable.
|
||||
- **Scope:** Provides a base layer of system-wide default settings. These settings have the lowest precedence and are intended to be overridden by user, project, or system override settings.
|
||||
- **User settings file:**
|
||||
- **Location:** `~/.gemini/settings.json` (where `~` is your home directory).
|
||||
- **Scope:** Applies to all Gemini CLI sessions for the current user. User settings override system defaults.
|
||||
- **Project settings file:**
|
||||
- **Location:** `.gemini/settings.json` within your project's root directory.
|
||||
- **Scope:** Applies only when running Gemini CLI from that specific project. Project settings override user settings and system defaults.
|
||||
- **System settings file:**
|
||||
- **Location:** `/etc/gemini-cli/settings.json` (Linux), `C:\ProgramData\gemini-cli\settings.json` (Windows) or `/Library/Application Support/GeminiCli/settings.json` (macOS). The path can be overridden using the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable.
|
||||
- **Scope:** Applies to all Gemini CLI sessions on the system, for all users. System settings act as overrides, taking precedence over all other settings files. May be useful for system administrators at enterprises to have controls over users' Gemini CLI setups.
|
||||
|
||||
**Note on environment variables in settings:** String values within your `settings.json` files can reference environment variables using either `$VAR_NAME` or `${VAR_NAME}` syntax. These variables will be automatically resolved when the settings are loaded. For example, if you have an environment variable `MY_API_TOKEN`, you could use it in `settings.json` like this: `"apiKey": "$MY_API_TOKEN"`.
|
||||
|
||||
> **Note for Enterprise Users:** For guidance on deploying and managing Gemini CLI in a corporate environment, please see the [Enterprise Configuration](./enterprise.md) documentation.
|
||||
|
||||
### The `.gemini` directory in your project
|
||||
|
||||
In addition to a project settings file, a project's `.gemini` directory can contain other project-specific files related to Gemini CLI's operation, such as:
|
||||
|
||||
- [Custom sandbox profiles](#sandboxing) (e.g., `.gemini/sandbox-macos-custom.sb`, `.gemini/sandbox.Dockerfile`).
|
||||
|
||||
### Available settings in `settings.json`
|
||||
|
||||
Settings are organized into categories. All settings should be placed within their corresponding top-level category object in your `settings.json` file.
|
||||
|
||||
#### `general`
|
||||
|
||||
- **`general.preferredEditor`** (string):
|
||||
- **Description:** The preferred editor to open files in.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`general.vimMode`** (boolean):
|
||||
- **Description:** Enable Vim keybindings.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`general.disableAutoUpdate`** (boolean):
|
||||
- **Description:** Disable automatic updates.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`general.disableUpdateNag`** (boolean):
|
||||
- **Description:** Disable update notification prompts.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`general.checkpointing.enabled`** (boolean):
|
||||
- **Description:** Enable session checkpointing for recovery.
|
||||
- **Default:** `false`
|
||||
|
||||
#### `output`
|
||||
|
||||
- **`output.format`** (string):
|
||||
- **Description:** The format of the CLI output.
|
||||
- **Default:** `"text"`
|
||||
- **Values:** `"text"`, `"json"`
|
||||
|
||||
#### `ui`
|
||||
|
||||
- **`ui.theme`** (string):
|
||||
- **Description:** The color theme for the UI. See [Themes](./themes.md) for available options.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`ui.customThemes`** (object):
|
||||
- **Description:** Custom theme definitions.
|
||||
- **Default:** `{}`
|
||||
|
||||
- **`ui.hideWindowTitle`** (boolean):
|
||||
- **Description:** Hide the window title bar.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.hideTips`** (boolean):
|
||||
- **Description:** Hide helpful tips in the UI.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.hideBanner`** (boolean):
|
||||
- **Description:** Hide the application banner.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.hideFooter`** (boolean):
|
||||
- **Description:** Hide the footer from the UI.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.showMemoryUsage`** (boolean):
|
||||
- **Description:** Display memory usage information in the UI.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.showLineNumbers`** (boolean):
|
||||
- **Description:** Show line numbers in the chat.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ui.showCitations`** (boolean):
|
||||
- **Description:** Show citations for generated text in the chat.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`ui.accessibility.disableLoadingPhrases`** (boolean):
|
||||
- **Description:** Disable loading phrases for accessibility.
|
||||
- **Default:** `false`
|
||||
|
||||
#### `ide`
|
||||
|
||||
- **`ide.enabled`** (boolean):
|
||||
- **Description:** Enable IDE integration mode.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`ide.hasSeenNudge`** (boolean):
|
||||
- **Description:** Whether the user has seen the IDE integration nudge.
|
||||
- **Default:** `false`
|
||||
|
||||
#### `privacy`
|
||||
|
||||
- **`privacy.usageStatisticsEnabled`** (boolean):
|
||||
- **Description:** Enable collection of usage statistics.
|
||||
- **Default:** `true`
|
||||
|
||||
#### `model`
|
||||
|
||||
- **`model.name`** (string):
|
||||
- **Description:** The Gemini model to use for conversations.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`model.maxSessionTurns`** (number):
|
||||
- **Description:** Maximum number of user/model/tool turns to keep in a session. -1 means unlimited.
|
||||
- **Default:** `-1`
|
||||
|
||||
- **`model.summarizeToolOutput`** (object):
|
||||
- **Description:** Enables or disables the summarization of tool output. You can specify the token budget for the summarization using the `tokenBudget` setting. Note: Currently only the `run_shell_command` tool is supported. For example `{"run_shell_command": {"tokenBudget": 2000}}`
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`model.chatCompression.contextPercentageThreshold`** (number):
|
||||
- **Description:** Sets the threshold for chat history compression as a percentage of the model's total token limit. This is a value between 0 and 1 that applies to both automatic compression and the manual `/compress` command. For example, a value of `0.6` will trigger compression when the chat history exceeds 60% of the token limit.
|
||||
- **Default:** `0.7`
|
||||
|
||||
- **`model.skipNextSpeakerCheck`** (boolean):
|
||||
- **Description:** Skip the next speaker check.
|
||||
- **Default:** `false`
|
||||
|
||||
#### `context`
|
||||
|
||||
- **`context.fileName`** (string or array of strings):
|
||||
- **Description:** The name of the context file(s).
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`context.importFormat`** (string):
|
||||
- **Description:** The format to use when importing memory.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`context.discoveryMaxDirs`** (number):
|
||||
- **Description:** Maximum number of directories to search for memory.
|
||||
- **Default:** `200`
|
||||
|
||||
- **`context.includeDirectories`** (array):
|
||||
- **Description:** Additional directories to include in the workspace context. Missing directories will be skipped with a warning.
|
||||
- **Default:** `[]`
|
||||
|
||||
- **`context.loadFromIncludeDirectories`** (boolean):
|
||||
- **Description:** Controls the behavior of the `/memory refresh` command. If set to `true`, `GEMINI.md` files should be loaded from all directories that are added. If set to `false`, `GEMINI.md` should only be loaded from the current directory.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`context.fileFiltering.respectGitIgnore`** (boolean):
|
||||
- **Description:** Respect .gitignore files when searching.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`context.fileFiltering.respectGeminiIgnore`** (boolean):
|
||||
- **Description:** Respect .geminiignore files when searching.
|
||||
- **Default:** `true`
|
||||
|
||||
- **`context.fileFiltering.enableRecursiveFileSearch`** (boolean):
|
||||
- **Description:** Whether to enable searching recursively for filenames under the current tree when completing `@` prefixes in the prompt.
|
||||
- **Default:** `true`
|
||||
|
||||
#### `tools`
|
||||
|
||||
- **`tools.sandbox`** (boolean or string):
|
||||
- **Description:** Sandbox execution environment (can be a boolean or a path string).
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`tools.usePty`** (boolean):
|
||||
- **Description:** Use node-pty for shell command execution. Fallback to child_process still applies.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`tools.core`** (array of strings):
|
||||
- **Description:** This can be used to restrict the set of built-in tools [with an allowlist](./enterprise.md#restricting-tool-access). See [Built-in Tools](../core/tools-api.md#built-in-tools) for a list of core tools. The match semantics are the same as `tools.allowed`.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`tools.exclude`** (array of strings):
|
||||
- **Description:** Tool names to exclude from discovery.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`tools.allowed`** (array of strings):
|
||||
- **Description:** A list of tool names that will bypass the confirmation dialog. This is useful for tools that you trust and use frequently. For example, `["run_shell_command(git)", "run_shell_command(npm test)"]` will skip the confirmation dialog to run any `git` and `npm test` commands. See [Shell Tool command restrictions](../tools/shell.md#command-restrictions) for details on prefix matching, command chaining, etc.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`tools.discoveryCommand`** (string):
|
||||
- **Description:** Command to run for tool discovery.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`tools.callCommand`** (string):
|
||||
- **Description:** Defines a custom shell command for calling a specific tool that was discovered using `tools.discoveryCommand`. The shell command must meet the following criteria:
|
||||
- It must take function `name` (exactly as in [function declaration](https://ai.google.dev/gemini-api/docs/function-calling#function-declarations)) as first command line argument.
|
||||
- It must read function arguments as JSON on `stdin`, analogous to [`functionCall.args`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#functioncall).
|
||||
- It must return function output as JSON on `stdout`, analogous to [`functionResponse.response.content`](https://cloud.google.com/vertex-ai/generative-ai/docs/model-reference/inference#functionresponse).
|
||||
- **Default:** `undefined`
|
||||
|
||||
#### `mcp`
|
||||
|
||||
- **`mcp.serverCommand`** (string):
|
||||
- **Description:** Command to start an MCP server.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`mcp.allowed`** (array of strings):
|
||||
- **Description:** An allowlist of MCP servers to allow.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`mcp.excluded`** (array of strings):
|
||||
- **Description:** A denylist of MCP servers to exclude.
|
||||
- **Default:** `undefined`
|
||||
|
||||
#### `security`
|
||||
|
||||
- **`security.folderTrust.enabled`** (boolean):
|
||||
- **Description:** Setting to track whether Folder trust is enabled.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`security.auth.selectedType`** (string):
|
||||
- **Description:** The currently selected authentication type.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`security.auth.enforcedType`** (string):
|
||||
- **Description:** The required auth type (useful for enterprises).
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`security.auth.useExternal`** (boolean):
|
||||
- **Description:** Whether to use an external authentication flow.
|
||||
- **Default:** `undefined`
|
||||
|
||||
#### `advanced`
|
||||
|
||||
- **`advanced.autoConfigureMemory`** (boolean):
|
||||
- **Description:** Automatically configure Node.js memory limits.
|
||||
- **Default:** `false`
|
||||
|
||||
- **`advanced.dnsResolutionOrder`** (string):
|
||||
- **Description:** The DNS resolution order.
|
||||
- **Default:** `undefined`
|
||||
|
||||
- **`advanced.excludedEnvVars`** (array of strings):
|
||||
- **Description:** Environment variables to exclude from project context.
|
||||
- **Default:** `["DEBUG","DEBUG_MODE"]`
|
||||
|
||||
- **`advanced.bugCommand`** (object):
|
||||
- **Description:** Configuration for the bug report command.
|
||||
- **Default:** `undefined`
|
||||
|
||||
#### `mcpServers`
|
||||
|
||||
Configures connections to one or more Model-Context Protocol (MCP) servers for discovering and using custom tools. Gemini CLI attempts to connect to each configured MCP server to discover available tools. If multiple MCP servers expose a tool with the same name, the tool names will be prefixed with the server alias you defined in the configuration (e.g., `serverAlias__actualToolName`) to avoid conflicts. Note that the system might strip certain schema properties from MCP tool definitions for compatibility. At least one of `command`, `url`, or `httpUrl` must be provided. If multiple are specified, the order of precedence is `httpUrl`, then `url`, then `command`.
|
||||
|
||||
- **`mcpServers.<SERVER_NAME>`** (object): The server parameters for the named server.
|
||||
- `command` (string, optional): The command to execute to start the MCP server via standard I/O.
|
||||
- `args` (array of strings, optional): Arguments to pass to the command.
|
||||
- `env` (object, optional): Environment variables to set for the server process.
|
||||
- `cwd` (string, optional): The working directory in which to start the server.
|
||||
- `url` (string, optional): The URL of an MCP server that uses Server-Sent Events (SSE) for communication.
|
||||
- `httpUrl` (string, optional): The URL of an MCP server that uses streamable HTTP for communication.
|
||||
- `headers` (object, optional): A map of HTTP headers to send with requests to `url` or `httpUrl`.
|
||||
- `timeout` (number, optional): Timeout in milliseconds for requests to this MCP server.
|
||||
- `trust` (boolean, optional): Trust this server and bypass all tool call confirmations.
|
||||
- `description` (string, optional): A brief description of the server, which may be used for display purposes.
|
||||
- `includeTools` (array of strings, optional): List of tool names to include from this MCP server. When specified, only the tools listed here will be available from this server (allowlist behavior). If not specified, all tools from the server are enabled by default.
|
||||
- `excludeTools` (array of strings, optional): List of tool names to exclude from this MCP server. Tools listed here will not be available to the model, even if they are exposed by the server. **Note:** `excludeTools` takes precedence over `includeTools` - if a tool is in both lists, it will be excluded.
|
||||
|
||||
#### `telemetry`
|
||||
|
||||
Configures logging and metrics collection for Gemini CLI. For more information, see [Telemetry](../telemetry.md).
|
||||
|
||||
- **Properties:**
|
||||
- **`enabled`** (boolean): Whether or not telemetry is enabled.
|
||||
- **`target`** (string): The destination for collected telemetry. Supported values are `local` and `gcp`.
|
||||
- **`otlpEndpoint`** (string): The endpoint for the OTLP Exporter.
|
||||
- **`otlpProtocol`** (string): The protocol for the OTLP Exporter (`grpc` or `http`).
|
||||
- **`logPrompts`** (boolean): Whether or not to include the content of user prompts in the logs.
|
||||
- **`outfile`** (string): The file to write telemetry to when `target` is `local`.
|
||||
|
||||
### Example `settings.json`
|
||||
|
||||
Here is an example of a `settings.json` file with the nested structure, new as of v0.3.0:
|
||||
|
||||
```json
|
||||
{
|
||||
"general": {
|
||||
"vimMode": true,
|
||||
"preferredEditor": "code"
|
||||
},
|
||||
"ui": {
|
||||
"theme": "GitHub",
|
||||
"hideBanner": true,
|
||||
"hideTips": false
|
||||
},
|
||||
"tools": {
|
||||
"sandbox": "docker",
|
||||
"discoveryCommand": "bin/get_tools",
|
||||
"callCommand": "bin/call_tool",
|
||||
"exclude": ["write_file"]
|
||||
},
|
||||
"mcpServers": {
|
||||
"mainServer": {
|
||||
"command": "bin/mcp_server.py"
|
||||
},
|
||||
"anotherServer": {
|
||||
"command": "node",
|
||||
"args": ["mcp_server.js", "--verbose"]
|
||||
}
|
||||
},
|
||||
"telemetry": {
|
||||
"enabled": true,
|
||||
"target": "local",
|
||||
"otlpEndpoint": "http://localhost:4317",
|
||||
"logPrompts": true
|
||||
},
|
||||
"privacy": {
|
||||
"usageStatisticsEnabled": true
|
||||
},
|
||||
"model": {
|
||||
"name": "gemini-1.5-pro-latest",
|
||||
"maxSessionTurns": 10,
|
||||
"summarizeToolOutput": {
|
||||
"run_shell_command": {
|
||||
"tokenBudget": 100
|
||||
}
|
||||
}
|
||||
},
|
||||
"context": {
|
||||
"fileName": ["CONTEXT.md", "GEMINI.md"],
|
||||
"includeDirectories": ["path/to/dir1", "~/path/to/dir2", "../path/to/dir3"],
|
||||
"loadFromIncludeDirectories": true,
|
||||
"fileFiltering": {
|
||||
"respectGitIgnore": false
|
||||
}
|
||||
},
|
||||
"advanced": {
|
||||
"excludedEnvVars": ["DEBUG", "DEBUG_MODE", "NODE_ENV"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Shell History
|
||||
|
||||
The CLI keeps a history of shell commands you run. To avoid conflicts between different projects, this history is stored in a project-specific directory within your user's home folder.
|
||||
|
||||
- **Location:** `~/.gemini/tmp/<project_hash>/shell_history`
|
||||
- `<project_hash>` is a unique identifier generated from your project's root path.
|
||||
- The history is stored in a file named `shell_history`.
|
||||
|
||||
## Environment Variables & `.env` Files
|
||||
|
||||
Environment variables are a common way to configure applications, especially for sensitive information like API keys or for settings that might change between environments. For authentication setup, see the [Authentication documentation](./authentication.md) which covers all available authentication methods.
|
||||
|
||||
The CLI automatically loads environment variables from an `.env` file. The loading order is:
|
||||
|
||||
1. `.env` file in the current working directory.
|
||||
2. If not found, it searches upwards in parent directories until it finds an `.env` file or reaches the project root (identified by a `.git` folder) or the home directory.
|
||||
3. If still not found, it looks for `~/.env` (in the user's home directory).
|
||||
|
||||
**Environment Variable Exclusion:** Some environment variables (like `DEBUG` and `DEBUG_MODE`) are automatically excluded from being loaded from project `.env` files to prevent interference with gemini-cli behavior. Variables from `.gemini/.env` files are never excluded. You can customize this behavior using the `advanced.excludedEnvVars` setting in your `settings.json` file.
|
||||
|
||||
- **`GEMINI_API_KEY`**:
|
||||
- Your API key for the Gemini API.
|
||||
- One of several available [authentication methods](./authentication.md).
|
||||
- Set this in your shell profile (e.g., `~/.bashrc`, `~/.zshrc`) or an `.env` file.
|
||||
- **`GEMINI_MODEL`**:
|
||||
- Specifies the default Gemini model to use.
|
||||
- Overrides the hardcoded default
|
||||
- Example: `export GEMINI_MODEL="gemini-2.5-flash"`
|
||||
- **`GOOGLE_API_KEY`**:
|
||||
- Your Google Cloud API key.
|
||||
- Required for using Vertex AI in express mode.
|
||||
- Ensure you have the necessary permissions.
|
||||
- Example: `export GOOGLE_API_KEY="YOUR_GOOGLE_API_KEY"`.
|
||||
- **`GOOGLE_CLOUD_PROJECT`**:
|
||||
- Your Google Cloud Project ID.
|
||||
- Required for using Code Assist or Vertex AI.
|
||||
- If using Vertex AI, ensure you have the necessary permissions in this project.
|
||||
- **Cloud Shell Note:** When running in a Cloud Shell environment, this variable defaults to a special project allocated for Cloud Shell users. If you have `GOOGLE_CLOUD_PROJECT` set in your global environment in Cloud Shell, it will be overridden by this default. To use a different project in Cloud Shell, you must define `GOOGLE_CLOUD_PROJECT` in a `.env` file.
|
||||
- Example: `export GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
|
||||
- **`GOOGLE_APPLICATION_CREDENTIALS`** (string):
|
||||
- **Description:** The path to your Google Application Credentials JSON file.
|
||||
- **Example:** `export GOOGLE_APPLICATION_CREDENTIALS="/path/to/your/credentials.json"`
|
||||
- **`OTLP_GOOGLE_CLOUD_PROJECT`**:
|
||||
- Your Google Cloud Project ID for Telemetry in Google Cloud
|
||||
- Example: `export OTLP_GOOGLE_CLOUD_PROJECT="YOUR_PROJECT_ID"`.
|
||||
- **`GOOGLE_CLOUD_LOCATION`**:
|
||||
- Your Google Cloud Project Location (e.g., us-central1).
|
||||
- Required for using Vertex AI in non express mode.
|
||||
- Example: `export GOOGLE_CLOUD_LOCATION="YOUR_PROJECT_LOCATION"`.
|
||||
- **`GEMINI_SANDBOX`**:
|
||||
- Alternative to the `sandbox` setting in `settings.json`.
|
||||
- Accepts `true`, `false`, `docker`, `podman`, or a custom command string.
|
||||
- **`SEATBELT_PROFILE`** (macOS specific):
|
||||
- Switches the Seatbelt (`sandbox-exec`) profile on macOS.
|
||||
- `permissive-open`: (Default) Restricts writes to the project folder (and a few other folders, see `packages/cli/src/utils/sandbox-macos-permissive-open.sb`) but allows other operations.
|
||||
- `strict`: Uses a strict profile that declines operations by default.
|
||||
- `<profile_name>`: Uses a custom profile. To define a custom profile, create a file named `sandbox-macos-<profile_name>.sb` in your project's `.gemini/` directory (e.g., `my-project/.gemini/sandbox-macos-custom.sb`).
|
||||
- **`DEBUG` or `DEBUG_MODE`** (often used by underlying libraries or the CLI itself):
|
||||
- Set to `true` or `1` to enable verbose debug logging, which can be helpful for troubleshooting.
|
||||
- **Note:** These variables are automatically excluded from project `.env` files by default to prevent interference with gemini-cli behavior. Use `.gemini/.env` files if you need to set these for gemini-cli specifically.
|
||||
- **`NO_COLOR`**:
|
||||
- Set to any value to disable all color output in the CLI.
|
||||
- **`CLI_TITLE`**:
|
||||
- Set to a string to customize the title of the CLI.
|
||||
- **`CODE_ASSIST_ENDPOINT`**:
|
||||
- Specifies the endpoint for the code assist server.
|
||||
- This is useful for development and testing.
|
||||
|
||||
## Command-Line Arguments
|
||||
|
||||
Arguments passed directly when running the CLI can override other configurations for that specific session.
|
||||
|
||||
- **`--model <model_name>`** (**`-m <model_name>`**):
|
||||
- Specifies the Gemini model to use for this session.
|
||||
- Example: `npm start -- --model gemini-1.5-pro-latest`
|
||||
- **`--prompt <your_prompt>`** (**`-p <your_prompt>`**):
|
||||
- Used to pass a prompt directly to the command. This invokes Gemini CLI in a non-interactive mode.
|
||||
- For scripting examples, use the `--output-format json` flag to get structured output.
|
||||
- **`--prompt-interactive <your_prompt>`** (**`-i <your_prompt>`**):
|
||||
- Starts an interactive session with the provided prompt as the initial input.
|
||||
- The prompt is processed within the interactive session, not before it.
|
||||
- Cannot be used when piping input from stdin.
|
||||
- Example: `gemini -i "explain this code"`
|
||||
- **`--output-format <format>`**:
|
||||
- **Description:** Specifies the format of the CLI output for non-interactive mode.
|
||||
- **Values:**
|
||||
- `text`: (Default) The standard human-readable output.
|
||||
- `json`: A machine-readable JSON output.
|
||||
- **Note:** For structured output and scripting, use the `--output-format json` flag.
|
||||
- **`--sandbox`** (**`-s`**):
|
||||
- Enables sandbox mode for this session.
|
||||
- **`--sandbox-image`**:
|
||||
- Sets the sandbox image URI.
|
||||
- **`--debug`** (**`-d`**):
|
||||
- Enables debug mode for this session, providing more verbose output.
|
||||
- **`--all-files`** (**`-a`**):
|
||||
- If set, recursively includes all files within the current directory as context for the prompt.
|
||||
- **`--help`** (or **`-h`**):
|
||||
- Displays help information about command-line arguments.
|
||||
- **`--show-memory-usage`**:
|
||||
- Displays the current memory usage.
|
||||
- **`--yolo`**:
|
||||
- Enables YOLO mode, which automatically approves all tool calls.
|
||||
- **`--approval-mode <mode>`**:
|
||||
- Sets the approval mode for tool calls. Available modes:
|
||||
- `default`: Prompt for approval on each tool call (default behavior)
|
||||
- `auto_edit`: Automatically approve edit tools (replace, write_file) while prompting for others
|
||||
- `yolo`: Automatically approve all tool calls (equivalent to `--yolo`)
|
||||
- Cannot be used together with `--yolo`. Use `--approval-mode=yolo` instead of `--yolo` for the new unified approach.
|
||||
- Example: `gemini --approval-mode auto_edit`
|
||||
- **`--allowed-tools <tool1,tool2,...>`**:
|
||||
- A comma-separated list of tool names that will bypass the confirmation dialog.
|
||||
- Example: `gemini --allowed-tools "ShellTool(git status)"`
|
||||
- **`--telemetry`**:
|
||||
- Enables [telemetry](../telemetry.md).
|
||||
- **`--telemetry-target`**:
|
||||
- Sets the telemetry target. See [telemetry](../telemetry.md) for more information.
|
||||
- **`--telemetry-otlp-endpoint`**:
|
||||
- Sets the OTLP endpoint for telemetry. See [telemetry](../telemetry.md) for more information.
|
||||
- **`--telemetry-otlp-protocol`**:
|
||||
- Sets the OTLP protocol for telemetry (`grpc` or `http`). Defaults to `grpc`. See [telemetry](../telemetry.md) for more information.
|
||||
- **`--telemetry-log-prompts`**:
|
||||
- Enables logging of prompts for telemetry. See [telemetry](../telemetry.md) for more information.
|
||||
- **`--checkpointing`**:
|
||||
- Enables [checkpointing](../checkpointing.md).
|
||||
- **`--extensions <extension_name ...>`** (**`-e <extension_name ...>`**):
|
||||
- Specifies a list of extensions to use for the session. If not provided, all available extensions are used.
|
||||
- Use the special term `gemini -e none` to disable all extensions.
|
||||
- Example: `gemini -e my-extension -e my-other-extension`
|
||||
- **`--list-extensions`** (**`-l`**):
|
||||
- Lists all available extensions and exits.
|
||||
- **`--proxy`**:
|
||||
- Sets the proxy for the CLI.
|
||||
- Example: `--proxy http://localhost:7890`.
|
||||
- **`--include-directories <dir1,dir2,...>`**:
|
||||
- Includes additional directories in the workspace for multi-directory support.
|
||||
- Can be specified multiple times or as comma-separated values.
|
||||
- 5 directories can be added at maximum.
|
||||
- Example: `--include-directories /path/to/project1,/path/to/project2` or `--include-directories /path/to/project1 --include-directories /path/to/project2`
|
||||
- **`--screen-reader`**:
|
||||
- Enables screen reader mode, which adjusts the TUI for better compatibility with screen readers.
|
||||
- **`--version`**:
|
||||
- Displays the version of the CLI.
|
||||
|
||||
## Context Files (Hierarchical Instructional Context)
|
||||
|
||||
While not strictly configuration for the CLI's _behavior_, context files (defaulting to `GEMINI.md` but configurable via the `context.fileName` setting) are crucial for configuring the _instructional context_ (also referred to as "memory") provided to the Gemini model. This powerful feature allows you to give project-specific instructions, coding style guides, or any relevant background information to the AI, making its responses more tailored and accurate to your needs. The CLI includes UI elements, such as an indicator in the footer showing the number of loaded context files, to keep you informed about the active context.
|
||||
|
||||
- **Purpose:** These Markdown files contain instructions, guidelines, or context that you want the Gemini model to be aware of during your interactions. The system is designed to manage this instructional context hierarchically.
|
||||
|
||||
### Example Context File Content (e.g., `GEMINI.md`)
|
||||
|
||||
Here's a conceptual example of what a context file at the root of a TypeScript project might contain:
|
||||
|
||||
```markdown
|
||||
# Project: My Awesome TypeScript Library
|
||||
|
||||
## General Instructions:
|
||||
|
||||
- When generating new TypeScript code, please follow the existing coding style.
|
||||
- Ensure all new functions and classes have JSDoc comments.
|
||||
- Prefer functional programming paradigms where appropriate.
|
||||
- All code should be compatible with TypeScript 5.0 and Node.js 20+.
|
||||
|
||||
## Coding Style:
|
||||
|
||||
- Use 2 spaces for indentation.
|
||||
- Interface names should be prefixed with `I` (e.g., `IUserService`).
|
||||
- Private class members should be prefixed with an underscore (`_`).
|
||||
- Always use strict equality (`===` and `!==`).
|
||||
|
||||
## Specific Component: `src/api/client.ts`
|
||||
|
||||
- This file handles all outbound API requests.
|
||||
- When adding new API call functions, ensure they include robust error handling and logging.
|
||||
- Use the existing `fetchWithRetry` utility for all GET requests.
|
||||
|
||||
## Regarding Dependencies:
|
||||
|
||||
- Avoid introducing new external dependencies unless absolutely necessary.
|
||||
- If a new dependency is required, please state the reason.
|
||||
```
|
||||
|
||||
This example demonstrates how you can provide general project context, specific coding conventions, and even notes about particular files or components. The more relevant and precise your context files are, the better the AI can assist you. Project-specific context files are highly encouraged to establish conventions and context.
|
||||
|
||||
- **Hierarchical Loading and Precedence:** The CLI implements a sophisticated hierarchical memory system by loading context files (e.g., `GEMINI.md`) from several locations. Content from files lower in this list (more specific) typically overrides or supplements content from files higher up (more general). The exact concatenation order and final context can be inspected using the `/memory show` command. The typical loading order is:
|
||||
1. **Global Context File:**
|
||||
- Location: `~/.gemini/<configured-context-filename>` (e.g., `~/.gemini/GEMINI.md` in your user home directory).
|
||||
- Scope: Provides default instructions for all your projects.
|
||||
2. **Project Root & Ancestors Context Files:**
|
||||
- Location: The CLI searches for the configured context file in the current working directory and then in each parent directory up to either the project root (identified by a `.git` folder) or your home directory.
|
||||
- Scope: Provides context relevant to the entire project or a significant portion of it.
|
||||
3. **Sub-directory Context Files (Contextual/Local):**
|
||||
- Location: The CLI also scans for the configured context file in subdirectories _below_ the current working directory (respecting common ignore patterns like `node_modules`, `.git`, etc.). The breadth of this search is limited to 200 directories by default, but can be configured with the `context.discoveryMaxDirs` setting in your `settings.json` file.
|
||||
- Scope: Allows for highly specific instructions relevant to a particular component, module, or subsection of your project.
|
||||
- **Concatenation & UI Indication:** The contents of all found context files are concatenated (with separators indicating their origin and path) and provided as part of the system prompt to the Gemini model. The CLI footer displays the count of loaded context files, giving you a quick visual cue about the active instructional context.
|
||||
- **Importing Content:** You can modularize your context files by importing other Markdown files using the `@path/to/file.md` syntax. For more details, see the [Memory Import Processor documentation](../core/memport.md).
|
||||
- **Commands for Memory Management:**
|
||||
- Use `/memory refresh` to force a re-scan and reload of all context files from all configured locations. This updates the AI's instructional context.
|
||||
- Use `/memory show` to display the combined instructional context currently loaded, allowing you to verify the hierarchy and content being used by the AI.
|
||||
- See the [Commands documentation](./commands.md#memory) for full details on the `/memory` command and its sub-commands (`show` and `refresh`).
|
||||
|
||||
By understanding and utilizing these configuration layers and the hierarchical nature of context files, you can effectively manage the AI's memory and tailor the Gemini CLI's responses to your specific needs and projects.
|
||||
|
||||
## Sandboxing
|
||||
|
||||
The Gemini CLI can execute potentially unsafe operations (like shell commands and file modifications) within a sandboxed environment to protect your system.
|
||||
|
||||
Sandboxing is disabled by default, but you can enable it in a few ways:
|
||||
|
||||
- Using `--sandbox` or `-s` flag.
|
||||
- Setting `GEMINI_SANDBOX` environment variable.
|
||||
- Sandbox is enabled when using `--yolo` or `--approval-mode=yolo` by default.
|
||||
|
||||
By default, it uses a pre-built `gemini-cli-sandbox` Docker image.
|
||||
|
||||
For project-specific sandboxing needs, you can create a custom Dockerfile at `.gemini/sandbox.Dockerfile` in your project's root directory. This Dockerfile can be based on the base sandbox image:
|
||||
|
||||
```dockerfile
|
||||
FROM gemini-cli-sandbox
|
||||
|
||||
# Add your custom dependencies or configurations here
|
||||
# For example:
|
||||
# RUN apt-get update && apt-get install -y some-package
|
||||
# COPY ./my-config /app/my-config
|
||||
```
|
||||
|
||||
When `.gemini/sandbox.Dockerfile` exists, you can use `BUILD_SANDBOX` environment variable when running Gemini CLI to automatically build the custom sandbox image:
|
||||
|
||||
```bash
|
||||
BUILD_SANDBOX=1 gemini -s
|
||||
```
|
||||
|
||||
## Usage Statistics
|
||||
|
||||
To help us improve the Gemini CLI, we collect anonymized usage statistics. This data helps us understand how the CLI is used, identify common issues, and prioritize new features.
|
||||
|
||||
**What we collect:**
|
||||
|
||||
- **Tool Calls:** We log the names of the tools that are called, whether they succeed or fail, and how long they take to execute. We do not collect the arguments passed to the tools or any data returned by them.
|
||||
- **API Requests:** We log the Gemini model used for each request, the duration of the request, and whether it was successful. We do not collect the content of the prompts or responses.
|
||||
- **Session Information:** We collect information about the configuration of the CLI, such as the enabled tools and the approval mode.
|
||||
|
||||
**What we DON'T collect:**
|
||||
|
||||
- **Personally Identifiable Information (PII):** We do not collect any personal information, such as your name, email address, or API keys.
|
||||
- **Prompt and Response Content:** We do not log the content of your prompts or the responses from the Gemini model.
|
||||
- **File Content:** We do not log the content of any files that are read or written by the CLI.
|
||||
|
||||
**How to opt out:**
|
||||
|
||||
You can opt out of usage statistics collection at any time by setting the `usageStatisticsEnabled` property to `false` under the `privacy` category in your `settings.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"privacy": {
|
||||
"usageStatisticsEnabled": false
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,315 @@
|
||||
# Custom commands
|
||||
|
||||
Custom commands let you save and reuse your favorite or most frequently used
|
||||
prompts as personal shortcuts within Gemini CLI. You can create commands that
|
||||
are specific to a single project or commands that are available globally across
|
||||
all your projects, streamlining your workflow and ensuring consistency.
|
||||
|
||||
## File locations and precedence
|
||||
|
||||
Gemini CLI discovers commands from two locations, loaded in a specific order:
|
||||
|
||||
1. **User commands (global):** Located in `~/.gemini/commands/`. These commands
|
||||
are available in any project you are working on.
|
||||
2. **Project commands (local):** Located in
|
||||
`<your-project-root>/.gemini/commands/`. These commands are specific to the
|
||||
current project and can be checked into version control to be shared with
|
||||
your team.
|
||||
|
||||
If a command in the project directory has the same name as a command in the user
|
||||
directory, the **project command will always be used.** This allows projects to
|
||||
override global commands with project-specific versions.
|
||||
|
||||
## Naming and namespacing
|
||||
|
||||
The name of a command is determined by its file path relative to its `commands`
|
||||
directory. Subdirectories are used to create namespaced commands, with the path
|
||||
separator (`/` or `\`) being converted to a colon (`:`).
|
||||
|
||||
- A file at `~/.gemini/commands/test.toml` becomes the command `/test`.
|
||||
- A file at `<project>/.gemini/commands/git/commit.toml` becomes the namespaced
|
||||
command `/git:commit`.
|
||||
|
||||
## TOML file format (v1)
|
||||
|
||||
Your command definition files must be written in the TOML format and use the
|
||||
`.toml` file extension.
|
||||
|
||||
### Required fields
|
||||
|
||||
- `prompt` (String): The prompt that will be sent to the Gemini model when the
|
||||
command is executed. This can be a single-line or multi-line string.
|
||||
|
||||
### Optional fields
|
||||
|
||||
- `description` (String): A brief, one-line description of what the command
|
||||
does. This text will be displayed next to your command in the `/help` menu.
|
||||
**If you omit this field, a generic description will be generated from the
|
||||
filename.**
|
||||
|
||||
## Handling arguments
|
||||
|
||||
Custom commands support two powerful methods for handling arguments. The CLI
|
||||
automatically chooses the correct method based on the content of your command's
|
||||
`prompt`.
|
||||
|
||||
### 1. Context-aware injection with `{{args}}`
|
||||
|
||||
If your `prompt` contains the special placeholder `{{args}}`, the CLI will
|
||||
replace that placeholder with the text the user typed after the command name.
|
||||
|
||||
The behavior of this injection depends on where it is used:
|
||||
|
||||
**A. Raw injection (outside shell commands)**
|
||||
|
||||
When used in the main body of the prompt, the arguments are injected exactly as
|
||||
the user typed them.
|
||||
|
||||
**Example (`git/fix.toml`):**
|
||||
|
||||
```toml
|
||||
# Invoked via: /git:fix "Button is misaligned"
|
||||
|
||||
description = "Generates a fix for a given issue."
|
||||
prompt = "Please provide a code fix for the issue described here: {{args}}."
|
||||
```
|
||||
|
||||
The model receives:
|
||||
`Please provide a code fix for the issue described here: "Button is misaligned".`
|
||||
|
||||
**B. Using arguments in shell commands (inside `!{...}` blocks)**
|
||||
|
||||
When you use `{{args}}` inside a shell injection block (`!{...}`), the arguments
|
||||
are automatically **shell-escaped** before replacement. This allows you to
|
||||
safely pass arguments to shell commands, ensuring the resulting command is
|
||||
syntactically correct and secure while preventing command injection
|
||||
vulnerabilities.
|
||||
|
||||
**Example (`/grep-code.toml`):**
|
||||
|
||||
```toml
|
||||
prompt = """
|
||||
Please summarize the findings for the pattern `{{args}}`.
|
||||
|
||||
Search Results:
|
||||
!{grep -r {{args}} .}
|
||||
"""
|
||||
```
|
||||
|
||||
When you run `/grep-code It's complicated`:
|
||||
|
||||
1. The CLI sees `{{args}}` used both outside and inside `!{...}`.
|
||||
2. Outside: The first `{{args}}` is replaced raw with `It's complicated`.
|
||||
3. Inside: The second `{{args}}` is replaced with the escaped version (e.g., on
|
||||
Linux: `"It\'s complicated"`).
|
||||
4. The command executed is `grep -r "It's complicated" .`.
|
||||
5. The CLI prompts you to confirm this exact, secure command before execution.
|
||||
6. The final prompt is sent.
|
||||
|
||||
### 2. Default argument handling
|
||||
|
||||
If your `prompt` does **not** contain the special placeholder `{{args}}`, the
|
||||
CLI uses a default behavior for handling arguments.
|
||||
|
||||
If you provide arguments to the command (e.g., `/mycommand arg1`), the CLI will
|
||||
append the full command you typed to the end of the prompt, separated by two
|
||||
newlines. This allows the model to see both the original instructions and the
|
||||
specific arguments you just provided.
|
||||
|
||||
If you do **not** provide any arguments (e.g., `/mycommand`), the prompt is sent
|
||||
to the model exactly as it is, with nothing appended.
|
||||
|
||||
**Example (`changelog.toml`):**
|
||||
|
||||
This example shows how to create a robust command by defining a role for the
|
||||
model, explaining where to find the user's input, and specifying the expected
|
||||
format and behavior.
|
||||
|
||||
```toml
|
||||
# In: <project>/.gemini/commands/changelog.toml
|
||||
# Invoked via: /changelog 1.2.0 added "Support for default argument parsing."
|
||||
|
||||
description = "Adds a new entry to the project's CHANGELOG.md file."
|
||||
prompt = """
|
||||
# Task: Update Changelog
|
||||
|
||||
You are an expert maintainer of this software project. A user has invoked a command to add a new entry to the changelog.
|
||||
|
||||
**The user's raw command is appended below your instructions.**
|
||||
|
||||
Your task is to parse the `<version>`, `<change_type>`, and `<message>` from their input and use the `write_file` tool to correctly update the `CHANGELOG.md` file.
|
||||
|
||||
## Expected Format
|
||||
The command follows this format: `/changelog <version> <type> <message>`
|
||||
- `<type>` must be one of: "added", "changed", "fixed", "removed".
|
||||
|
||||
## Behavior
|
||||
1. Read the `CHANGELOG.md` file.
|
||||
2. Find the section for the specified `<version>`.
|
||||
3. Add the `<message>` under the correct `<type>` heading.
|
||||
4. If the version or type section doesn't exist, create it.
|
||||
5. Adhere strictly to the "Keep a Changelog" format.
|
||||
"""
|
||||
```
|
||||
|
||||
When you run `/changelog 1.2.0 added "New feature"`, the final text sent to the
|
||||
model will be the original prompt followed by two newlines and the command you
|
||||
typed.
|
||||
|
||||
### 3. Executing shell commands with `!{...}`
|
||||
|
||||
You can make your commands dynamic by executing shell commands directly within
|
||||
your `prompt` and injecting their output. This is ideal for gathering context
|
||||
from your local environment, like reading file content or checking the status of
|
||||
Git.
|
||||
|
||||
When a custom command attempts to execute a shell command, Gemini CLI will now
|
||||
prompt you for confirmation before proceeding. This is a security measure to
|
||||
ensure that only intended commands can be run.
|
||||
|
||||
**How it works:**
|
||||
|
||||
1. **Inject commands:** Use the `!{...}` syntax.
|
||||
2. **Argument substitution:** If `{{args}}` is present inside the block, it is
|
||||
automatically shell-escaped (see
|
||||
[Context-Aware Injection](#1-context-aware-injection-with-args) above).
|
||||
3. **Robust parsing:** The parser correctly handles complex shell commands that
|
||||
include nested braces, such as JSON payloads. **Note:** The content inside
|
||||
`!{...}` must have balanced braces (`{` and `}`). If you need to execute a
|
||||
command containing unbalanced braces, consider wrapping it in an external
|
||||
script file and calling the script within the `!{...}` block.
|
||||
4. **Security check and confirmation:** The CLI performs a security check on
|
||||
the final, resolved command (after arguments are escaped and substituted). A
|
||||
dialog will appear showing the exact command(s) to be executed.
|
||||
5. **Execution and error reporting:** The command is executed. If the command
|
||||
fails, the output injected into the prompt will include the error messages
|
||||
(stderr) followed by a status line, e.g.,
|
||||
`[Shell command exited with code 1]`. This helps the model understand the
|
||||
context of the failure.
|
||||
|
||||
**Example (`git/commit.toml`):**
|
||||
|
||||
This command gets the staged git diff and uses it to ask the model to write a
|
||||
commit message.
|
||||
|
||||
````toml
|
||||
# In: <project>/.gemini/commands/git/commit.toml
|
||||
# Invoked via: /git:commit
|
||||
|
||||
description = "Generates a Git commit message based on staged changes."
|
||||
|
||||
# The prompt uses !{...} to execute the command and inject its output.
|
||||
prompt = """
|
||||
Please generate a Conventional Commit message based on the following git diff:
|
||||
|
||||
```diff
|
||||
!{git diff --staged}
|
||||
```
|
||||
|
||||
"""
|
||||
|
||||
````
|
||||
|
||||
When you run `/git:commit`, the CLI first executes `git diff --staged`, then
|
||||
replaces `!{git diff --staged}` with the output of that command before sending
|
||||
the final, complete prompt to the model.
|
||||
|
||||
### 4. Injecting file content with `@{...}`
|
||||
|
||||
You can directly embed the content of a file or a directory listing into your
|
||||
prompt using the `@{...}` syntax. This is useful for creating commands that
|
||||
operate on specific files.
|
||||
|
||||
**How it works:**
|
||||
|
||||
- **File injection**: `@{path/to/file.txt}` is replaced by the content of
|
||||
`file.txt`.
|
||||
- **Multimodal support**: If the path points to a supported image (e.g., PNG,
|
||||
JPEG), PDF, audio, or video file, it will be correctly encoded and injected as
|
||||
multimodal input. Other binary files are handled gracefully and skipped.
|
||||
- **Directory listing**: `@{path/to/dir}` is traversed and each file present
|
||||
within the directory and all subdirectories is inserted into the prompt. This
|
||||
respects `.gitignore` and `.geminiignore` if enabled.
|
||||
- **Workspace-aware**: The command searches for the path in the current
|
||||
directory and any other workspace directories. Absolute paths are allowed if
|
||||
they are within the workspace.
|
||||
- **Processing order**: File content injection with `@{...}` is processed
|
||||
_before_ shell commands (`!{...}`) and argument substitution (`{{args}}`).
|
||||
- **Parsing**: The parser requires the content inside `@{...}` (the path) to
|
||||
have balanced braces (`{` and `}`).
|
||||
|
||||
**Example (`review.toml`):**
|
||||
|
||||
This command injects the content of a _fixed_ best practices file
|
||||
(`docs/best-practices.md`) and uses the user's arguments to provide context for
|
||||
the review.
|
||||
|
||||
```toml
|
||||
# In: <project>/.gemini/commands/review.toml
|
||||
# Invoked via: /review FileCommandLoader.ts
|
||||
|
||||
description = "Reviews the provided context using a best practice guide."
|
||||
prompt = """
|
||||
You are an expert code reviewer.
|
||||
|
||||
Your task is to review {{args}}.
|
||||
|
||||
Use the following best practices when providing your review:
|
||||
|
||||
@{docs/best-practices.md}
|
||||
"""
|
||||
```
|
||||
|
||||
When you run `/review FileCommandLoader.ts`, the `@{docs/best-practices.md}`
|
||||
placeholder is replaced by the content of that file, and `{{args}}` is replaced
|
||||
by the text you provided, before the final prompt is sent to the model.
|
||||
|
||||
---
|
||||
|
||||
## Example: A "Pure Function" refactoring command
|
||||
|
||||
Let's create a global command that asks the model to refactor a piece of code.
|
||||
|
||||
**1. Create the file and directories:**
|
||||
|
||||
First, ensure the user commands directory exists, then create a `refactor`
|
||||
subdirectory for organization and the final TOML file.
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.gemini/commands/refactor
|
||||
touch ~/.gemini/commands/refactor/pure.toml
|
||||
```
|
||||
|
||||
**2. Add the content to the file:**
|
||||
|
||||
Open `~/.gemini/commands/refactor/pure.toml` in your editor and add the
|
||||
following content. We are including the optional `description` for best
|
||||
practice.
|
||||
|
||||
```toml
|
||||
# In: ~/.gemini/commands/refactor/pure.toml
|
||||
# This command will be invoked via: /refactor:pure
|
||||
|
||||
description = "Asks the model to refactor the current context into a pure function."
|
||||
|
||||
prompt = """
|
||||
Please analyze the code I've provided in the current context.
|
||||
Refactor it into a pure function.
|
||||
|
||||
Your response should include:
|
||||
1. The refactored, pure function code block.
|
||||
2. A brief explanation of the key changes you made and why they contribute to purity.
|
||||
"""
|
||||
```
|
||||
|
||||
**3. Run the command:**
|
||||
|
||||
That's it! You can now run your command in the CLI. First, you might add a file
|
||||
to the context, and then invoke your command:
|
||||
|
||||
```
|
||||
> @my-messy-function.js
|
||||
> /refactor:pure
|
||||
```
|
||||
|
||||
Gemini CLI will then execute the multi-line prompt defined in your TOML file.
|
||||
+246
-64
@@ -1,27 +1,43 @@
|
||||
# Gemini CLI for the Enterprise
|
||||
# Gemini CLI for the enterprise
|
||||
|
||||
This document outlines configuration patterns and best practices for deploying and managing Gemini CLI in an enterprise environment. By leveraging system-level settings, administrators can enforce security policies, manage tool access, and ensure a consistent experience for all users.
|
||||
This document outlines configuration patterns and best practices for deploying
|
||||
and managing Gemini CLI in an enterprise environment. By leveraging system-level
|
||||
settings, administrators can enforce security policies, manage tool access, and
|
||||
ensure a consistent experience for all users.
|
||||
|
||||
> **A Note on Security:** The patterns described in this document are intended to help administrators create a more controlled and secure environment for using Gemini CLI. However, they should not be considered a foolproof security boundary. A determined user with sufficient privileges on their local machine may still be able to circumvent these configurations. These measures are designed to prevent accidental misuse and enforce corporate policy in a managed environment, not to defend against a malicious actor with local administrative rights.
|
||||
> **A note on security:** The patterns described in this document are intended
|
||||
> to help administrators create a more controlled and secure environment for
|
||||
> using Gemini CLI. However, they should not be considered a foolproof security
|
||||
> boundary. A determined user with sufficient privileges on their local machine
|
||||
> may still be able to circumvent these configurations. These measures are
|
||||
> designed to prevent accidental misuse and enforce corporate policy in a
|
||||
> managed environment, not to defend against a malicious actor with local
|
||||
> administrative rights.
|
||||
|
||||
## Centralized Configuration: The System Settings File
|
||||
## Centralized configuration: The system settings file
|
||||
|
||||
The most powerful tools for enterprise administration are the system-wide settings files. These files allow you to define a baseline configuration (`system-defaults.json`) and a set of overrides (`settings.json`) that apply to all users on a machine. For a complete overview of configuration options, see the [Configuration documentation](./configuration.md).
|
||||
The most powerful tools for enterprise administration are the system-wide
|
||||
settings files. These files allow you to define a baseline configuration
|
||||
(`system-defaults.json`) and a set of overrides (`settings.json`) that apply to
|
||||
all users on a machine. For a complete overview of configuration options, see
|
||||
the [Configuration documentation](../get-started/configuration.md).
|
||||
|
||||
Settings are merged from four files. The precedence order for single-value settings (like `theme`) is:
|
||||
Settings are merged from four files. The precedence order for single-value
|
||||
settings (like `theme`) is:
|
||||
|
||||
1. System Defaults (`system-defaults.json`)
|
||||
2. User Settings (`~/.gemini/settings.json`)
|
||||
3. Workspace Settings (`<project>/.gemini/settings.json`)
|
||||
4. System Overrides (`settings.json`)
|
||||
|
||||
This means the System Overrides file has the final say. For settings that are arrays (`includeDirectories`) or objects (`mcpServers`), the values are merged.
|
||||
This means the System Overrides file has the final say. For settings that are
|
||||
arrays (`includeDirectories`) or objects (`mcpServers`), the values are merged.
|
||||
|
||||
**Example of Merging and Precedence:**
|
||||
**Example of merging and precedence:**
|
||||
|
||||
Here is how settings from different levels are combined.
|
||||
|
||||
- **System Defaults `system-defaults.json`:**
|
||||
- **System defaults `system-defaults.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -73,7 +89,7 @@ Here is how settings from different levels are combined.
|
||||
}
|
||||
```
|
||||
|
||||
- **System Overrides `settings.json`:**
|
||||
- **System overrides `settings.json`:**
|
||||
```json
|
||||
{
|
||||
"ui": {
|
||||
@@ -92,7 +108,7 @@ Here is how settings from different levels are combined.
|
||||
|
||||
This results in the following merged configuration:
|
||||
|
||||
- **Final Merged Configuration:**
|
||||
- **Final merged configuration:**
|
||||
```json
|
||||
{
|
||||
"ui": {
|
||||
@@ -122,26 +138,83 @@ This results in the following merged configuration:
|
||||
|
||||
**Why:**
|
||||
|
||||
- **`theme`**: The value from the system overrides (`system-enforced-theme`) is used, as it has the highest precedence.
|
||||
- **`mcpServers`**: The objects are merged. The `corp-server` definition from the system overrides takes precedence over the user's definition. The unique `user-tool` and `project-tool` are included.
|
||||
- **`includeDirectories`**: The arrays are concatenated in the order of System Defaults, User, Workspace, and then System Overrides.
|
||||
- **`theme`**: The value from the system overrides (`system-enforced-theme`) is
|
||||
used, as it has the highest precedence.
|
||||
- **`mcpServers`**: The objects are merged. The `corp-server` definition from
|
||||
the system overrides takes precedence over the user's definition. The unique
|
||||
`user-tool` and `project-tool` are included.
|
||||
- **`includeDirectories`**: The arrays are concatenated in the order of System
|
||||
Defaults, User, Workspace, and then System Overrides.
|
||||
|
||||
- **Location**:
|
||||
- **Linux**: `/etc/gemini-cli/settings.json`
|
||||
- **Windows**: `C:\ProgramData\gemini-cli\settings.json`
|
||||
- **macOS**: `/Library/Application Support/GeminiCli/settings.json`
|
||||
- The path can be overridden using the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable.
|
||||
- **Control**: This file should be managed by system administrators and protected with appropriate file permissions to prevent unauthorized modification by users.
|
||||
- The path can be overridden using the `GEMINI_CLI_SYSTEM_SETTINGS_PATH`
|
||||
environment variable.
|
||||
- **Control**: This file should be managed by system administrators and
|
||||
protected with appropriate file permissions to prevent unauthorized
|
||||
modification by users.
|
||||
|
||||
By using the system settings file, you can enforce the security and configuration patterns described below.
|
||||
By using the system settings file, you can enforce the security and
|
||||
configuration patterns described below.
|
||||
|
||||
## Restricting Tool Access
|
||||
### Enforcing system settings with a wrapper script
|
||||
|
||||
You can significantly enhance security by controlling which tools the Gemini model can use. This is achieved through the `tools.core` and `tools.exclude` settings. For a list of available tools, see the [Tools documentation](../tools/index.md).
|
||||
While the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` environment variable provides
|
||||
flexibility, a user could potentially override it to point to a different
|
||||
settings file, bypassing the centrally managed configuration. To mitigate this,
|
||||
enterprises can deploy a wrapper script or alias that ensures the environment
|
||||
variable is always set to the corporate-controlled path.
|
||||
|
||||
This approach ensures that no matter how the user calls the `gemini` command,
|
||||
the enterprise settings are always loaded with the highest precedence.
|
||||
|
||||
**Example wrapper script:**
|
||||
|
||||
Administrators can create a script named `gemini` and place it in a directory
|
||||
that appears earlier in the user's `PATH` than the actual Gemini CLI binary
|
||||
(e.g., `/usr/local/bin/gemini`).
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
|
||||
# Enforce the path to the corporate system settings file.
|
||||
# This ensures that the company's configuration is always applied.
|
||||
export GEMINI_CLI_SYSTEM_SETTINGS_PATH="/etc/gemini-cli/settings.json"
|
||||
|
||||
# Find the original gemini executable.
|
||||
# This is a simple example; a more robust solution might be needed
|
||||
# depending on the installation method.
|
||||
REAL_GEMINI_PATH=$(type -aP gemini | grep -v "^$(type -P gemini)$" | head -n 1)
|
||||
|
||||
if [ -z "$REAL_GEMINI_PATH" ]; then
|
||||
echo "Error: The original 'gemini' executable was not found." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Pass all arguments to the real Gemini CLI executable.
|
||||
exec "$REAL_GEMINI_PATH" "$@"
|
||||
```
|
||||
|
||||
By deploying this script, the `GEMINI_CLI_SYSTEM_SETTINGS_PATH` is set within
|
||||
the script's environment, and the `exec` command replaces the script process
|
||||
with the actual Gemini CLI process, which inherits the environment variable.
|
||||
This makes it significantly more difficult for a user to bypass the enforced
|
||||
settings.
|
||||
|
||||
## Restricting tool access
|
||||
|
||||
You can significantly enhance security by controlling which tools the Gemini
|
||||
model can use. This is achieved through the `tools.core` and `tools.exclude`
|
||||
settings. For a list of available tools, see the
|
||||
[Tools documentation](../tools/index.md).
|
||||
|
||||
### Allowlisting with `coreTools`
|
||||
|
||||
The most secure approach is to explicitly add the tools and commands that users are permitted to execute to an allowlist. This prevents the use of any tool not on the approved list.
|
||||
The most secure approach is to explicitly add the tools and commands that users
|
||||
are permitted to execute to an allowlist. This prevents the use of any tool not
|
||||
on the approved list.
|
||||
|
||||
**Example:** Allow only safe, read-only file operations and listing files.
|
||||
|
||||
@@ -155,7 +228,8 @@ The most secure approach is to explicitly add the tools and commands that users
|
||||
|
||||
### Blocklisting with `excludeTools`
|
||||
|
||||
Alternatively, you can add specific tools that are considered dangerous in your environment to a blocklist.
|
||||
Alternatively, you can add specific tools that are considered dangerous in your
|
||||
environment to a blocklist.
|
||||
|
||||
**Example:** Prevent the use of the shell tool for removing files.
|
||||
|
||||
@@ -167,32 +241,74 @@ Alternatively, you can add specific tools that are considered dangerous in your
|
||||
}
|
||||
```
|
||||
|
||||
**Security Note:** Blocklisting with `excludeTools` is less secure than allowlisting with `coreTools`, as it relies on blocking known-bad commands, and clever users may find ways to bypass simple string-based blocks. **Allowlisting is the recommended approach.**
|
||||
**Security note:** Blocklisting with `excludeTools` is less secure than
|
||||
allowlisting with `coreTools`, as it relies on blocking known-bad commands, and
|
||||
clever users may find ways to bypass simple string-based blocks. **Allowlisting
|
||||
is the recommended approach.**
|
||||
|
||||
## Managing Custom Tools (MCP Servers)
|
||||
### Disabling YOLO mode
|
||||
|
||||
If your organization uses custom tools via [Model-Context Protocol (MCP) servers](../core/tools-api.md), it is crucial to understand how server configurations are managed to apply security policies effectively.
|
||||
To ensure that users cannot bypass the confirmation prompt for tool execution,
|
||||
you can disable YOLO mode at the policy level. This adds a critical layer of
|
||||
safety, as it prevents the model from executing tools without explicit user
|
||||
approval.
|
||||
|
||||
### How MCP Server Configurations are Merged
|
||||
**Example:** Force all tool executions to require user confirmation.
|
||||
|
||||
Gemini CLI loads `settings.json` files from three levels: System, Workspace, and User. When it comes to the `mcpServers` object, these configurations are **merged**:
|
||||
```json
|
||||
{
|
||||
"security": {
|
||||
"disableYoloMode": true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
1. **Merging:** The lists of servers from all three levels are combined into a single list.
|
||||
2. **Precedence:** If a server with the **same name** is defined at multiple levels (e.g., a server named `corp-api` exists in both system and user settings), the definition from the highest-precedence level is used. The order of precedence is: **System > Workspace > User**.
|
||||
This setting is highly recommended in an enterprise environment to prevent
|
||||
unintended tool execution.
|
||||
|
||||
This means a user **cannot** override the definition of a server that is already defined in the system-level settings. However, they **can** add new servers with unique names.
|
||||
## Managing custom tools (MCP servers)
|
||||
|
||||
### Enforcing a Catalog of Tools
|
||||
If your organization uses custom tools via
|
||||
[Model-Context Protocol (MCP) servers](../core/tools-api.md), it is crucial to
|
||||
understand how server configurations are managed to apply security policies
|
||||
effectively.
|
||||
|
||||
The security of your MCP tool ecosystem depends on a combination of defining the canonical servers and adding their names to an allowlist.
|
||||
### How MCP server configurations are merged
|
||||
|
||||
### Restricting Tools Within an MCP Server
|
||||
Gemini CLI loads `settings.json` files from three levels: System, Workspace, and
|
||||
User. When it comes to the `mcpServers` object, these configurations are
|
||||
**merged**:
|
||||
|
||||
For even greater security, especially when dealing with third-party MCP servers, you can restrict which specific tools from a server are exposed to the model. This is done using the `includeTools` and `excludeTools` properties within a server's definition. This allows you to use a subset of tools from a server without allowing potentially dangerous ones.
|
||||
1. **Merging:** The lists of servers from all three levels are combined into a
|
||||
single list.
|
||||
2. **Precedence:** If a server with the **same name** is defined at multiple
|
||||
levels (e.g., a server named `corp-api` exists in both system and user
|
||||
settings), the definition from the highest-precedence level is used. The
|
||||
order of precedence is: **System > Workspace > User**.
|
||||
|
||||
Following the principle of least privilege, it is highly recommended to use `includeTools` to create an allowlist of only the necessary tools.
|
||||
This means a user **cannot** override the definition of a server that is already
|
||||
defined in the system-level settings. However, they **can** add new servers with
|
||||
unique names.
|
||||
|
||||
**Example:** Only allow the `code-search` and `get-ticket-details` tools from a third-party MCP server, even if the server offers other tools like `delete-ticket`.
|
||||
### Enforcing a catalog of tools
|
||||
|
||||
The security of your MCP tool ecosystem depends on a combination of defining the
|
||||
canonical servers and adding their names to an allowlist.
|
||||
|
||||
### Restricting tools within an MCP server
|
||||
|
||||
For even greater security, especially when dealing with third-party MCP servers,
|
||||
you can restrict which specific tools from a server are exposed to the model.
|
||||
This is done using the `includeTools` and `excludeTools` properties within a
|
||||
server's definition. This allows you to use a subset of tools from a server
|
||||
without allowing potentially dangerous ones.
|
||||
|
||||
Following the principle of least privilege, it is highly recommended to use
|
||||
`includeTools` to create an allowlist of only the necessary tools.
|
||||
|
||||
**Example:** Only allow the `code-search` and `get-ticket-details` tools from a
|
||||
third-party MCP server, even if the server offers other tools like
|
||||
`delete-ticket`.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -208,17 +324,23 @@ Following the principle of least privilege, it is highly recommended to use `inc
|
||||
}
|
||||
```
|
||||
|
||||
#### More Secure Pattern: Define and Add to Allowlist in System Settings
|
||||
#### More secure pattern: Define and add to allowlist in system settings
|
||||
|
||||
To create a secure, centrally-managed catalog of tools, the system administrator **must** do both of the following in the system-level `settings.json` file:
|
||||
To create a secure, centrally-managed catalog of tools, the system administrator
|
||||
**must** do both of the following in the system-level `settings.json` file:
|
||||
|
||||
1. **Define the full configuration** for every approved server in the `mcpServers` object. This ensures that even if a user defines a server with the same name, the secure system-level definition will take precedence.
|
||||
2. **Add the names** of those servers to an allowlist using the `mcp.allowed` setting. This is a critical security step that prevents users from running any servers that are not on this list. If this setting is omitted, the CLI will merge and allow any server defined by the user.
|
||||
1. **Define the full configuration** for every approved server in the
|
||||
`mcpServers` object. This ensures that even if a user defines a server with
|
||||
the same name, the secure system-level definition will take precedence.
|
||||
2. **Add the names** of those servers to an allowlist using the `mcp.allowed`
|
||||
setting. This is a critical security step that prevents users from running
|
||||
any servers that are not on this list. If this setting is omitted, the CLI
|
||||
will merge and allow any server defined by the user.
|
||||
|
||||
**Example System `settings.json`:**
|
||||
**Example system `settings.json`:**
|
||||
|
||||
1. Add the _names_ of all approved servers to an allowlist.
|
||||
This will prevent users from adding their own servers.
|
||||
1. Add the _names_ of all approved servers to an allowlist. This will prevent
|
||||
users from adding their own servers.
|
||||
|
||||
2. Provide the canonical _definition_ for each server on the allowlist.
|
||||
|
||||
@@ -239,16 +361,20 @@ To create a secure, centrally-managed catalog of tools, the system administrator
|
||||
}
|
||||
```
|
||||
|
||||
This pattern is more secure because it uses both definition and an allowlist. Any server a user defines will either be overridden by the system definition (if it has the same name) or blocked because its name is not in the `mcp.allowed` list.
|
||||
This pattern is more secure because it uses both definition and an allowlist.
|
||||
Any server a user defines will either be overridden by the system definition (if
|
||||
it has the same name) or blocked because its name is not in the `mcp.allowed`
|
||||
list.
|
||||
|
||||
### Less Secure Pattern: Omitting the Allowlist
|
||||
### Less secure pattern: Omitting the allowlist
|
||||
|
||||
If the administrator defines the `mcpServers` object but fails to also specify the `mcp.allowed` allowlist, users may add their own servers.
|
||||
If the administrator defines the `mcpServers` object but fails to also specify
|
||||
the `mcp.allowed` allowlist, users may add their own servers.
|
||||
|
||||
**Example System `settings.json`:**
|
||||
**Example system `settings.json`:**
|
||||
|
||||
This configuration defines servers but does not enforce the allowlist.
|
||||
The administrator has NOT included the "mcp.allowed" setting.
|
||||
This configuration defines servers but does not enforce the allowlist. The
|
||||
administrator has NOT included the "mcp.allowed" setting.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -260,11 +386,16 @@ The administrator has NOT included the "mcp.allowed" setting.
|
||||
}
|
||||
```
|
||||
|
||||
In this scenario, a user can add their own server in their local `settings.json`. Because there is no `mcp.allowed` list to filter the merged results, the user's server will be added to the list of available tools and allowed to run.
|
||||
In this scenario, a user can add their own server in their local
|
||||
`settings.json`. Because there is no `mcp.allowed` list to filter the merged
|
||||
results, the user's server will be added to the list of available tools and
|
||||
allowed to run.
|
||||
|
||||
## Enforcing Sandboxing for Security
|
||||
## Enforcing sandboxing for security
|
||||
|
||||
To mitigate the risk of potentially harmful operations, you can enforce the use of sandboxing for all tool execution. The sandbox isolates tool execution in a containerized environment.
|
||||
To mitigate the risk of potentially harmful operations, you can enforce the use
|
||||
of sandboxing for all tool execution. The sandbox isolates tool execution in a
|
||||
containerized environment.
|
||||
|
||||
**Example:** Force all tool execution to happen within a Docker sandbox.
|
||||
|
||||
@@ -276,13 +407,18 @@ To mitigate the risk of potentially harmful operations, you can enforce the use
|
||||
}
|
||||
```
|
||||
|
||||
You can also specify a custom, hardened Docker image for the sandbox using the `--sandbox-image` command-line argument or by building a custom `sandbox.Dockerfile` as described in the [Sandboxing documentation](./configuration.md#sandboxing).
|
||||
You can also specify a custom, hardened Docker image for the sandbox by building
|
||||
a custom `sandbox.Dockerfile` as described in the
|
||||
[Sandboxing documentation](./sandbox.md).
|
||||
|
||||
## Controlling Network Access via Proxy
|
||||
## Controlling network access via proxy
|
||||
|
||||
In corporate environments with strict network policies, you can configure Gemini CLI to route all outbound traffic through a corporate proxy. This can be set via an environment variable, but it can also be enforced for custom tools via the `mcpServers` configuration.
|
||||
In corporate environments with strict network policies, you can configure Gemini
|
||||
CLI to route all outbound traffic through a corporate proxy. This can be set via
|
||||
an environment variable, but it can also be enforced for custom tools via the
|
||||
`mcpServers` configuration.
|
||||
|
||||
**Example (for an MCP Server):**
|
||||
**Example (for an MCP server):**
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -299,11 +435,15 @@ In corporate environments with strict network policies, you can configure Gemini
|
||||
}
|
||||
```
|
||||
|
||||
## Telemetry and Auditing
|
||||
## Telemetry and auditing
|
||||
|
||||
For auditing and monitoring purposes, you can configure Gemini CLI to send telemetry data to a central location. This allows you to track tool usage and other events. For more information, see the [telemetry documentation](../telemetry.md).
|
||||
For auditing and monitoring purposes, you can configure Gemini CLI to send
|
||||
telemetry data to a central location. This allows you to track tool usage and
|
||||
other events. For more information, see the
|
||||
[telemetry documentation](./telemetry.md).
|
||||
|
||||
**Example:** Enable telemetry and send it to a local OTLP collector. If `otlpEndpoint` is not specified, it defaults to `http://localhost:4317`.
|
||||
**Example:** Enable telemetry and send it to a local OTLP collector. If
|
||||
`otlpEndpoint` is not specified, it defaults to `http://localhost:4317`.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -315,11 +455,15 @@ For auditing and monitoring purposes, you can configure Gemini CLI to send telem
|
||||
}
|
||||
```
|
||||
|
||||
**Note:** Ensure that `logPrompts` is set to `false` in an enterprise setting to avoid collecting potentially sensitive information from user prompts.
|
||||
**Note:** Ensure that `logPrompts` is set to `false` in an enterprise setting to
|
||||
avoid collecting potentially sensitive information from user prompts.
|
||||
|
||||
## Authentication
|
||||
|
||||
You can enforce a specific authentication method for all users by setting the `enforcedAuthType` in the system-level `settings.json` file. This prevents users from choosing a different authentication method. See the [Authentication docs](./authentication.md) for more details.
|
||||
You can enforce a specific authentication method for all users by setting the
|
||||
`enforcedAuthType` in the system-level `settings.json` file. This prevents users
|
||||
from choosing a different authentication method. See the
|
||||
[Authentication docs](./authentication.md) for more details.
|
||||
|
||||
**Example:** Enforce the use of Google login for all users.
|
||||
|
||||
@@ -329,11 +473,48 @@ You can enforce a specific authentication method for all users by setting the `e
|
||||
}
|
||||
```
|
||||
|
||||
If a user has a different authentication method configured, they will be prompted to switch to the enforced method. In non-interactive mode, the CLI will exit with an error if the configured authentication method does not match the enforced one.
|
||||
If a user has a different authentication method configured, they will be
|
||||
prompted to switch to the enforced method. In non-interactive mode, the CLI will
|
||||
exit with an error if the configured authentication method does not match the
|
||||
enforced one.
|
||||
|
||||
## Putting It All Together: Example System `settings.json`
|
||||
### Restricting logins to corporate domains
|
||||
|
||||
Here is an example of a system `settings.json` file that combines several of the patterns discussed above to create a secure, controlled environment for Gemini CLI.
|
||||
For enterprises using Google Workspace, you can enforce that users only
|
||||
authenticate with their corporate Google accounts. This is a network-level
|
||||
control that is configured on a proxy server, not within Gemini CLI itself. It
|
||||
works by intercepting authentication requests to Google and adding a special
|
||||
HTTP header.
|
||||
|
||||
This policy prevents users from logging in with personal Gmail accounts or other
|
||||
non-corporate Google accounts.
|
||||
|
||||
For detailed instructions, see the Google Workspace Admin Help article on
|
||||
[blocking access to consumer accounts](https://support.google.com/a/answer/1668854?hl=en#zippy=%2Cstep-choose-a-web-proxy-server%2Cstep-configure-the-network-to-block-certain-accounts).
|
||||
|
||||
The general steps are as follows:
|
||||
|
||||
1. **Intercept Requests**: Configure your web proxy to intercept all requests
|
||||
to `google.com`.
|
||||
2. **Add HTTP Header**: For each intercepted request, add the
|
||||
`X-GoogApps-Allowed-Domains` HTTP header.
|
||||
3. **Specify Domains**: The value of the header should be a comma-separated
|
||||
list of your approved Google Workspace domain names.
|
||||
|
||||
**Example header:**
|
||||
|
||||
```
|
||||
X-GoogApps-Allowed-Domains: my-corporate-domain.com, secondary-domain.com
|
||||
```
|
||||
|
||||
When this header is present, Google's authentication service will only allow
|
||||
logins from accounts belonging to the specified domains.
|
||||
|
||||
## Putting it all together: example system `settings.json`
|
||||
|
||||
Here is an example of a system `settings.json` file that combines several of the
|
||||
patterns discussed above to create a secure, controlled environment for Gemini
|
||||
CLI.
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -376,7 +557,8 @@ Here is an example of a system `settings.json` file that combines several of the
|
||||
This configuration:
|
||||
|
||||
- Forces all tool execution into a Docker sandbox.
|
||||
- Strictly uses an allowlist for a small set of safe shell commands and file tools.
|
||||
- Strictly uses an allowlist for a small set of safe shell commands and file
|
||||
tools.
|
||||
- Defines and allows a single corporate MCP server for custom tools.
|
||||
- Enables telemetry for auditing, without logging prompt content.
|
||||
- Redirects the `/bug` command to an internal ticketing system.
|
||||
|
||||
@@ -1,22 +1,33 @@
|
||||
# Ignoring Files
|
||||
# Ignoring files
|
||||
|
||||
This document provides an overview of the Gemini Ignore (`.geminiignore`) feature of the Gemini CLI.
|
||||
This document provides an overview of the Gemini Ignore (`.geminiignore`)
|
||||
feature of the Gemini CLI.
|
||||
|
||||
The Gemini CLI includes the ability to automatically ignore files, similar to `.gitignore` (used by Git) and `.aiexclude` (used by Gemini Code Assist). Adding paths to your `.geminiignore` file will exclude them from tools that support this feature, although they will still be visible to other services (such as Git).
|
||||
The Gemini CLI includes the ability to automatically ignore files, similar to
|
||||
`.gitignore` (used by Git) and `.aiexclude` (used by Gemini Code Assist). Adding
|
||||
paths to your `.geminiignore` file will exclude them from tools that support
|
||||
this feature, although they will still be visible to other services (such as
|
||||
Git).
|
||||
|
||||
## How it works
|
||||
|
||||
When you add a path to your `.geminiignore` file, tools that respect this file will exclude matching files and directories from their operations. For example, when you use the [`read_many_files`](./tools/multi-file.md) command, any paths in your `.geminiignore` file will be automatically excluded.
|
||||
When you add a path to your `.geminiignore` file, tools that respect this file
|
||||
will exclude matching files and directories from their operations. For example,
|
||||
when you use the `@` command to share files, any paths in your `.geminiignore`
|
||||
file will be automatically excluded.
|
||||
|
||||
For the most part, `.geminiignore` follows the conventions of `.gitignore` files:
|
||||
For the most part, `.geminiignore` follows the conventions of `.gitignore`
|
||||
files:
|
||||
|
||||
- Blank lines and lines starting with `#` are ignored.
|
||||
- Standard glob patterns are supported (such as `*`, `?`, and `[]`).
|
||||
- Putting a `/` at the end will only match directories.
|
||||
- Putting a `/` at the beginning anchors the path relative to the `.geminiignore` file.
|
||||
- Putting a `/` at the beginning anchors the path relative to the
|
||||
`.geminiignore` file.
|
||||
- `!` negates a pattern.
|
||||
|
||||
You can update your `.geminiignore` file at any time. To apply the changes, you must restart your Gemini CLI session.
|
||||
You can update your `.geminiignore` file at any time. To apply the changes, you
|
||||
must restart your Gemini CLI session.
|
||||
|
||||
## How to use `.geminiignore`
|
||||
|
||||
@@ -27,7 +38,8 @@ To enable `.geminiignore`:
|
||||
To add a file or directory to `.geminiignore`:
|
||||
|
||||
1. Open your `.geminiignore` file.
|
||||
2. Add the path or file you want to ignore, for example: `/archive/` or `apikeys.txt`.
|
||||
2. Add the path or file you want to ignore, for example: `/archive/` or
|
||||
`apikeys.txt`.
|
||||
|
||||
### `.geminiignore` examples
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
# Provide context with GEMINI.md files
|
||||
|
||||
Context files, which use the default name `GEMINI.md`, are a powerful feature
|
||||
for providing instructional context to the Gemini model. You can use these files
|
||||
to give project-specific instructions, define a persona, or provide coding style
|
||||
guides to make the AI's responses more accurate and tailored to your needs.
|
||||
|
||||
Instead of repeating instructions in every prompt, you can define them once in a
|
||||
context file.
|
||||
|
||||
## Understand the context hierarchy
|
||||
|
||||
The CLI uses a hierarchical system to source context. It loads various context
|
||||
files from several locations, concatenates the contents of all found files, and
|
||||
sends them to the model with every prompt. The CLI loads files in the following
|
||||
order:
|
||||
|
||||
1. **Global context file:**
|
||||
- **Location:** `~/.gemini/GEMINI.md` (in your user home directory).
|
||||
- **Scope:** Provides default instructions for all your projects.
|
||||
|
||||
2. **Project root and ancestor context files:**
|
||||
- **Location:** The CLI searches for a `GEMINI.md` file in your current
|
||||
working directory and then in each parent directory up to the project root
|
||||
(identified by a `.git` folder).
|
||||
- **Scope:** Provides context relevant to the entire project.
|
||||
|
||||
3. **Sub-directory context files:**
|
||||
- **Location:** The CLI also scans for `GEMINI.md` files in subdirectories
|
||||
below your current working directory. It respects rules in `.gitignore`
|
||||
and `.geminiignore`.
|
||||
- **Scope:** Lets you write highly specific instructions for a particular
|
||||
component or module.
|
||||
|
||||
The CLI footer displays the number of loaded context files, which gives you a
|
||||
quick visual cue of the active instructional context.
|
||||
|
||||
### Example `GEMINI.md` file
|
||||
|
||||
Here is an example of what you can include in a `GEMINI.md` file at the root of
|
||||
a TypeScript project:
|
||||
|
||||
```markdown
|
||||
# Project: My TypeScript Library
|
||||
|
||||
## General Instructions
|
||||
|
||||
- When you generate new TypeScript code, follow the existing coding style.
|
||||
- Ensure all new functions and classes have JSDoc comments.
|
||||
- Prefer functional programming paradigms where appropriate.
|
||||
|
||||
## Coding Style
|
||||
|
||||
- Use 2 spaces for indentation.
|
||||
- Prefix interface names with `I` (for example, `IUserService`).
|
||||
- Always use strict equality (`===` and `!==`).
|
||||
```
|
||||
|
||||
## Manage context with the `/memory` command
|
||||
|
||||
You can interact with the loaded context files by using the `/memory` command.
|
||||
|
||||
- **`/memory show`**: Displays the full, concatenated content of the current
|
||||
hierarchical memory. This lets you inspect the exact instructional context
|
||||
being provided to the model.
|
||||
- **`/memory refresh`**: Forces a re-scan and reload of all `GEMINI.md` files
|
||||
from all configured locations.
|
||||
- **`/memory add <text>`**: Appends your text to your global
|
||||
`~/.gemini/GEMINI.md` file. This lets you add persistent memories on the fly.
|
||||
|
||||
## Modularize context with imports
|
||||
|
||||
You can break down large `GEMINI.md` files into smaller, more manageable
|
||||
components by importing content from other files using the `@file.md` syntax.
|
||||
This feature supports both relative and absolute paths.
|
||||
|
||||
**Example `GEMINI.md` with imports:**
|
||||
|
||||
```markdown
|
||||
# Main GEMINI.md file
|
||||
|
||||
This is the main content.
|
||||
|
||||
@./components/instructions.md
|
||||
|
||||
More content here.
|
||||
|
||||
@../shared/style-guide.md
|
||||
```
|
||||
|
||||
For more details, see the [Memory Import Processor](../core/memport.md)
|
||||
documentation.
|
||||
|
||||
## Customize the context file name
|
||||
|
||||
While `GEMINI.md` is the default filename, you can configure this in your
|
||||
`settings.json` file. To specify a different name or a list of names, use the
|
||||
`context.fileName` property.
|
||||
|
||||
**Example `settings.json`:**
|
||||
|
||||
```json
|
||||
{
|
||||
"context": {
|
||||
"fileName": ["AGENTS.md", "CONTEXT.md", "GEMINI.md"]
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -0,0 +1,210 @@
|
||||
# Advanced Model Configuration
|
||||
|
||||
This guide details the Model Configuration system within the Gemini CLI.
|
||||
Designed for researchers, AI quality engineers, and advanced users, this system
|
||||
provides a rigorous framework for managing generative model hyperparameters and
|
||||
behaviors.
|
||||
|
||||
> **Warning**: This is a power-user feature. Configuration values are passed
|
||||
> directly to the model provider with minimal validation. Incorrect settings
|
||||
> (e.g., incompatible parameter combinations) may result in runtime errors from
|
||||
> the API.
|
||||
|
||||
## 1. System Overview
|
||||
|
||||
The Model Configuration system (`ModelConfigService`) enables deterministic
|
||||
control over model generation. It decouples the requested model identifier
|
||||
(e.g., a CLI flag or agent request) from the underlying API configuration. This
|
||||
allows for:
|
||||
|
||||
- **Precise Hyperparameter Tuning**: Direct control over `temperature`, `topP`,
|
||||
`thinkingBudget`, and other SDK-level parameters.
|
||||
- **Environment-Specific Behavior**: Distinct configurations for different
|
||||
operating contexts (e.g., testing vs. production).
|
||||
- **Agent-Scoped Customization**: Applying specific settings only when a
|
||||
particular agent is active.
|
||||
|
||||
The system operates on two core primitives: **Aliases** and **Overrides**.
|
||||
|
||||
## 2. Configuration Primitives
|
||||
|
||||
These settings are located under the `modelConfigs` key in your configuration
|
||||
file.
|
||||
|
||||
### Aliases (`customAliases`)
|
||||
|
||||
Aliases are named, reusable configuration presets. Users should define their own
|
||||
aliases (or override system defaults) in the `customAliases` map.
|
||||
|
||||
- **Inheritance**: An alias can `extends` another alias (including system
|
||||
defaults like `chat-base`), inheriting its `modelConfig`. Child aliases can
|
||||
overwrite or augment inherited settings.
|
||||
- **Abstract Aliases**: An alias is not required to specify a concrete `model`
|
||||
if it serves purely as a base for other aliases.
|
||||
|
||||
**Example Hierarchy**:
|
||||
|
||||
```json
|
||||
"modelConfigs": {
|
||||
"customAliases": {
|
||||
"base": {
|
||||
"modelConfig": {
|
||||
"generateContentConfig": { "temperature": 0.0 }
|
||||
}
|
||||
},
|
||||
"chat-base": {
|
||||
"extends": "base",
|
||||
"modelConfig": {
|
||||
"generateContentConfig": { "temperature": 0.7 }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Overrides (`overrides`)
|
||||
|
||||
Overrides are conditional rules that inject configuration based on the runtime
|
||||
context. They are evaluated dynamically for each model request.
|
||||
|
||||
- **Match Criteria**: Overrides apply when the request context matches the
|
||||
specified `match` properties.
|
||||
- `model`: Matches the requested model name or alias.
|
||||
- `overrideScope`: Matches the distinct scope of the request (typically the
|
||||
agent name, e.g., `codebaseInvestigator`).
|
||||
|
||||
**Example Override**:
|
||||
|
||||
```json
|
||||
"modelConfigs": {
|
||||
"overrides": [
|
||||
{
|
||||
"match": {
|
||||
"overrideScope": "codebaseInvestigator"
|
||||
},
|
||||
"modelConfig": {
|
||||
"generateContentConfig": { "temperature": 0.1 }
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Resolution Strategy
|
||||
|
||||
The `ModelConfigService` resolves the final configuration through a two-step
|
||||
process:
|
||||
|
||||
### Step 1: Alias Resolution
|
||||
|
||||
The requested model string is looked up in the merged map of system `aliases`
|
||||
and user `customAliases`.
|
||||
|
||||
1. If found, the system recursively resolves the `extends` chain.
|
||||
2. Settings are merged from parent to child (child wins).
|
||||
3. This results in a base `ResolvedModelConfig`.
|
||||
4. If not found, the requested string is treated as the raw model name.
|
||||
|
||||
### Step 2: Override Application
|
||||
|
||||
The system evaluates the `overrides` list against the request context (`model`
|
||||
and `overrideScope`).
|
||||
|
||||
1. **Filtering**: All matching overrides are identified.
|
||||
2. **Sorting**: Matches are prioritized by **specificity** (the number of
|
||||
matched keys in the `match` object).
|
||||
- Specific matches (e.g., `model` + `overrideScope`) override broad matches
|
||||
(e.g., `model` only).
|
||||
- Tie-breaking: If specificity is equal, the order of definition in the
|
||||
`overrides` array is preserved (last one wins).
|
||||
3. **Merging**: The configurations from the sorted overrides are merged
|
||||
sequentially onto the base configuration.
|
||||
|
||||
## 4. Configuration Reference
|
||||
|
||||
The configuration follows the `ModelConfigServiceConfig` interface.
|
||||
|
||||
### `ModelConfig` Object
|
||||
|
||||
Defines the actual parameters for the model.
|
||||
|
||||
| Property | Type | Description |
|
||||
| :---------------------- | :------- | :----------------------------------------------------------------- |
|
||||
| `model` | `string` | The identifier of the model to be called (e.g., `gemini-2.5-pro`). |
|
||||
| `generateContentConfig` | `object` | The configuration object passed to the `@google/genai` SDK. |
|
||||
|
||||
### `GenerateContentConfig` (Common Parameters)
|
||||
|
||||
Directly maps to the SDK's `GenerateContentConfig`. Common parameters include:
|
||||
|
||||
- **`temperature`**: (`number`) Controls output randomness. Lower values (0.0)
|
||||
are deterministic; higher values (>0.7) are creative.
|
||||
- **`topP`**: (`number`) Nucleus sampling probability.
|
||||
- **`maxOutputTokens`**: (`number`) Limit on generated response length.
|
||||
- **`thinkingConfig`**: (`object`) Configuration for models with reasoning
|
||||
capabilities (e.g., `thinkingBudget`, `includeThoughts`).
|
||||
|
||||
## 5. Practical Examples
|
||||
|
||||
### Defining a Deterministic Baseline
|
||||
|
||||
Create an alias for tasks requiring high precision, extending the standard chat
|
||||
configuration but enforcing zero temperature.
|
||||
|
||||
```json
|
||||
"modelConfigs": {
|
||||
"customAliases": {
|
||||
"precise-mode": {
|
||||
"extends": "chat-base",
|
||||
"modelConfig": {
|
||||
"generateContentConfig": {
|
||||
"temperature": 0.0,
|
||||
"topP": 1.0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Agent-Specific Parameter Injection
|
||||
|
||||
Enforce extended thinking budgets for a specific agent without altering the
|
||||
global default, e.g. for the `codebaseInvestigator`.
|
||||
|
||||
```json
|
||||
"modelConfigs": {
|
||||
"overrides": [
|
||||
{
|
||||
"match": {
|
||||
"overrideScope": "codebaseInvestigator"
|
||||
},
|
||||
"modelConfig": {
|
||||
"generateContentConfig": {
|
||||
"thinkingConfig": { "thinkingBudget": 4096 }
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Experimental Model Evaluation
|
||||
|
||||
Route traffic for a specific alias to a preview model for A/B testing, without
|
||||
changing client code.
|
||||
|
||||
```json
|
||||
"modelConfigs": {
|
||||
"overrides": [
|
||||
{
|
||||
"match": {
|
||||
"model": "gemini-2.5-pro"
|
||||
},
|
||||
"modelConfig": {
|
||||
"model": "gemini-2.5-pro-experimental-001"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user