commit 4c1e7a7eb92c3bfa407e023b254d9fd07230ba1c Author: rootiest Date: Sun Apr 26 00:59:48 2026 -0400 feat: initial fish shell configuration - Core config layered on CachyOS base with Catppuccin Mocha theming - Fisher plugins: fzf.fish, catppuccin, autopair, replay, puffer-fish, magic-enter, spark - Smart CLI wrappers with fallbacks (bat, lsd, btop, dust, prettyping) - Custom functions: git, docker, network, kitty, AI session management - Extensive abbreviation system for keyboard-driven workflows - Secrets/local sourcing pattern for private and machine-specific config - README with full documentation and personalization guide - AGPLv3+ license with copyright headers on all source files diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ea76ca --- /dev/null +++ b/.gitignore @@ -0,0 +1,14 @@ +# Debug Files/Folders +OLD/ +*.OLD +*.DISABLE +*.DISABLED +*_old + +# AI Session IDs +.claude_session +.gemini_session +.remember/ + +# Auto-managed by fish; contains machine-local state and universal vars +fish_variables diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e20b431 --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e53dbb3 --- /dev/null +++ b/README.md @@ -0,0 +1,526 @@ +# Fish Shell Configuration + +A feature-rich Fish shell configuration for CachyOS (Arch Linux), built around a Catppuccin Mocha aesthetic with a curated set of modern CLI tool integrations, smart shell functions, and a heavily customized abbreviation system for keyboard-driven workflows. + +## Table of Contents + +- [Overview](#overview) +- [Structure](#structure) +- [Plugins](#plugins) +- [Theme & Prompt](#theme--prompt) +- [Integrations](#integrations) +- [Functions](#functions) +- [Abbreviations](#abbreviations) +- [Dependencies](#dependencies) +- [Installation](#installation) +- [Personalization](#personalization) +- [Full Requirements](#full-requirements) + +--- + +## Overview + +This config layers on top of the CachyOS base Fish configuration and adds: + +- **Catppuccin Mocha** theming throughout (prompt, FZF, Zellij) +- **Starship** prompt with Bobthefish fallback color definitions +- **Fisher** plugin management with FZF key bindings and Catppuccin syntax colors +- **Smart CLI wrappers** that prefer modern tools (`bat`, `lsd`, `btop`, `dust`, `prettyping`) with graceful fallbacks +- **Auto Python venv** activation on directory change (direnv-aware) +- **Kitty terminal** deep integration for splits, tabs, and SSH +- **AI workflow** helpers for Claude and Gemini session management +- **WakaTime** shell activity tracking + +--- + +## Structure + +``` +~/.config/fish/ +├── config.fish # Main entry point +├── fish_plugins # Fisher plugin list +├── fish_variables # Universal variables +├── conf.d/ # Auto-sourced configuration fragments +│ ├── abbr.fish # All abbreviations +│ ├── bobthefish.fish # Bobthefish theme + VI key bindings +│ ├── cheat.fish # cheat.sh completions +│ ├── fzf.fish # FZF key binding initialization +│ ├── tailscale.fish # Tailscale CLI completions +│ ├── wakatime.fish # WakaTime shell hook +│ └── zoxide.fish # Zoxide z/zi aliases +├── functions/ # Custom functions (one per file) +├── completions/ # Custom tab completions +├── integrations/ # Integration scripts +│ └── fzf.fish # FZF theme and binding config +└── themes/ # Catppuccin theme files + ├── Catppuccin Mocha.theme + ├── Catppuccin Macchiato.theme + ├── Catppuccin Frappe.theme + └── Catppuccin Latte.theme +``` + +--- + +## Plugins + +Managed via [Fisher](https://github.com/jorgebucaran/fisher): + +| Plugin | Purpose | +|---|---| +| `jorgebucaran/fisher` | Plugin manager | +| `patrickf1/fzf.fish` | FZF key bindings for history, files, processes, git | +| `catppuccin/fish` | Catppuccin Mocha syntax highlighting | +| `jorgebucaran/autopair.fish` | Auto-close brackets, quotes, and other pairs | +| `jorgebucaran/replay.fish` | Run bash commands in fish without losing state | +| `nickeb96/puffer-fish` | Expand `...` to `../..`, `!!` to last command, etc. | +| `mattmc3/magic-enter.fish` | Smart Enter: runs `ls` / `git status` on blank line | +| `jorgebucaran/spark.fish` | Sparkline bar charts in the terminal | + +Install plugins after cloning: + +```fish +fisher update +``` + +--- + +## Theme & Prompt + +### Starship + +The primary prompt is [Starship](https://starship.rs/), initialized in `config.fish`. Configure it via `~/.config/starship.toml`. + +### FZF + +FZF is themed to Catppuccin Mocha with the following colors set via `FZF_DEFAULT_OPTS`: + +- Background: `#1E1E2E` / `#313244` +- Foreground: `#CDD6F4` +- Highlights: `#F38BA8` (red), `#CBA6F7` (mauve), `#B4BEFE` (lavender) + +**Key bindings** (from `fzf.fish`): + +| Binding | Action | +|---|---| +| `Ctrl+R` | Search command history | +| `Ctrl+F` | Search directory files | +| `Ctrl+Alt+F` | Search git-tracked files | +| `Ctrl+Alt+L` | Search git log | +| `Ctrl+Alt+S` | Search git status | +| `Ctrl+V` | Search shell variables | +| `Ctrl+Alt+P` | Search running processes | + +--- + +## Integrations + +### Zoxide + +Smart `cd` replacement. `z ` jumps to the best frecency match; `zi` opens an interactive selector. + +### DirEnv + +Automatically loads `.envrc` files on directory change. Takes priority over the built-in auto-venv logic. + +### Auto Python Venv + +When entering a directory containing a `.venv/`, the virtualenv is automatically activated. It is deactivated when you leave the project tree. DirEnv-managed directories are skipped to avoid conflicts. + +### WakaTime + +Every shell command is reported to WakaTime for time-tracking. Disable by setting `FISH_WAKATIME_DISABLED=1`. + +### Tailscale + +Full tab completion for the `tailscale` CLI is provided via `conf.d/tailscale.fish`. + +--- + +## Functions + +### Modern CLI Replacements + +These functions wrap modern alternatives with graceful fallbacks to standard tools. + +| Function | Replaces | Tool | +|---|---|---| +| `ls` | `ls` | `lsd` with hyperlinks | +| `cat` | `cat` | `bat` (plain, no pager) | +| `less` | `less` | `most` | +| `ping` | `ping` | `prettyping --nolegend` | +| `top` | `top` | `btop` | +| `rg` | `rg` | ripgrep with `--hyperlink-format=kitty` | +| `ssh` | `ssh` | `kitten ssh` when inside Kitty | +| `du` | `du` | `duf` (disks) / `dust` (directories) — auto-detected by argument | +| `mkdir` | `mkdir` | Always passes `-p` in interactive mode | + +#### `du` — Smart Disk Usage + +```fish +du # → duf (disk overview) +du /some/dir # → dust (directory breakdown) +du --disk # → duf (force disk view) +du --dir # → dust (force directory view) +du --dua # → dua (interactive mode) +``` + +#### `rm` — Trash-Aware Remove + +```fish +rm # List current trash contents +rm file.txt # Move to trash (recoverable) +rm -r dir/ # Move directory to trash +rm -e # Empty all trash +rm -e --within 2weeks # Empty trash older than 2 weeks +rm -S file.txt # Permanent secure delete + fstrim +rm -f file.txt # Falls through to standard rm -f +``` + +### Directory & File Listing + +| Function | Description | +|---|---| +| `l` | `lsd -la` — long listing with git status and header | +| `ls` | `lsd` with hyperlinks | +| `lt` | `lsd --tree --depth=2` — shallow tree | +| `ltr` | Reversed time-sorted listing | +| `lS` | Size-sorted listing | +| `llm` | Long listing sorted by modification time | +| `lstree` | Full recursive tree via lsd | + +### Git + +| Function | Description | +|---|---| +| `git-clean` | Fetch, prune, update current branch, delete orphaned local branches | +| `git-clean --force` | Same but force-deletes unmerged orphaned branches | +| `clone` | `clone-in-kitty` wrapper | + +### Package Management (Arch / paru) + +| Function | Description | +|---|---| +| `pkg ` | Install package: `paru -S ` | +| `search ` | Search/install interactively: `paru ` | +| `upgrade` | Full system upgrade: `paru -Syu --noconfirm` | +| `cleanup` | Log and remove orphaned packages | + +### Docker + +| Function | Description | +|---|---| +| `ld` / `lzd` | Launch LazyDocker using the currently active Docker context | +| `dockup [dir]` | Pull latest images and restart docker compose services | +| `docker ps` | Intercepted to use `dops` for a prettier process listing | + +### Network + +| Function | Description | +|---|---| +| `gip` | Show both public IPv4 and IPv6 addresses | +| `gip4` | Show public IPv4 address only | +| `gip6` | Show public IPv6 address (or error if unavailable) | +| `ports` | List all active TCP listeners via `lsof` | + +### Clipboard + +| Function | Description | +|---|---| +| `y ` | Copy text to clipboard (Wayland `wl-copy` or X11 `xclip`) | +| `cb ` | Copy to clipboard (alias for `y`) | +| `paste` | Paste from clipboard to stdout | + +### Kitty Terminal + +| Function | Description | +|---|---| +| `split [-h\|-v] [cmd]` | Open a new Kitty split pane, optionally running a command | +| `spwin` | Spawn a new Kitty OS window via `spawn-window.sh` | +| `detach ` | Run a command fully detached (`nohup`), no output | +| `bkg ` | Background a command, discarding all output | + +### System + +| Function | Description | +|---|---| +| `lock` | Lock the session via `loginctl lock-session` | +| `screensleep` | Turn off the display via KDE PowerDevil | +| `wake-lock ` | Run a command with `systemd-inhibit` to prevent sleep | +| `swapstat` | Colorized zRAM compression ratio, swappiness, and swap priority report | +| `monitors` | Open a 4-pane Kitty layout running `btop` locally and on remote servers | +| `tmux-clean` | Kill all detached tmux sessions | + +### Editors & Development + +| Function | Description | +|---|---| +| `edit` / `e` | Open in Neovim | +| `view` | Open in Neovim read-only mode | +| `nvimup` | Update Neovim headlessly | +| `nlazyup` | Sync Lazy.nvim plugins headlessly | + +### AI Assistants + +| Function | Description | +|---|---| +| `claude-resume` | Resume Claude Code session from `.claude_session` in CWD | +| `gemini-resume` | Resume Gemini CLI session from `.gemini_session` in CWD | +| `code-resume` | Smart resume — tries Claude then Gemini, falls back to picker | +| `superpowers [on\|off]` | Enable/disable the Superpowers extension for Claude and Gemini | + +### Fetch & Info + +| Function | Description | +|---|---| +| `ffetch` | Run fastfetch with `~/.fastfetch.jsonc` if present | +| `cffetch` | Clear screen then run fastfetch | +| `hist` | FZF history search — selected command is placed in the prompt and copied to clipboard | +| `qr ` | Generate a terminal QR code | + +### Miscellaneous + +| Function | Description | +|---|---| +| `upgrade` | System upgrade via paru | +| `zellij` | Zellij with `--theme catppuccin-mocha` | +| `antigravity` | Wrapper that suppresses a noisy deprecation warning | +| `bash` | Drop into bash (raw Fish session via `rawfish`) | +| `sbver` | Show system/binary versions | + +--- + +## Abbreviations + +Abbreviations expand in-place as you type, keeping your history clean. + +### Editors + +| Abbr | Expands To | +|---|---| +| `n`, `nv` | `nvim` | +| `e` | `edit` | +| `se` | `sudoedit` | +| `v` | `antigravity` (VSCode-equivalent) | +| `k` | `kate` | + +### Navigation + +| Abbr | Expands To | +|---|---| +| `cdnv` | `cd ~/.config/nvim` | +| `:cdf` | `cd ~/.config/fish/` | +| `:cdk` | `cd ~/.config/kitty/` | +| `:cdh` | `cd ~` | +| `:cdp` | `cd ~/projects/` (with cursor placement) | +| `:cdcz` | `cd ~/.local/share/chezmoi/` | +| `cdnote` | `cd ~/Documents/Rootiest Notes/` | + +### Git + +| Abbr | Expands To | +|---|---| +| `g` | `git` | +| `lg` | `lazygit` | + +### Chezmoi + +| Abbr | Expands To | +|---|---| +| `cm` / `cz` | `chezmoi` | +| `cme` | `chezmoi edit` | +| `cmad` | `chezmoi add` | +| `cmap` | `chezmoi apply` | +| `cmf` | `chezmoi forget` | +| `cmi` | `chezmoi init` | + +### Kitty Window Management + +These abbreviations mirror Vim/tmux ergonomics for managing Kitty splits, tabs, and windows. + +| Abbr | Action | +|---|---| +| `:q` | Close active pane | +| `:Q` | Close active tab | +| `:w` | New OS window | +| `:t` | New tab | +| `:wv` | Horizontal split | +| `:wh` | Vertical split | +| `:tp` / `:tn` | Navigate tabs left/right | +| `:tl "Title"` | Rename current tab | +| `:tgn` | New tab in `~/.config/nvim` | +| `:tgf` | New tab in `~/.config/fish` | +| `:tgp` | New tab in `~/projects` | +| `:tgr` | New root tab (`sudo -i`) | + +### SSH + +| Abbr | Expands To | +|---|---| +| `sshr` | `ssh rootiest@rootiest-server.local` | +| `sshrt` | `ssh rootiest-server` | + +### Docker + +| Abbr | Expands To | +|---|---| +| `dcr` | `docker context use rootiest` | +| `dcl` | `docker context use default` | +| `dck` | `docker context use racknerd` | +| `dcls` | `docker context ls` | +| `lzd` | `ld` (LazyDocker) | + +### Systemctl + +| Abbr | Expands To | +|---|---| +| `sc` | `systemctl` | +| `ssc` | `sudo systemctl` | +| `scu` | `systemctl --user` | +| `st` | `systemctl status` | +| `scr` | `systemctl restart` | + +### Beads (bd) + +| Abbr | Expands To | +|---|---| +| `bl` | `bd list` | +| `bs` | `bd sync` | +| `bc` | `bd create --title` | +| `lb` | `lazybeads` | + +--- + +## Dependencies + +### Required + +| Tool | Purpose | +|---|---| +| [Fish](https://fishshell.com/) | Shell | +| [Fisher](https://github.com/jorgebucaran/fisher) | Plugin manager | +| [Starship](https://starship.rs/) | Prompt | +| [fzf](https://github.com/junegunn/fzf) | Fuzzy finder | +| [zoxide](https://github.com/ajeetdsouza/zoxide) | Smart directory jumper | +| [direnv](https://direnv.net/) | Per-directory env loading | +| [paru](https://github.com/Morganamilo/paru) | AUR helper | + +### Recommended + +| Tool | Replaces | +|---|---| +| [lsd](https://github.com/lsd-rs/lsd) | `ls` | +| [bat](https://github.com/sharkdp/bat) | `cat` | +| [btop](https://github.com/aristocratsupply/btop) | `top` | +| [dust](https://github.com/bootandy/dust) | `du` (directories) | +| [duf](https://github.com/muesli/duf) | `du` (disks) | +| [prettyping](https://github.com/denilsonsa/prettyping) | `ping` | +| [most](https://www.jedsoft.org/most/) | `less` | +| [ripgrep](https://github.com/BurntSushi/ripgrep) | `grep` | +| [lazygit](https://github.com/jesseduffield/lazygit) | git TUI | +| [lazydocker](https://github.com/jesseduffield/lazydocker) | Docker TUI | +| [trash-cli](https://github.com/andreafrancia/trash-cli) | Safe `rm` | +| [Kitty](https://sw.kovidgoyal.net/kitty/) | Terminal emulator | +| [WakaTime](https://wakatime.com/) | Activity tracking | + +--- + +## Installation + +This config is managed as a Git repository. To use it on a new machine: + +```fish +# Back up any existing config +mv ~/.config/fish ~/.config/fish.bak + +# Clone this repo +git clone ~/.config/fish + +# Install Fisher and plugins +curl -sL https://raw.githubusercontent.com/jorgebucaran/fisher/main/functions/fisher.fish | source +fisher update + +# Apply the Catppuccin Mocha theme +fish_config theme save "Catppuccin Mocha" +``` + +A [chezmoi](https://www.chezmoi.io/) dotfile manager is also configured — secrets are sourced from `~/.config/.user-dots/fish/secrets.fish` and excluded from version control. + +--- + +## Personalization + +Sensitive credentials and machine-specific paths are kept out of version control via a secondary private directory at `~/.config/.user-dots/fish/`. Two files are sourced automatically by `config.fish` if they exist: + +``` +~/.config/.user-dots/fish/ +├── secrets.fish # API keys, tokens, passwords, personal identifiers +└── local.fish # Machine-specific paths and environment variables +``` + +### secrets.fish + +Use this file for anything you would not commit to a public repo: API keys, auth tokens, passwords, and personal identifiers like usernames or email addresses. + +```fish +# ~/.config/.user-dots/fish/secrets.fish + +### Identity ### +set -gx MY_NAME "Your Name" +set -gx MY_EMAIL "you@example.com" +set -gx GPG_RECIPIENT "you@example.com" + +### API Keys & Tokens ### +set -gx GITHUB_TOKEN ghp_yourTokenHere +set -gx OPENAI_API_KEY sk-proj-yourKeyHere +set -gx GITEA_TOKEN yourGiteaTokenHere +set -gx GITEA_CHOSEN_LOGIN your.gitea.instance +set -gx TEA_LOGIN your.gitea.instance + +### Backup ### +set -gx KOPIA_PASSWORD yourKopiaPassword +``` + +### local.fish + +Use this file for paths and variables that are specific to one machine — things that would break or be wrong on any other system. + +```fish +# ~/.config/.user-dots/fish/local.fish + +# Project root for quick cd +set -gx cdp /home/youruser/projects + +# CDPATH — directories searched by cd +set -gx CDPATH . /home/youruser/projects /home/youruser +``` + +### How it works + +`config.fish` sources both files with an existence check so the public config works cleanly on any machine that doesn't have the private repo: + +```fish +if test -f $HOME/.config/.user-dots/fish/secrets.fish + source $HOME/.config/.user-dots/fish/secrets.fish +end + +if test -f $HOME/.config/.user-dots/fish/local.fish + source $HOME/.config/.user-dots/fish/local.fish +end +``` + +`fish_variables` (which fish auto-manages and may contain universal variable state) is excluded from this repo via `.gitignore`. + +--- + +## Full Requirements + +For a complete, categorized list of all non-standard tools required or used by this configuration, see [requirements.md](requirements.md). + +--- + +## License + +Copyright (C) 2026 Rootiest + +This project is licensed under the **GNU Affero General Public License v3.0 or later** (AGPLv3+). +See the [LICENSE](LICENSE) file for the full license text. diff --git a/completions/bd.fish b/completions/bd.fish new file mode 100644 index 0000000..2605beb --- /dev/null +++ b/completions/bd.fish @@ -0,0 +1,235 @@ +# fish completion for bd -*- shell-script -*- + +function __bd_debug + set -l file "$BASH_COMP_DEBUG_FILE" + if test -n "$file" + echo "$argv" >> $file + end +end + +function __bd_perform_completion + __bd_debug "Starting __bd_perform_completion" + + # Extract all args except the last one + set -l args (commandline -opc) + # Extract the last arg and escape it in case it is a space + set -l lastArg (string escape -- (commandline -ct)) + + __bd_debug "args: $args" + __bd_debug "last arg: $lastArg" + + # Disable ActiveHelp which is not supported for fish shell + set -l requestComp "BD_ACTIVE_HELP=0 $args[1] __complete $args[2..-1] $lastArg" + + __bd_debug "Calling $requestComp" + set -l results (eval $requestComp 2> /dev/null) + + # Some programs may output extra empty lines after the directive. + # Let's ignore them or else it will break completion. + # Ref: https://github.com/spf13/cobra/issues/1279 + for line in $results[-1..1] + if test (string trim -- $line) = "" + # Found an empty line, remove it + set results $results[1..-2] + else + # Found non-empty line, we have our proper output + break + end + end + + set -l comps $results[1..-2] + set -l directiveLine $results[-1] + + # For Fish, when completing a flag with an = (e.g., -n=) + # completions must be prefixed with the flag + set -l flagPrefix (string match -r -- '-.*=' "$lastArg") + + __bd_debug "Comps: $comps" + __bd_debug "DirectiveLine: $directiveLine" + __bd_debug "flagPrefix: $flagPrefix" + + for comp in $comps + printf "%s%s\n" "$flagPrefix" "$comp" + end + + printf "%s\n" "$directiveLine" +end + +# this function limits calls to __bd_perform_completion, by caching the result behind $__bd_perform_completion_once_result +function __bd_perform_completion_once + __bd_debug "Starting __bd_perform_completion_once" + + if test -n "$__bd_perform_completion_once_result" + __bd_debug "Seems like a valid result already exists, skipping __bd_perform_completion" + return 0 + end + + set --global __bd_perform_completion_once_result (__bd_perform_completion) + if test -z "$__bd_perform_completion_once_result" + __bd_debug "No completions, probably due to a failure" + return 1 + end + + __bd_debug "Performed completions and set __bd_perform_completion_once_result" + return 0 +end + +# this function is used to clear the $__bd_perform_completion_once_result variable after completions are run +function __bd_clear_perform_completion_once_result + __bd_debug "" + __bd_debug "========= clearing previously set __bd_perform_completion_once_result variable ==========" + set --erase __bd_perform_completion_once_result + __bd_debug "Successfully erased the variable __bd_perform_completion_once_result" +end + +function __bd_requires_order_preservation + __bd_debug "" + __bd_debug "========= checking if order preservation is required ==========" + + __bd_perform_completion_once + if test -z "$__bd_perform_completion_once_result" + __bd_debug "Error determining if order preservation is required" + return 1 + end + + set -l directive (string sub --start 2 $__bd_perform_completion_once_result[-1]) + __bd_debug "Directive is: $directive" + + set -l shellCompDirectiveKeepOrder 32 + set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) % 2) + __bd_debug "Keeporder is: $keeporder" + + if test $keeporder -ne 0 + __bd_debug "This does require order preservation" + return 0 + end + + __bd_debug "This doesn't require order preservation" + return 1 +end + + +# This function does two things: +# - Obtain the completions and store them in the global __bd_comp_results +# - Return false if file completion should be performed +function __bd_prepare_completions + __bd_debug "" + __bd_debug "========= starting completion logic ==========" + + # Start fresh + set --erase __bd_comp_results + + __bd_perform_completion_once + __bd_debug "Completion results: $__bd_perform_completion_once_result" + + if test -z "$__bd_perform_completion_once_result" + __bd_debug "No completion, probably due to a failure" + # Might as well do file completion, in case it helps + return 1 + end + + set -l directive (string sub --start 2 $__bd_perform_completion_once_result[-1]) + set --global __bd_comp_results $__bd_perform_completion_once_result[1..-2] + + __bd_debug "Completions are: $__bd_comp_results" + __bd_debug "Directive is: $directive" + + set -l shellCompDirectiveError 1 + set -l shellCompDirectiveNoSpace 2 + set -l shellCompDirectiveNoFileComp 4 + set -l shellCompDirectiveFilterFileExt 8 + set -l shellCompDirectiveFilterDirs 16 + + if test -z "$directive" + set directive 0 + end + + set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) + if test $compErr -eq 1 + __bd_debug "Received error directive: aborting." + # Might as well do file completion, in case it helps + return 1 + end + + set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) + set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) + if test $filefilter -eq 1; or test $dirfilter -eq 1 + __bd_debug "File extension filtering or directory filtering not supported" + # Do full file completion instead + return 1 + end + + set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) + set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) + + __bd_debug "nospace: $nospace, nofiles: $nofiles" + + # If we want to prevent a space, or if file completion is NOT disabled, + # we need to count the number of valid completions. + # To do so, we will filter on prefix as the completions we have received + # may not already be filtered so as to allow fish to match on different + # criteria than the prefix. + if test $nospace -ne 0; or test $nofiles -eq 0 + set -l prefix (commandline -t | string escape --style=regex) + __bd_debug "prefix: $prefix" + + set -l completions (string match -r -- "^$prefix.*" $__bd_comp_results) + set --global __bd_comp_results $completions + __bd_debug "Filtered completions are: $__bd_comp_results" + + # Important not to quote the variable for count to work + set -l numComps (count $__bd_comp_results) + __bd_debug "numComps: $numComps" + + if test $numComps -eq 1; and test $nospace -ne 0 + # We must first split on \t to get rid of the descriptions to be + # able to check what the actual completion will be. + # We don't need descriptions anyway since there is only a single + # real completion which the shell will expand immediately. + set -l split (string split --max 1 \t $__bd_comp_results[1]) + + # Fish won't add a space if the completion ends with any + # of the following characters: @=/:., + set -l lastChar (string sub -s -1 -- $split) + if not string match -r -q "[@=/:.,]" -- "$lastChar" + # In other cases, to support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __bd_debug "Adding second completion to perform nospace directive" + set --global __bd_comp_results $split[1] $split[1]. + __bd_debug "Completions are now: $__bd_comp_results" + end + end + + if test $numComps -eq 0; and test $nofiles -eq 0 + # To be consistent with bash and zsh, we only trigger file + # completion when there are no other completions + __bd_debug "Requesting file completion" + return 1 + end + end + + return 0 +end + +# Since Fish completions are only loaded once the user triggers them, we trigger them ourselves +# so we can properly delete any completions provided by another script. +# Only do this if the program can be found, or else fish may print some errors; besides, +# the existing completions will only be loaded if the program can be found. +if type -q "bd" + # The space after the program name is essential to trigger completion for the program + # and not completion of the program name itself. + # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. + complete --do-complete "bd " > /dev/null 2>&1 +end + +# Remove any pre-existing completions for the program since we will be handling all of them. +complete -c bd -e + +# this will get called after the two calls below and clear the $__bd_perform_completion_once_result global +complete -c bd -n '__bd_clear_perform_completion_once_result' +# The call to __bd_prepare_completions will setup __bd_comp_results +# which provides the program's completion choices. +# If this doesn't require order preservation, we don't use the -k flag +complete -c bd -n 'not __bd_requires_order_preservation && __bd_prepare_completions' -f -a '$__bd_comp_results' +# otherwise we use the -k flag +complete -k -c bd -n '__bd_requires_order_preservation && __bd_prepare_completions' -f -a '$__bd_comp_results' diff --git a/completions/deadbranch.fish b/completions/deadbranch.fish new file mode 100644 index 0000000..8b41569 --- /dev/null +++ b/completions/deadbranch.fish @@ -0,0 +1,121 @@ +# Print an optspec for argparse to handle cmd's options that are independent of any subcommand. +function __fish_deadbranch_global_optspecs + string join \n h/help V/version +end + +function __fish_deadbranch_needs_command + # Figure out if the current invocation already has a command. + set -l cmd (commandline -opc) + set -e cmd[1] + argparse -s (__fish_deadbranch_global_optspecs) -- $cmd 2>/dev/null + or return + if set -q argv[1] + # Also print the command, so this can be used to figure out what it is. + echo $argv[1] + return 1 + end + return 0 +end + +function __fish_deadbranch_using_subcommand + set -l cmd (__fish_deadbranch_needs_command) + test -z "$cmd" + and return 1 + contains -- $cmd[1] $argv +end + +complete -c deadbranch -n "__fish_deadbranch_needs_command" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "list" -d 'List stale branches' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "clean" -d 'Delete stale branches (merged only by default, use --force for unmerged)' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "config" -d 'Manage configuration' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "backup" -d 'Manage backups' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "stats" -d 'Show repository branch statistics' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "completions" -d 'Generate shell completion scripts' +complete -c deadbranch -n "__fish_deadbranch_needs_command" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -s d -l days -d 'Only show branches older than N days (default: from config or 30)' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l local -d 'Only show local branches' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l remote -d 'Only show remote branches' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -l merged -d 'Only show merged branches' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand list" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s d -l days -d 'Only delete branches older than N days (default: from config or 30)' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -l merged -d 'Only delete merged branches (this is the default behavior)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -l force -d 'Force delete unmerged branches (dangerous!)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -l dry-run -d 'Show what would be deleted without doing it' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -l local -d 'Only delete local branches' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -l remote -d 'Only delete remote branches' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s y -l yes -d 'Skip confirmation prompts (useful for scripts)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s i -l interactive -d 'Open interactive TUI for branch selection' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand clean" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "set" -d 'Set a configuration value' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "show" -d 'Show current configuration' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "edit" -d 'Open config file in $EDITOR' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "reset" -d 'Reset configuration to defaults' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and not __fish_seen_subcommand_from set show edit reset help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from set" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from set" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from show" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from show" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from edit" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from edit" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from reset" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from reset" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "set" -d 'Set a configuration value' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "show" -d 'Show current configuration' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "edit" -d 'Open config file in $EDITOR' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "reset" -d 'Reset configuration to defaults' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand config; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "list" -d 'List available backups' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "restore" -d 'Restore a branch from backup' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "stats" -d 'Show backup storage statistics' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "clean" -d 'Remove old backups, keeping the most recent ones' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and not __fish_seen_subcommand_from list restore stats clean help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -l repo -d 'Show backups for a specific repository by name' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -l current -d 'Only show backups for current repository' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from list" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from restore" -l from -d 'Restore from a specific backup file (defaults to most recent)' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from restore" -l as -d 'Restore with a different branch name' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from restore" -l force -d 'Overwrite existing branch if it exists' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from restore" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from restore" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from stats" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from stats" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -l repo -d 'Clean backups for a specific repository by name' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -l keep -d 'Number of most recent backups to keep (default: 10)' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -l current -d 'Clean backups for current repository' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -l dry-run -d 'Show what would be deleted without doing it' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s y -l yes -d 'Skip confirmation prompt' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from clean" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "list" -d 'List available backups' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "restore" -d 'Restore a branch from backup' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "stats" -d 'Show backup storage statistics' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "clean" -d 'Remove old backups, keeping the most recent ones' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand backup; and __fish_seen_subcommand_from help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s d -l days -d 'Treat branches older than N days as stale (default: from config or 30)' -r +complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand stats" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand completions" -s h -l help -d 'Print help' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand completions" -s V -l version -d 'Print version' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "list" -d 'List stale branches' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "clean" -d 'Delete stale branches (merged only by default, use --force for unmerged)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "config" -d 'Manage configuration' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "backup" -d 'Manage backups' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "stats" -d 'Show repository branch statistics' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "completions" -d 'Generate shell completion scripts' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and not __fish_seen_subcommand_from list clean config backup stats completions help" -f -a "help" -d 'Print this message or the help of the given subcommand(s)' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "set" -d 'Set a configuration value' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "show" -d 'Show current configuration' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "edit" -d 'Open config file in $EDITOR' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from config" -f -a "reset" -d 'Reset configuration to defaults' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "list" -d 'List available backups' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "restore" -d 'Restore a branch from backup' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "stats" -d 'Show backup storage statistics' +complete -c deadbranch -n "__fish_deadbranch_using_subcommand help; and __fish_seen_subcommand_from backup" -f -a "clean" -d 'Remove old backups, keeping the most recent ones' diff --git a/completions/fisher.fish b/completions/fisher.fish new file mode 100644 index 0000000..6d23ce4 --- /dev/null +++ b/completions/fisher.fish @@ -0,0 +1,7 @@ +complete --command fisher --exclusive --long help --description "Print help" +complete --command fisher --exclusive --long version --description "Print version" +complete --command fisher --exclusive --condition __fish_use_subcommand --arguments install --description "Install plugins" +complete --command fisher --exclusive --condition __fish_use_subcommand --arguments update --description "Update installed plugins" +complete --command fisher --exclusive --condition __fish_use_subcommand --arguments remove --description "Remove installed plugins" +complete --command fisher --exclusive --condition __fish_use_subcommand --arguments list --description "List installed plugins matching regex" +complete --command fisher --exclusive --condition "__fish_seen_subcommand_from update remove" --arguments "(fisher list)" diff --git a/completions/fzf_configure_bindings.fish b/completions/fzf_configure_bindings.fish new file mode 100644 index 0000000..b38ef92 --- /dev/null +++ b/completions/fzf_configure_bindings.fish @@ -0,0 +1,8 @@ +complete fzf_configure_bindings --no-files +complete fzf_configure_bindings --long help --short h --description "Print help" --condition "not __fish_seen_argument --help -h" +complete fzf_configure_bindings --long directory --description "Change the key binding for Search Directory" --condition "not __fish_seen_argument --directory" +complete fzf_configure_bindings --long git_log --description "Change the key binding for Search Git Log" --condition "not __fish_seen_argument --git_log" +complete fzf_configure_bindings --long git_status --description "Change the key binding for Search Git Status" --condition "not __fish_seen_argument --git_status" +complete fzf_configure_bindings --long history --description "Change the key binding for Search History" --condition "not __fish_seen_argument --history" +complete fzf_configure_bindings --long processes --description "Change the key binding for Search Processes" --condition "not __fish_seen_argument --processes" +complete fzf_configure_bindings --long variables --description "Change the key binding for Search Variables" --condition "not __fish_seen_argument --variables" diff --git a/completions/replay.fish b/completions/replay.fish new file mode 100644 index 0000000..34b6477 --- /dev/null +++ b/completions/replay.fish @@ -0,0 +1,2 @@ +complete --command replay --exclusive --long version --description "Print version" +complete --command replay --exclusive --long help --description "Print help" diff --git a/completions/spark.fish b/completions/spark.fish new file mode 100644 index 0000000..d8daeed --- /dev/null +++ b/completions/spark.fish @@ -0,0 +1,4 @@ +complete --command spark --exclusive --long min --description "Minimum range" +complete --command spark --exclusive --long max --description "Maximum range" +complete --command spark --exclusive --long version --description "Print version" +complete --command spark --exclusive --long help --description "Print this help message" diff --git a/conf.d/abbr.fish b/conf.d/abbr.fish new file mode 100644 index 0000000..93395ad --- /dev/null +++ b/conf.d/abbr.fish @@ -0,0 +1,229 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +### Abreviations ### + +# Neovim +abbr -a n nvim +abbr -a nv nvim +abbr -a neovim nvim +abbr -a cdnv 'cd ~/.config/nvim # Neovim Config' +abbr -a cdnvn 'cd ~/.config/nvim;nvim' +# VSCode +abbr -a v antigravity +# Kate +abbr -a k kate +# WezTerm SSH +abbr -a s wezterm ssh +# Neovim in a new tab +#abbr -a editt wezterm cli spawn nvim # WezTerm +abbr -a editt kitty @ launch --type=tab --cwd=current nvim # Kitty +# LazyGit +abbr -a lg lazygit +# Sudo shell +abbr -a sudu sudo -s +# Kitty +abbr -a kt kitty +# cat +abbr -a c cat +# chezmoi +abbr -a cm chezmoi +# chezmoi cd +abbr -a cmcd chezmoi cd +abbr -a czcd chezmoi cd +abbr -a cdcm chezmoi cd +abbr -a cdcz chezmoi cd +# chezmoi edit +abbr -a cme chezmoi edit +abbr -a cze chezmoi edit +# chezmoi add +abbr -a cmad chezmoi add +abbr -a czad chezmoi add +# chezmoi apply +abbr -a cmap chezmoi apply +abbr -a czap chezmoi apply +# chezmoi rm +abbr -a cmrm chezmoi forget +abbr -a cmf chezmoi forget +abbr -a czrm chezmoi forget +abbr -a czf chezmoi forget +# chezmoi init +abbr -a cmi chezmoi init +abbr -a czi chezmoi init +# Edit +abbr -a e edit +# Sudoedit +abbr -a se sudoedit +# Git +abbr -a g git +# Antigravity +abbr -a ag antigravity +abbr -a ag. antigravity . +# Quit +# abbr -a :q wezterm cli kill-pane # WezTerm +# abbr -a :Q wezterm cli kill-pane # WezTerm +abbr -a :q kitty @ close-window # Kitty (Closes the active split/pane) +abbr -a :Q kitty @ close-tab # Kitty (Closes the whole tab) + +# Window Creation (OS Windows) +# abbr -a :w wezterm cli spawn --new-window # WezTerm +abbr -a :w kitty @ launch --type=os-window # Kitty + +# Window Splits (Panes) +# abbr -a :wv wezterm cli split-pane --bottom # WezTerm +abbr -a :wv kitty @ launch --location=hsplit # Kitty (Horizontal split) +# abbr -a :wh wezterm cli split-pane --right # WezTerm +abbr -a :wh kitty @ launch --location=vsplit # Kitty (Vertical split) + +# Window Detach (Move Pane) +# abbr -a :wo wezterm cli move-pane-to-new-tab --new-window # WezTerm +abbr -a :wo kitty @ detach-window --target-tab=new # Kitty (Moves pane to new tab) +# abbr -a :wot wezterm cli move-pane-to-new-tab # WezTerm +abbr -a :wot kitty @ detach-window # Kitty (Same as above, default behavior) + +# Tab Creation +# abbr -a :t wezterm cli spawn # WezTerm +abbr -a :t kitty @ launch --type=tab # Kitty + +# Rename Tab +# abbr -a :tl wezterm cli set-tab-title # WezTerm +abbr -a :tl "kitty @ set-tab-title" # Kitty -> Usage: :tl "New Title" + +# Rename Window +# abbr -a :tw wezterm cli set-window-title # WezTerm +abbr -a :tw "kitty @ set-window-title" # Kitty + +# Rename Workspace +# abbr -a :twk wezterm cli rename-workspace # WezTerm +# Kitty does not have a direct CLI equivalent for renaming a dynamic "workspace" session. + +# Tab Navigation +# abbr -a :tp wezterm cli activate-tab --tab-relative -1 # WezTerm +abbr -a :tp "kitty @ focus-tab --match neighbor:left" # Kitty +# abbr -a :tn wezterm cli activate-tab --tab-relative 1 # WezTerm +abbr -a :tn "kitty @ focus-tab --match neighbor:right" # Kitty + +# Specialty Tab Shortcuts (New Tab in specific dir) +# abbr -a :tgk wezterm cli spawn --cwd ~/.config/kitty # WezTerm +abbr -a :tgk kitty @ launch --type=tab --cwd ~/.config/kitty # Kitty +# abbr -a :tgn wezterm cli spawn --cwd ~/.config/nvim # WezTerm +abbr -a :tgn kitty @ launch --type=tab --cwd ~/.config/nvim # Kitty +# abbr -a :tgf wezterm cli spawn --cwd ~/.config/fish # WezTerm +abbr -a :tgf kitty @ launch --type=tab --cwd ~/.config/fish # Kitty +# abbr -a :tgh wezterm cli spawn --cwd ~ # WezTerm +abbr -a :tgh kitty @ launch --type=tab --cwd ~ +# abbr -a :tgcz wezterm cli spawn --cwd ~/.local/share/chezmoi # WezTerm +abbr -a :tgcz kitty @ launch --type=tab --cwd ~/.local/share/chezmoi # Kitty +# abbr -a :tgcm wezterm cli spawn --cwd ~/.config/chezmoi # WezTerm +abbr -a :tgcm kitty @ launch --type=tab --cwd ~/.config/chezmoi # Kitty +# abbr -a :tgp wezterm cli spawn --cwd ~/projects # WezTerm +abbr -a :tgp kitty @ launch --type=tab --cwd ~/projects # Kitty +# abbr -a :tgr wezterm cli spawn -- sudo -i # WezTerm +abbr -a :tgr kitty @ launch --type=tab -- sudo -i + +# Specialty Window Shortcuts (New OS Window in specific dir) +# abbr -a :wgk wezterm cli spawn --new-window --cwd ~/.config/kitty # WezTerm +abbr -a :wgk kitty @ launch --type=os-window --cwd ~/.config/kitty # Kitty +# abbr -a :wgn wezterm cli spawn --new-window --cwd ~/.config/nvim # WezTerm +abbr -a :wgn kitty @ launch --type=os-window --cwd ~/.config/nvim # Kitty +# abbr -a :wgf wezterm cli spawn --new-window --cwd ~/.config/fish # WezTerm +abbr -a :wgf kitty @ launch --type=os-window --cwd ~/.config/fish # Kitty +# abbr -a :wgh wezterm cli spawn --new-window --cwd ~ # WezTerm +abbr -a :wgh kitty @ launch --type=os-window --cwd ~ +# abbr -a :wgzd wezterm cli spawn --new-window --cwd ~/.local/share/chezmoi # WezTerm +abbr -a :wgzd kitty @ launch --type=os-window --cwd ~/.local/share/chezmoi # Kitty +# abbr -a :wgcz wezterm cli spawn --new-window --cwd ~/.config/chezmoi # WezTerm +abbr -a :wgcz kitty @ launch --type=os-window --cwd ~/.config/chezmoi # Kitty +# abbr -a :wgp wezterm cli spawn --new-window --cwd ~/projects # WezTerm +abbr -a :wgp kitty @ launch --type=os-window --cwd ~/projects # Kitty +# abbr -a :wgr wezterm cli spawn --new-window -- sudo -i # WezTerm +abbr -a :wgr kitty @ launch --type=os-window -- sudo -i # Kitty + +# Specialty Window Vertical Shortcuts (Split Bottom) +# abbr -a :wvgk wezterm cli split-pane --bottom --cwd ~/.config/kitty # WezTerm +abbr -a :wvgk kitty @ launch --location=hsplit --cwd ~/.config/kitty # Kitty +# abbr -a :wvgn wezterm cli split-pane --bottom --cwd ~/.config/nvim # WezTerm +abbr -a :wvgn kitty @ launch --location=hsplit --cwd ~/.config/nvim # Kitty +# abbr -a :wvgf wezterm cli split-pane --bottom --cwd ~/.config/fish # WezTerm +abbr -a :wvgf kitty @ launch --location=hsplit --cwd ~/.config/fish # Kitty +# abbr -a :wvgh wezterm cli split-pane --bottom --cwd ~ # WezTerm +abbr -a :wvgh kitty @ launch --location=hsplit --cwd ~ # Kitty +# abbr -a :wvgcz wezterm cli split-pane --bottom --cwd ~/.local/share/chezmoi # WezTerm +abbr -a :wvgcz kitty @ launch --location=hsplit --cwd ~/.local/share/chezmoi # Kitty +# abbr -a :wvgcm wezterm cli split-pane --bottom --cwd ~/.config/chezmoi # WezTerm +abbr -a :wvgcm kitty @ launch --location=hsplit --cwd ~/.config/chezmoi # Kitty +# abbr -a :wvgp wezterm cli split-pane --bottom --cwd ~/projects # WezTerm +abbr -a :wvgp kitty @ launch --location=hsplit --cwd ~/projects # Kitty +# abbr -a :wvgr wezterm cli split-pane --bottom -- sudo -i # WezTerm +abbr -a :wvgr kitty @ launch --location=hsplit -- sudo -i # Kitty + +# Specialty Window Horizontal Shortcuts (Split Right) +# abbr -a :whgk wezterm cli split-pane --bottom --cwd ~/.config/kitty # WezTerm +abbr -a :whgk kitty @ launch --location=vsplit --cwd ~/.config/kitty # Kitty +# abbr -a :whgn wezterm cli split-pane --bottom --cwd ~/.config/nvim # WezTerm +abbr -a :whgn kitty @ launch --location=vsplit --cwd ~/.config/nvim # Kitty +# abbr -a :whgf wezterm cli split-pane --bottom --cwd ~/.config/fish # WezTerm +abbr -a :whgf kitty @ launch --location=vsplit --cwd ~/.config/fish # Kitty +# abbr -a :whgh wezterm cli split-pane --bottom --cwd ~ # WezTerm +abbr -a :whgh kitty @ launch --location=vsplit --cwd ~ # Kitty +# abbr -a :whgcz wezterm cli split-pane --bottom --cwd ~/.local/share/chezmoi # WezTerm +abbr -a :whgcz kitty @ launch --location=vsplit --cwd ~/.local/share/chezmoi # Kitty +# abbr -a :whgcm wezterm cli split-pane --bottom --cwd ~/.config/chezmoi # WezTerm +abbr -a :whgcm kitty @ launch --location=vsplit --cwd ~/.config/chezmoi # Kitty +# abbr -a :whgp wezterm cli split-pane --bottom --cwd ~/projects # WezTerm +abbr -a :whgp kitty @ launch --location=vsplit --cwd ~/projects # Kitty +# abbr -a :whgr wezterm cli split-pane --bottom -- sudo -i # WezTerm +abbr -a :whgr kitty @ launch --location=vsplit --cwd current sudo -i # Kitty -> Specialty cd Shortcuts +abbr -a :cdk 'cd ~/.config/kitty/ # Kitty Config' +abbr -a :cdkn 'cd ~/.config/kitty;nvim' +abbr -a :cdn cd '~/.config/nvim/ # Neovim Config' +abbr -a :cdnn 'cd ~/.config/nvim;nvim' +abbr -a :cdf 'cd ~/.config/fish/ # Fish Config' +abbr -a :cdfn 'cd ~/.config/fish;nvim' +abbr -a :cdh 'cd ~ # Home Directory' +abbr -a :cdhn 'cd ~;nvim' +abbr -a :cdcz cd '~/.local/share/chezmoi/ # Chezmoi Source' +abbr -a :cdczn 'cd ~/.local/share/chezmoi;nvim' +abbr -a :cdcm 'cd ~/.config/chezmoi/ # Chezmoi Config' +abbr -a :cdcmn 'cd ~/.config/chezmoi;nvim' +abbr -a :cdp --regex ':cdp' --set-cursor 'cd ~/projects/%' +# abbr -a cdp_slash --position anywhere --regex ':cdp/' --set-cursor 'cd ~/projects/%' +abbr -a :cdpn 'cd ~/projects;nvim' +abbr -a :cdw 'cd ~/.config/wezterm/ # WezTerm Config' +abbr -a :cdwn 'cd ~/.config/wezterm;nvim' +abbr -a editt kitty @ launch --type tab nvim +# Spawn window +abbr -a :sw spwin + +### SSH ### +abbr -a sshr 'ssh rootiest@rootiest-server.local' +abbr -a sshrt 'ssh rootiest-server' +abbr -a sshrl 'ssh rootiest@rootiest-server.local' + +### Docker ### +abbr -a dcr 'docker context use rootiest # Rootiest Server' +abbr -a dcl 'docker context use default # Local Host' +abbr -a dck 'docker context use racknerd # RackNerd Server' +abbr -a lzd ld +abbr -a dcls 'docker context ls' + +### Beads ### +abbr -a bl 'bd list' +abbr -a bs 'bd sync' +abbr -a bc 'bd create --title' +abbr -a bsh 'bd show' +abbr -a lb lazybeads + +### Systemctl ### +abbr -a sc systemctl +abbr -a ssc 'sudo systemctl' +abbr -a scu 'systemctl --user' +abbr -a st 'systemctl status' +abbr -a scs 'systemctl start' +abbr -a scr 'systemctl restart' +abbr -a ssct 'sudo systemctl status' +abbr -a sscs 'sudo systemctl start' +abbr -a sscr 'sudo systemctl restart' + +### Rootiest Notes ### +abbr -a cdnote 'cd ~/Documents/Rootiest\ Notes/ # Rootiest Notes' diff --git a/conf.d/autopair.fish b/conf.d/autopair.fish new file mode 100644 index 0000000..abb4bf3 --- /dev/null +++ b/conf.d/autopair.fish @@ -0,0 +1,39 @@ +status is-interactive || exit + +set --global autopair_left "(" "[" "{" '"' "'" +set --global autopair_right ")" "]" "}" '"' "'" +set --global autopair_pairs "()" "[]" "{}" '""' "''" + +function _autopair_fish_key_bindings --on-variable fish_key_bindings + set --query fish_key_bindings[1] || return + + test $fish_key_bindings = fish_default_key_bindings && + set --local mode default insert || + set --local mode insert default + + bind --mode $mode[-1] --erase \177 \b \t + + bind --mode $mode[1] \177 _autopair_backspace # macOS ⌫ + bind --mode $mode[1] \b _autopair_backspace + bind --mode $mode[1] \t _autopair_tab + + printf "%s\n" $autopair_pairs | while read --local left right --delimiter "" + bind --mode $mode[-1] --erase $left $right + if test $left = $right + bind --mode $mode[1] $left "_autopair_insert_same \\$left" + else + bind --mode $mode[1] $left "_autopair_insert_left \\$left \\$right" + bind --mode $mode[1] $right "_autopair_insert_right \\$right" + end + end +end + +_autopair_fish_key_bindings + +function _autopair_uninstall --on-event autopair_uninstall + string collect ( + bind --all | string replace --filter --regex -- "_autopair.*" --erase + set --names | string replace --filter --regex -- "^autopair" "set --erase autopair" + ) | source + functions --erase (functions --all | string match "_autopair_*") +end diff --git a/conf.d/bobthefish.fish b/conf.d/bobthefish.fish new file mode 100644 index 0000000..0aaf713 --- /dev/null +++ b/conf.d/bobthefish.fish @@ -0,0 +1,54 @@ +set -g theme_nerd_fonts yes +set -g theme_color_scheme catpuccin-mocha +set -g theme_display_user ssh +set -g default_user rootiest +set -g theme_display_cmd_duration yes +set -g theme_display_git_dirty yes +set -g theme_display_virtualenv yes +set -g theme_display_screen yes +set -g theme_display_docker_machine yes +set -g theme_display_docker_context yes +set -g theme_display_nix yes +set -g theme_display_k8s_context yes +set -g theme_display_k8s_namespace yes +set -g theme_display_aws_vault_profile yes + +set -g fish_key_bindings fish_vi_key_bindings + +set -g theme_nerd_fonts yes +set -g theme_color_scheme catpuccin-mocha +set -g theme_display_user ssh +set -g default_user rootiest + +function bobthefish_colors -S -d 'Define a custom bobthefish color scheme' + + # optionally include a base color scheme... + # ___bobthefish_colors default + + # then override everything you want! note that these must be defined with `set -x` + set -x color_initial_segment_exit ffffff ce000f --bold + set -x color_initial_segment_private ffffff 255e87 + set -x color_initial_segment_su ffffff 189303 --bold + set -x color_initial_segment_jobs ffffff 255e87 --bold + set -x color_path 45475a a6adc8 + set -x color_path_basename 45475a ffffff --bold + set -x color_path_nowrite f37799 585b70 + set -x color_path_nowrite_basename f37799 585b70 --bold + set -x color_repo 89d88b 585b70 + set -x color_repo_work_tree 45475a ffffff --bold + set -x color_repo_dirty f3799a ffffff + set -x color_repo_staged ebd391 45475a + set -x color_vi_mode_default a6adc8 45475a --bold + set -x color_vi_mode_insert 89d88b 45475a --bold + set -x color_vi_mode_visual ebd391 45475a --bold + set -x color_vagrant 48b4fb ffffff --bold + set -x color_aws_vault + set -x color_aws_vault_expired + set -x color_username 585b70 74a8fc --bold + set -x color_hostname 585b70 74a8fc + set -x color_rvm f37799 585b70 --bold + set -x color_virtualfish 89b4fa 585b70 --bold + set -x color_virtualgo 89b4fa 585b70 --bold + set -x color_desk 89b4fa 585b70 --bold + set -x color_nix 89b4fa 585b70 --bold +end diff --git a/conf.d/cheat.fish b/conf.d/cheat.fish new file mode 100644 index 0000000..84faa6d --- /dev/null +++ b/conf.d/cheat.fish @@ -0,0 +1,13 @@ +complete -c cheat -f -a "(cheat -l | tail -n +2 | cut -d ' ' -f 1)" +complete -c cheat -l init -d "Write a default config file to stdout" +complete -c cheat -s c -l colorize -d "Colorize output" +complete -c cheat -s d -l directories -d "List cheatsheet directories" +complete -c cheat -s e -l edit -x -a "(cheat -l | tail -n +2 | cut -d ' ' -f 1)" -d "Edit cheatsheet" +complete -c cheat -s l -l list -d "List cheatsheets" +complete -c cheat -s p -l path -x -a "(cheat -d | cut -d ':' -f 1)" -d "Return only sheets found on given path" +complete -c cheat -s r -l regex -d "Treat search phrase as a regex" +complete -c cheat -s s -l search -x -d "Search cheatsheets for given phrase" +complete -c cheat -s t -l tag -x -a "(cheat -T)" -d "Return only sheets matching the given tag" +complete -c cheat -s T -l tags -d "List all tags in use" +complete -c cheat -s v -l version -d "Print the version number" +complete -c cheat -l rm -x -a "(cheat -l | tail -n +2 | cut -d ' ' -f 1)" -d "Remove (delete) cheatsheet" diff --git a/conf.d/fish_frozen_key_bindings.fish b/conf.d/fish_frozen_key_bindings.fish new file mode 100644 index 0000000..495aee9 --- /dev/null +++ b/conf.d/fish_frozen_key_bindings.fish @@ -0,0 +1,14 @@ +# This file was created by fish when upgrading to version 4.3, to migrate +# the 'fish_key_bindings' variable from its old default scope (universal) +# to its new default scope (global). We recommend you delete this file +# and configure key bindings in ~/.config/fish/config.fish if needed. + +# set --global fish_key_bindings fish_default_key_bindings + +# Prior to version 4.3, fish shipped an event handler that runs +# `set --universal fish_key_bindings fish_default_key_bindings` +# whenever the fish_key_bindings variable is erased. +# This means that as long as any fish < 4.3 is still running on this system, +# we cannot complete the migration. +# As a workaround, erase the universal variable at every shell startup. +set --erase --universal fish_key_bindings diff --git a/conf.d/fish_frozen_theme.fish b/conf.d/fish_frozen_theme.fish new file mode 100644 index 0000000..56502b4 --- /dev/null +++ b/conf.d/fish_frozen_theme.fish @@ -0,0 +1,49 @@ +# This file was created by fish when upgrading to version 4.3, to migrate +# theme variables from universal to global scope. +# Don't edit this file, as it will be written by the web-config tool (`fish_config`). +# To customize your theme, delete this file and see +# help interactive#syntax-highlighting +# or +# man fish-interactive | less +/^SYNTAX.HIGHLIGHTING +# for appropriate commands to add to ~/.config/fish/config.fish instead. +# See also the release notes for fish 4.3.0 (run `help relnotes`). + +set --global fish_color_autosuggestion 6c7086 +set --global fish_color_cancel f38ba8 +set --global fish_color_command 89b4fa +set --global fish_color_comment 7f849c +set --global fish_color_cwd f9e2af +set --global fish_color_cwd_root red +set --global fish_color_end fab387 +set --global fish_color_error f38ba8 +set --global fish_color_escape eba0ac +set --global fish_color_gray 6c7086 +set --global fish_color_history_current --bold +set --global fish_color_host 89b4fa +set --global fish_color_host_remote a6e3a1 +set --global fish_color_keyword f38ba8 +set --global fish_color_match F28779 +set --global fish_color_normal cdd6f4 +set --global fish_color_operator f5c2e7 +set --global fish_color_option a6e3a1 +set --global fish_color_param f2cdcd +set --global fish_color_quote a6e3a1 +set --global fish_color_redirection f5c2e7 +set --global fish_color_search_match --background=313244 +set --global fish_color_selection --background=313244 +set --global fish_color_status f38ba8 +set --global fish_color_user 94e2d5 +set --global fish_color_valid_path --underline +set --global fish_pager_color_background +set --global fish_pager_color_completion cdd6f4 +set --global fish_pager_color_description 6c7086 +set --global fish_pager_color_prefix f5c2e7 +set --global fish_pager_color_progress 6c7086 +set --global fish_pager_color_secondary_background +set --global fish_pager_color_secondary_completion +set --global fish_pager_color_secondary_description +set --global fish_pager_color_secondary_prefix +set --global fish_pager_color_selected_background +set --global fish_pager_color_selected_completion +set --global fish_pager_color_selected_description +set --global fish_pager_color_selected_prefix diff --git a/conf.d/fzf.fish b/conf.d/fzf.fish new file mode 100644 index 0000000..446dd3c --- /dev/null +++ b/conf.d/fzf.fish @@ -0,0 +1,27 @@ +# fzf.fish is only meant to be used in interactive mode. If not in interactive mode and not in CI, skip the config to speed up shell startup +if not status is-interactive && test "$CI" != true + exit +end + +# Because of scoping rules, to capture the shell variables exactly as they are, we must read +# them before even executing _fzf_search_variables. We use psub to store the +# variables' info in temporary files and pass in the filenames as arguments. +# This variable is global so that it can be referenced by fzf_configure_bindings and in tests +set --global _fzf_search_vars_command '_fzf_search_variables (set --show | psub) (set --names | psub)' + +# Install the default bindings, which are mnemonic and minimally conflict with fish's preset bindings +fzf_configure_bindings + +# Doesn't erase autoloaded _fzf_* functions because they are not easily accessible once key bindings are erased +function _fzf_uninstall --on-event fzf_uninstall + _fzf_uninstall_bindings + + set --erase _fzf_search_vars_command + functions --erase _fzf_uninstall _fzf_migration_message _fzf_uninstall_bindings fzf_configure_bindings + complete --erase fzf_configure_bindings + + set_color cyan + echo "fzf.fish uninstalled." + echo "You may need to manually remove fzf_configure_bindings from your config.fish if you were using custom key bindings." + set_color normal +end diff --git a/conf.d/magic-enter.fish b/conf.d/magic-enter.fish new file mode 100644 index 0000000..19b1234 --- /dev/null +++ b/conf.d/magic-enter.fish @@ -0,0 +1,25 @@ +function magic-enter-cmd --description "Print the command to run when no command was given" + set -l cmd ls + if command git rev-parse --is-inside-work-tree &>/dev/null + set cmd "git status -sb" + end + echo $cmd +end + +function magic-enter + set -l cmd (commandline) + if test -z "$cmd" + commandline -r (magic-enter-cmd) + commandline -f suppress-autosuggestion + end + commandline -f execute +end + +function magic-enter-bindings --description "Bind magic-enter for default and vi key bindings" + bind \r magic-enter + if functions -q fish_vi_key_bindings + bind -M insert \r magic-enter + bind -M default \r magic-enter + end +end +magic-enter-bindings diff --git a/conf.d/puffer_fish_key_bindings.fish b/conf.d/puffer_fish_key_bindings.fish new file mode 100644 index 0000000..44ff6f0 --- /dev/null +++ b/conf.d/puffer_fish_key_bindings.fish @@ -0,0 +1,27 @@ +status is-interactive || exit + +function _puffer_fish_key_bindings --on-variable fish_key_bindings + set -l modes + if test "$fish_key_bindings" = fish_default_key_bindings + set modes default insert + else + set modes insert default + end + + bind --mode $modes[1] '.' _puffer_fish_expand_dot + bind --mode $modes[1] '!' _puffer_fish_expand_bang + bind --mode $modes[1] '$' _puffer_fish_expand_buck + bind --mode $modes[1] '*' _puffer_fish_expand_star + bind --mode $modes[2] --erase '.' '!' '$' '*' +end + +_puffer_fish_key_bindings + +set -l uninstall_event puffer_fish_key_bindings_uninstall + +function _$uninstall_event --on-event $uninstall_event + bind -e '.' + bind -e '!' + bind -e '$' + bind -e '*' +end diff --git a/conf.d/tailscale.fish b/conf.d/tailscale.fish new file mode 100644 index 0000000..10af6b3 --- /dev/null +++ b/conf.d/tailscale.fish @@ -0,0 +1,248 @@ +# Copyright 2013-2023 The Cobra Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# fish completion for tailscale -*- shell-script -*- + +function __tailscale_debug + set -l file "$BASH_COMP_DEBUG_FILE" + if test -n "$file" + echo "$argv" >> $file + end +end + +function __tailscale_perform_completion + __tailscale_debug "Starting __tailscale_perform_completion" + + # Extract all args except the last one + set -l args (commandline -opc) + # Extract the last arg and escape it in case it is a space + set -l lastArg (string escape -- (commandline -ct)) + + __tailscale_debug "args: $args" + __tailscale_debug "last arg: $lastArg" + + set -l requestComp "$args[1] completion __complete --descs=true --flags=true -- $args[2..-1] $lastArg" + + __tailscale_debug "Calling $requestComp" + set -l results (eval $requestComp 2> /dev/null) + + # Some programs may output extra empty lines after the directive. + # Let's ignore them or else it will break completion. + # Ref: https://github.com/spf13/cobra/issues/1279 + for line in $results[-1..1] + if test (string trim -- $line) = "" + # Found an empty line, remove it + set results $results[1..-2] + else + # Found non-empty line, we have our proper output + break + end + end + + set -l comps $results[1..-2] + set -l directiveLine $results[-1] + + # For Fish, when completing a flag with an = (e.g., -n=) + # completions must be prefixed with the flag + set -l flagPrefix (string match -r -- '-.*=' "$lastArg") + + __tailscale_debug "Comps: $comps" + __tailscale_debug "DirectiveLine: $directiveLine" + __tailscale_debug "flagPrefix: $flagPrefix" + + for comp in $comps + printf "%s%s\n" "$flagPrefix" "$comp" + end + + printf "%s\n" "$directiveLine" +end + +# this function limits calls to __tailscale_perform_completion, by caching the result behind $__tailscale_perform_completion_once_result +function __tailscale_perform_completion_once + __tailscale_debug "Starting __tailscale_perform_completion_once" + + if test -n "$__tailscale_perform_completion_once_result" + __tailscale_debug "Seems like a valid result already exists, skipping __tailscale_perform_completion" + return 0 + end + + set --global __tailscale_perform_completion_once_result (__tailscale_perform_completion) + if test -z "$__tailscale_perform_completion_once_result" + __tailscale_debug "No completions, probably due to a failure" + return 1 + end + + __tailscale_debug "Performed completions and set __tailscale_perform_completion_once_result" + return 0 +end + +# this function is used to clear the $__tailscale_perform_completion_once_result variable after completions are run +function __tailscale_clear_perform_completion_once_result + __tailscale_debug "" + __tailscale_debug "========= clearing previously set __tailscale_perform_completion_once_result variable ==========" + set --erase __tailscale_perform_completion_once_result + __tailscale_debug "Successfully erased the variable __tailscale_perform_completion_once_result" +end + +function __tailscale_requires_order_preservation + __tailscale_debug "" + __tailscale_debug "========= checking if order preservation is required ==========" + + __tailscale_perform_completion_once + if test -z "$__tailscale_perform_completion_once_result" + __tailscale_debug "Error determining if order preservation is required" + return 1 + end + + set -l directive (string sub --start 2 $__tailscale_perform_completion_once_result[-1]) + __tailscale_debug "Directive is: $directive" + + set -l shellCompDirectiveKeepOrder 32 + set -l keeporder (math (math --scale 0 $directive / $shellCompDirectiveKeepOrder) % 2) + __tailscale_debug "Keeporder is: $keeporder" + + if test $keeporder -ne 0 + __tailscale_debug "This does require order preservation" + return 0 + end + + __tailscale_debug "This doesn't require order preservation" + return 1 +end + + +# This function does two things: +# - Obtain the completions and store them in the global __tailscale_comp_results +# - Return false if file completion should be performed +function __tailscale_prepare_completions + __tailscale_debug "" + __tailscale_debug "========= starting completion logic ==========" + + # Start fresh + set --erase __tailscale_comp_results + + __tailscale_perform_completion_once + __tailscale_debug "Completion results: $__tailscale_perform_completion_once_result" + + if test -z "$__tailscale_perform_completion_once_result" + __tailscale_debug "No completion, probably due to a failure" + # Might as well do file completion, in case it helps + return 1 + end + + set -l directive (string sub --start 2 $__tailscale_perform_completion_once_result[-1]) + set --global __tailscale_comp_results $__tailscale_perform_completion_once_result[1..-2] + + __tailscale_debug "Completions are: $__tailscale_comp_results" + __tailscale_debug "Directive is: $directive" + + set -l shellCompDirectiveError 1 + set -l shellCompDirectiveNoSpace 2 + set -l shellCompDirectiveNoFileComp 4 + set -l shellCompDirectiveFilterFileExt 8 + set -l shellCompDirectiveFilterDirs 16 + + if test -z "$directive" + set directive 0 + end + + set -l compErr (math (math --scale 0 $directive / $shellCompDirectiveError) % 2) + if test $compErr -eq 1 + __tailscale_debug "Received error directive: aborting." + # Might as well do file completion, in case it helps + return 1 + end + + set -l filefilter (math (math --scale 0 $directive / $shellCompDirectiveFilterFileExt) % 2) + set -l dirfilter (math (math --scale 0 $directive / $shellCompDirectiveFilterDirs) % 2) + if test $filefilter -eq 1; or test $dirfilter -eq 1 + __tailscale_debug "File extension filtering or directory filtering not supported" + # Do full file completion instead + return 1 + end + + set -l nospace (math (math --scale 0 $directive / $shellCompDirectiveNoSpace) % 2) + set -l nofiles (math (math --scale 0 $directive / $shellCompDirectiveNoFileComp) % 2) + + __tailscale_debug "nospace: $nospace, nofiles: $nofiles" + + # If we want to prevent a space, or if file completion is NOT disabled, + # we need to count the number of valid completions. + # To do so, we will filter on prefix as the completions we have received + # may not already be filtered so as to allow fish to match on different + # criteria than the prefix. + if test $nospace -ne 0; or test $nofiles -eq 0 + set -l prefix (commandline -t | string escape --style=regex) + __tailscale_debug "prefix: $prefix" + + set -l completions (string match -r -- "^$prefix.*" $__tailscale_comp_results) + set --global __tailscale_comp_results $completions + __tailscale_debug "Filtered completions are: $__tailscale_comp_results" + + # Important not to quote the variable for count to work + set -l numComps (count $__tailscale_comp_results) + __tailscale_debug "numComps: $numComps" + + if test $numComps -eq 1; and test $nospace -ne 0 + # We must first split on \t to get rid of the descriptions to be + # able to check what the actual completion will be. + # We don't need descriptions anyway since there is only a single + # real completion which the shell will expand immediately. + set -l split (string split --max 1 \t $__tailscale_comp_results[1]) + + # Fish won't add a space if the completion ends with any + # of the following characters: @=/:., + set -l lastChar (string sub -s -1 -- $split) + if not string match -r -q "[@=/:.,]" -- "$lastChar" + # In other cases, to support the "nospace" directive we trick the shell + # by outputting an extra, longer completion. + __tailscale_debug "Adding second completion to perform nospace directive" + set --global __tailscale_comp_results $split[1] $split[1]. + __tailscale_debug "Completions are now: $__tailscale_comp_results" + end + end + + if test $numComps -eq 0; and test $nofiles -eq 0 + # To be consistent with bash and zsh, we only trigger file + # completion when there are no other completions + __tailscale_debug "Requesting file completion" + return 1 + end + end + + return 0 +end + +# Since Fish completions are only loaded once the user triggers them, we trigger them ourselves +# so we can properly delete any completions provided by another script. +# Only do this if the program can be found, or else fish may print some errors; besides, +# the existing completions will only be loaded if the program can be found. +if type -q "tailscale" + # The space after the program name is essential to trigger completion for the program + # and not completion of the program name itself. + # Also, we use '> /dev/null 2>&1' since '&>' is not supported in older versions of fish. + complete --do-complete "tailscale " > /dev/null 2>&1 +end + +# Remove any pre-existing completions for the program since we will be handling all of them. +complete -c tailscale -e + +# this will get called after the two calls below and clear the $__tailscale_perform_completion_once_result global +complete -c tailscale -n '__tailscale_clear_perform_completion_once_result' +# The call to __tailscale_prepare_completions will setup __tailscale_comp_results +# which provides the program's completion choices. +# If this doesn't require order preservation, we don't use the -k flag +complete -c tailscale -n 'not __tailscale_requires_order_preservation && __tailscale_prepare_completions' -f -a '$__tailscale_comp_results' +# otherwise we use the -k flag +complete -k -c tailscale -n '__tailscale_requires_order_preservation && __tailscale_prepare_completions' -f -a '$__tailscale_comp_results' diff --git a/conf.d/wakatime.fish b/conf.d/wakatime.fish new file mode 100644 index 0000000..cb2d75f --- /dev/null +++ b/conf.d/wakatime.fish @@ -0,0 +1,43 @@ +### +# wakatime.fish +# +# hook script to send wakatime a tick (unofficial) +# see: https://github.com/ik11235/wakatime.fish +### + +function __register_wakatime_fish_before_exec -e fish_postexec + if set -q FISH_WAKATIME_DISABLED + return 0 + end + + set -l exec_command_str + + set exec_command_str (string split -f1 ' ' "$argv") + + if test "$exec_command_str" = 'exit' + return 0 + end + + set -l PLUGIN_NAME "ik11235/wakatime.fish" + set -l PLUGIN_VERSION "0.0.6" + + set -l project + set -l wakatime_path + + if type -p wakatime 2>&1 > /dev/null + set wakatime_path (type -p wakatime) + else if type -p ~/.wakatime/wakatime-cli 2>&1 > /dev/null + set wakatime_path (type -p ~/.wakatime/wakatime-cli) + else + echo "wakatime command not found. Please read \"https://wakatime.com/terminal\" and install wakatime." + return 1 + end + + if git rev-parse --is-inside-work-tree &> /dev/null + set project (basename (git rev-parse --show-toplevel)) + else + set project "Terminal" + end + + $wakatime_path --write --plugin "$PLUGIN_NAME/$PLUGIN_VERSION" --entity-type app --project "$project" --entity "$exec_command_str" &> /dev/null&; disown +end diff --git a/conf.d/zoxide.fish b/conf.d/zoxide.fish new file mode 100644 index 0000000..1d10e44 --- /dev/null +++ b/conf.d/zoxide.fish @@ -0,0 +1,100 @@ +# ============================================================================= +# +# Utility functions for zoxide. +# + +# pwd based on the value of _ZO_RESOLVE_SYMLINKS. +function __zoxide_pwd + builtin pwd -L +end + +# A copy of fish's internal cd function. This makes it possible to use +# `alias cd=z` without causing an infinite loop. +if ! builtin functions --query __zoxide_cd_internal + string replace --regex -- '^function cd\s' 'function __zoxide_cd_internal ' <$__fish_data_dir/functions/cd.fish | source +end + +# cd + custom logic based on the value of _ZO_ECHO. +function __zoxide_cd + if set -q __zoxide_loop + builtin echo "zoxide: infinite loop detected" + builtin echo "Avoid aliasing `cd` to `z` directly, use `zoxide init --cmd=cd fish` instead" + return 1 + end + __zoxide_loop=1 __zoxide_cd_internal $argv +end + +# ============================================================================= +# +# Hook configuration for zoxide. +# + +# Initialize hook to add new entries to the database. +function __zoxide_hook --on-variable PWD + test -z "$fish_private_mode" + and command zoxide add -- (__zoxide_pwd) +end + +# ============================================================================= +# +# When using zoxide with --no-cmd, alias these internal functions as desired. +# + +# Jump to a directory using only keywords. +function __zoxide_z + set -l argc (builtin count $argv) + if test $argc -eq 0 + __zoxide_cd $HOME + else if test "$argv" = - + __zoxide_cd - + else if test $argc -eq 1 -a -d $argv[1] + __zoxide_cd $argv[1] + else if test $argc -eq 2 -a $argv[1] = -- + __zoxide_cd -- $argv[2] + else + set -l result (command zoxide query --exclude (__zoxide_pwd) -- $argv) + and __zoxide_cd $result + end +end + +# Completions. +function __zoxide_z_complete + set -l tokens (builtin commandline --current-process --tokenize) + set -l curr_tokens (builtin commandline --cut-at-cursor --current-process --tokenize) + + if test (builtin count $tokens) -le 2 -a (builtin count $curr_tokens) -eq 1 + # If there are < 2 arguments, use `cd` completions. + complete --do-complete "'' "(builtin commandline --cut-at-cursor --current-token) | string match --regex -- '.*/$' + else if test (builtin count $tokens) -eq (builtin count $curr_tokens) + # If the last argument is empty, use interactive selection. + set -l query $tokens[2..-1] + set -l result (command zoxide query --exclude (__zoxide_pwd) --interactive -- $query) + and __zoxide_cd $result + and builtin commandline --function cancel-commandline repaint + end +end +complete --command __zoxide_z --no-files --arguments '(__zoxide_z_complete)' + +# Jump to a directory using interactive search. +function __zoxide_zi + set -l result (command zoxide query --interactive -- $argv) + and __zoxide_cd $result +end + +# ============================================================================= +# +# Commands for zoxide. Disable these using --no-cmd. +# + +abbr --erase z &>/dev/null +alias z=__zoxide_z + +abbr --erase zi &>/dev/null +alias zi=__zoxide_zi + +# ============================================================================= +# +# To initialize zoxide, add this to your configuration (usually +# ~/.config/fish/config.fish): +# +# zoxide init fish | source diff --git a/config.fish b/config.fish new file mode 100644 index 0000000..ed80e75 --- /dev/null +++ b/config.fish @@ -0,0 +1,107 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# ╭──────────────────────────────────────────────────────────╮ +# │ Fish Configuration │ +# ╰──────────────────────────────────────────────────────────╯ + +# ──────────────────────── Source CachyOS configs ──────────────────────── +source /usr/share/cachyos-fish-config/cachyos-config.fish + +# ───────────────────────── Source user secrets ────────────────────────── +if test -f $HOME/.config/.user-dots/fish/secrets.fish + source $HOME/.config/.user-dots/fish/secrets.fish +end + +# ─────────────────────── Source machine-local config ───────────────────── +if test -f $HOME/.config/.user-dots/fish/local.fish + source $HOME/.config/.user-dots/fish/local.fish +end + +# ──────────────────────────── PATH variables ──────────────────────────── +fish_add_path ~/Applications +fish_add_path ~/scripts +fish_add_path -mg --move ~/.cargo/bin +fish_add_path ~/.npm-global/bin +fish_add_path ~/.lmstudio/bin + +# ─────────────────────────── Editor variables ─────────────────────────── +if command -v nvim >/dev/null + set -gx EDITOR (command -s nvim) +else + set -gx EDITOR (command -s vi) +end +set -gx VISUAL $EDITOR +set -gx SUDO_EDITOR $EDITOR + +# ──────────────────────────── GPG variables ───────────────────────────── +set -gx GPG_TTY (tty) + +# ──────────────────────── Source FZF integration ──────────────────────── +source ~/.config/fish/integrations/fzf.fish +# Configure FZF theme +set -Ux FZF_DEFAULT_OPTS "\ +--color=bg+:#313244,bg:#1E1E2E,spinner:#F5E0DC,hl:#F38BA8 \ +--color=fg:#CDD6F4,header:#F38BA8,info:#CBA6F7,pointer:#F5E0DC \ +--color=marker:#B4BEFE,fg+:#CDD6F4,prompt:#CBA6F7,hl+:#F38BA8 \ +--color=selected-bg:#45475A \ +--color=border:#6C7086,label:#CDD6F4" + +# ──────────────────────────────── DirENV ──────────────────────────────── +# Tool to handle automatic environment loading in directories and their children +# Use when children need to load venv as well. +# +# The Auto-Venv script above will ignore directories with a +# .envrc file (direnv configuration) to prevent conflicts. +direnv hook fish | source + +# ────────────────────────────── Auto-Venv ─────────────────────────────── +# Auto-activate Python venv on directory change +function __auto_source_fallback_venv --on-variable PWD + status --is-command-substitution; and return + + # 1. Skip if direnv is already managing this directory + if set -q DIRENV_DIR; or test -e ".envrc" + return + end + + # 2. If we are already in a venv, check if we've left its tree + if set -q VIRTUAL_ENV + # Check if the current PWD is still within the directory that owns the venv + # (Assuming the venv is at the root of the project) + set -l venv_root (string replace -r '/.venv$' '' $VIRTUAL_ENV) + if not string match -q "$venv_root*" "$PWD" + type -q deactivate; and deactivate + end + return + end + + # 3. Only source the venv if we aren't already in one + if test -e ".venv/bin/activate.fish" + source .venv/bin/activate.fish + end +end + +# ──────────────────── Docker Contexts for LazyDocker ──────────────────── +function ld --description 'Run lazydocker on the current Docker context' + # Fetch the host endpoint of the currently active Docker context + set -l current_host (docker context inspect --format '{{.Endpoints.docker.Host}}') + + # Run lazydocker with the DOCKER_HOST variable set for this command only + env DOCKER_HOST=$current_host lazydocker +end + +# ────────────────────── Claude Code Env Variables ─────────────────────── +set -gx CLAUDE_CODE_NO_FLICKER 1 + +# ───────────────────── Directory shortcut variables ───────────────────── +set -U cdp ~/projects + +# ───────────────────────── CDPATH projects dir ────────────────────────── +# Allows cd-ing to projects automatically from anywhere +set -U CDPATH . ~/projects ~ + +# ─────────────────────────── Starship prompt ──────────────────────────── +# STARSHIP_START +starship init fish | source +# STARSHIP_END diff --git a/fish_plugins b/fish_plugins new file mode 100644 index 0000000..b2b5cf3 --- /dev/null +++ b/fish_plugins @@ -0,0 +1,8 @@ +catppuccin/fish +patrickf1/fzf.fish +jorgebucaran/fisher +jorgebucaran/autopair.fish +jorgebucaran/replay.fish +nickeb96/puffer-fish +mattmc3/magic-enter.fish +jorgebucaran/spark.fish diff --git a/functions/_autopair_backspace.fish b/functions/_autopair_backspace.fish new file mode 100644 index 0000000..a43fa79 --- /dev/null +++ b/functions/_autopair_backspace.fish @@ -0,0 +1,9 @@ +function _autopair_backspace + set --local index (commandline --cursor) + set --local buffer (commandline) + + test $index -ge 1 && + contains -- (string sub --start=$index --length=2 -- "$buffer") $autopair_pairs && + commandline --function delete-char + commandline --function backward-delete-char +end diff --git a/functions/_autopair_insert_left.fish b/functions/_autopair_insert_left.fish new file mode 100644 index 0000000..f078e86 --- /dev/null +++ b/functions/_autopair_insert_left.fish @@ -0,0 +1,13 @@ +function _autopair_insert_left --argument-names left right + set --local buffer (commandline) + set --local before (commandline --cut-at-cursor) + + commandline --insert -- $left + + switch "$buffer" + case "$before"{," "\*,$autopair_right\*} + set --local index (commandline --cursor) + commandline --insert -- $right + commandline --cursor $index + end +end diff --git a/functions/_autopair_insert_right.fish b/functions/_autopair_insert_right.fish new file mode 100644 index 0000000..a0bd61c --- /dev/null +++ b/functions/_autopair_insert_right.fish @@ -0,0 +1,11 @@ +function _autopair_insert_right --argument-names key + set --local buffer (commandline) + set --local before (commandline --cut-at-cursor) + + switch "$buffer" + case "$before$key"\* + commandline --cursor (math (commandline --cursor) + 1) + case \* + commandline --insert -- $key + end +end diff --git a/functions/_autopair_insert_same.fish b/functions/_autopair_insert_same.fish new file mode 100644 index 0000000..27f971d --- /dev/null +++ b/functions/_autopair_insert_same.fish @@ -0,0 +1,20 @@ +function _autopair_insert_same --argument-names key + set --local buffer (commandline) + set --local index (commandline --cursor) + set --local next (string sub --start=(math $index + 1) --length=1 -- "$buffer") + + if test (math (count (string match --all --regex -- "$key" "$buffer")) % 2) = 0 + test $key = $next && commandline --cursor (math $index + 1) && return + + commandline --insert -- $key + + if test $index -lt 1 || + contains -- (string sub --start=$index --length=1 -- "$buffer") "" " " $autopair_left && + contains -- $next "" " " $autopair_right + commandline --insert -- $key + commandline --cursor (math $index + 1) + end + else + commandline --insert -- $key + end +end diff --git a/functions/_autopair_tab.fish b/functions/_autopair_tab.fish new file mode 100644 index 0000000..f2ab8eb --- /dev/null +++ b/functions/_autopair_tab.fish @@ -0,0 +1,7 @@ +function _autopair_tab + commandline --paging-mode && down-or-search && return + + string match --quiet --regex -- '\$[^\s]*"$' (commandline --current-token) && + commandline --function end-of-line --function backward-delete-char + commandline --function complete +end diff --git a/functions/_fzf_configure_bindings_help.fish b/functions/_fzf_configure_bindings_help.fish new file mode 100644 index 0000000..c4df8cd --- /dev/null +++ b/functions/_fzf_configure_bindings_help.fish @@ -0,0 +1,44 @@ +function _fzf_configure_bindings_help --description "Prints the help message for fzf_configure_bindings." + echo "\ +USAGE: + fzf_configure_bindings [--COMMAND=[KEY_SEQUENCE]...] + +DESCRIPTION + fzf_configure_bindings installs key bindings for fzf.fish's commands and erases any bindings it + previously installed. It installs bindings for both default and insert modes. fzf.fish executes + it without options on fish startup to install the out-of-the-box key bindings. + + By default, commands are bound to a mnemonic key sequence, shown below. Each command's binding + can be configured using a namesake corresponding option: + COMMAND | DEFAULT KEY SEQUENCE | CORRESPONDING OPTION + Search Directory | Ctrl+Alt+F (F for file) | --directory + Search Git Log | Ctrl+Alt+L (L for log) | --git_log + Search Git Status | Ctrl+Alt+S (S for status) | --git_status + Search History | Ctrl+R (R for reverse) | --history + Search Processes | Ctrl+Alt+P (P for process) | --processes + Search Variables | Ctrl+V (V for variable) | --variables + Override a command's binding by specifying its corresponding option with the desired key + sequence using fish's key name syntax (e.g. ctrl-f, ctrl-alt-v). Disable a command's binding + by specifying its corresponding option with no value. + + Because fzf_configure_bindings erases bindings it previously installed, it can be cleanly + executed multiple times. Once the desired fzf_configure_bindings command has been found, add it + to your config.fish in order to persist the customized bindings. + + In terms of validation, fzf_configure_bindings fails if passed unknown options. It expects an + equals sign between an option's name and value. However, it does not validate key sequences. + + Pass -h or --help to print this help message and exit. + +EXAMPLES + Default bindings but bind Search Directory to Ctrl+F and Search Variables to Ctrl+Alt+V + \$ fzf_configure_bindings --directory=ctrl-f --variables=ctrl-alt-v + Default bindings but disable Search History + \$ fzf_configure_bindings --history= + An agglomeration of different options + \$ fzf_configure_bindings --git_status=ctrl-g --history=ctrl-h --variables= --processes= + +SEE Also + To learn more about fish key bindings, see bind(1) and fish_key_reader(1). +" +end diff --git a/functions/_fzf_extract_var_info.fish b/functions/_fzf_extract_var_info.fish new file mode 100644 index 0000000..34a4b44 --- /dev/null +++ b/functions/_fzf_extract_var_info.fish @@ -0,0 +1,13 @@ +# helper function for _fzf_search_variables +function _fzf_extract_var_info --argument-names variable_name set_show_output --description "Extract and reformat lines pertaining to \$variable_name from \$set_show_output." + # Extract only the lines about the variable, all of which begin with either + # $variable_name: ...or... $variable_name[ + string match --regex "^\\\$$variable_name(?::|\[).*" <$set_show_output | + # Strip the variable name prefix, including ": " for scope info lines + string replace --regex "^\\\$$variable_name(?:: )?" '' | + # Distill the lines of values, replacing... + # [1]: |value| + # ...with... + # [1] value + string replace --regex ": \|(.*)\|" ' $1' +end diff --git a/functions/_fzf_preview_changed_file.fish b/functions/_fzf_preview_changed_file.fish new file mode 100644 index 0000000..78dd561 --- /dev/null +++ b/functions/_fzf_preview_changed_file.fish @@ -0,0 +1,49 @@ +# helper for _fzf_search_git_status +# arg should be a line from git status --short, e.g. +# MM functions/_fzf_preview_changed_file.fish +# D README.md +# R LICENSE -> "New License" +function _fzf_preview_changed_file --argument-names path_status --description "Show the git diff of the given file." + # remove quotes because they'll be interpreted literally by git diff + # no need to requote when referencing $path because fish does not perform word splitting + # https://fishshell.com/docs/current/fish_for_bash_users.html + set -f path (string unescape (string sub --start 4 $path_status)) + # first letter of short format shows index, second letter shows working tree + # https://git-scm.com/docs/git-status/2.35.0#_short_format + set -f index_status (string sub --length 1 $path_status) + set -f working_tree_status (string sub --start 2 --length 1 $path_status) + + set -f diff_opts --color=always + + if test $index_status = '?' + _fzf_report_diff_type Untracked + _fzf_preview_file $path + else if contains {$index_status}$working_tree_status DD AU UD UA DU AA UU + # Unmerged statuses taken directly from git status help's short format table + # Unmerged statuses are mutually exclusive with other statuses, so if we see + # these, then safe to assume the path is unmerged + _fzf_report_diff_type Unmerged + git diff $diff_opts -- $path + else + if test $index_status != ' ' + _fzf_report_diff_type Staged + + # renames are only detected in the index, never working tree, so only need to test for it here + # https://stackoverflow.com/questions/73954214 + if test $index_status = R + # diff the post-rename path with the original path, otherwise the diff will show the entire file as being added + set -f orig_and_new_path (string split --max 1 -- ' -> ' $path) + git diff --staged $diff_opts -- $orig_and_new_path[1] $orig_and_new_path[2] + # path currently has the form of "original -> current", so we need to correct it before it's used below + set path $orig_and_new_path[2] + else + git diff --staged $diff_opts -- $path + end + end + + if test $working_tree_status != ' ' + _fzf_report_diff_type Unstaged + git diff $diff_opts -- $path + end + end +end diff --git a/functions/_fzf_preview_file.fish b/functions/_fzf_preview_file.fish new file mode 100644 index 0000000..c926475 --- /dev/null +++ b/functions/_fzf_preview_file.fish @@ -0,0 +1,43 @@ +# helper function for _fzf_search_directory and _fzf_search_git_status +function _fzf_preview_file --description "Print a preview for the given file based on its file type." + # because there's no way to guarantee that _fzf_search_directory passes the path to _fzf_preview_file + # as one argument, we collect all the arguments into one single variable and treat that as the path + set -f file_path $argv + + if test -L "$file_path" # symlink + # notify user and recurse on the target of the symlink, which can be any of these file types + set -l target_path (realpath "$file_path") + + set_color yellow + echo "'$file_path' is a symlink to '$target_path'." + set_color normal + + _fzf_preview_file "$target_path" + else if test -f "$file_path" # regular file + if set --query fzf_preview_file_cmd + # need to escape quotes to make sure eval receives file_path as a single arg + eval "$fzf_preview_file_cmd '$file_path'" + else + bat --style=numbers --color=always "$file_path" + end + else if test -d "$file_path" # directory + if set --query fzf_preview_dir_cmd + # see above + eval "$fzf_preview_dir_cmd '$file_path'" + else + # -A list hidden files as well, except for . and .. + # -F helps classify files by appending symbols after the file name + command ls -A -F "$file_path" + end + else if test -c "$file_path" + _fzf_report_file_type "$file_path" "character device file" + else if test -b "$file_path" + _fzf_report_file_type "$file_path" "block device file" + else if test -S "$file_path" + _fzf_report_file_type "$file_path" socket + else if test -p "$file_path" + _fzf_report_file_type "$file_path" "named pipe" + else + echo "$file_path doesn't exist." >&2 + end +end diff --git a/functions/_fzf_report_diff_type.fish b/functions/_fzf_report_diff_type.fish new file mode 100644 index 0000000..cc26fb3 --- /dev/null +++ b/functions/_fzf_report_diff_type.fish @@ -0,0 +1,18 @@ +# helper for _fzf_preview_changed_file +# prints out something like +# ╭────────╮ +# │ Staged │ +# ╰────────╯ +function _fzf_report_diff_type --argument-names diff_type --description "Print a distinct colored header meant to preface a git patch." + # number of "-" to draw is the length of the string to box + 2 for padding + set -f repeat_count (math 2 + (string length $diff_type)) + set -f line (string repeat --count $repeat_count ─) + set -f top_border ╭$line╮ + set -f btm_border ╰$line╯ + + set_color yellow + echo $top_border + echo "│ $diff_type │" + echo $btm_border + set_color normal +end diff --git a/functions/_fzf_report_file_type.fish b/functions/_fzf_report_file_type.fish new file mode 100644 index 0000000..49e02e1 --- /dev/null +++ b/functions/_fzf_report_file_type.fish @@ -0,0 +1,6 @@ +# helper function for _fzf_preview_file +function _fzf_report_file_type --argument-names file_path file_type --description "Explain the file type for a file." + set_color red + echo "Cannot preview '$file_path': it is a $file_type." + set_color normal +end diff --git a/functions/_fzf_search_directory.fish b/functions/_fzf_search_directory.fish new file mode 100644 index 0000000..b00c34a --- /dev/null +++ b/functions/_fzf_search_directory.fish @@ -0,0 +1,32 @@ +function _fzf_search_directory --description "Search the current directory. Replace the current token with the selected file paths." + # Directly use fd binary to avoid output buffering delay caused by a fd alias, if any. + # Debian-based distros install fd as fdfind and the fd package is something else, so + # check for fdfind first. Fall back to "fd" for a clear error message. + set -f fd_cmd (command -v fdfind || command -v fd || echo "fd") + set -f --append fd_cmd --color=always $fzf_fd_opts + + set -f fzf_arguments --multi --ansi $fzf_directory_opts + set -f token (commandline --current-token) + # expand any variables or leading tilde (~) in the token + set -f expanded_token (eval echo -- $token) + # unescape token because it's already quoted so backslashes will mess up the path + set -f unescaped_exp_token (string unescape -- $expanded_token) + + # If the current token is a directory and has a trailing slash, + # then use it as fd's base directory. + if string match --quiet -- "*/" $unescaped_exp_token && test -d "$unescaped_exp_token" + set --append fd_cmd --base-directory=$unescaped_exp_token + # use the directory name as fzf's prompt to indicate the search is limited to that directory + set --prepend fzf_arguments --prompt="Directory $unescaped_exp_token> " --preview="_fzf_preview_file $expanded_token{}" + set -f file_paths_selected $unescaped_exp_token($fd_cmd 2>/dev/null | _fzf_wrapper $fzf_arguments) + else + set --prepend fzf_arguments --prompt="Directory> " --query="$unescaped_exp_token" --preview='_fzf_preview_file {}' + set -f file_paths_selected ($fd_cmd 2>/dev/null | _fzf_wrapper $fzf_arguments) + end + + if test $status -eq 0 + commandline --current-token --replace -- (string escape -- $file_paths_selected | string join ' ') + end + + commandline --function repaint +end diff --git a/functions/_fzf_search_git_log.fish b/functions/_fzf_search_git_log.fish new file mode 100644 index 0000000..aa54724 --- /dev/null +++ b/functions/_fzf_search_git_log.fish @@ -0,0 +1,36 @@ +function _fzf_search_git_log --description "Search the output of git log and preview commits. Replace the current token with the selected commit hash." + if not git rev-parse --git-dir >/dev/null 2>&1 + echo '_fzf_search_git_log: Not in a git repository.' >&2 + else + if not set --query fzf_git_log_format + # %h gives you the abbreviated commit hash, which is useful for saving screen space, but we will have to expand it later below + set -f fzf_git_log_format '%C(bold blue)%h%C(reset) - %C(cyan)%ad%C(reset) %C(yellow)%d%C(reset) %C(normal)%s%C(reset) %C(dim normal)[%an]%C(reset)' + end + + set -f preview_cmd 'git show --color=always --stat --patch {1}' + if set --query fzf_diff_highlighter + set preview_cmd "$preview_cmd | $fzf_diff_highlighter" + end + + set -f selected_log_lines ( + git log --no-show-signature --color=always --format=format:$fzf_git_log_format --date=short | \ + _fzf_wrapper --ansi \ + --multi \ + --scheme=history \ + --prompt="Git Log> " \ + --preview=$preview_cmd \ + --query=(commandline --current-token) \ + $fzf_git_log_opts + ) + if test $status -eq 0 + for line in $selected_log_lines + set -f abbreviated_commit_hash (string split --field 1 " " $line) + set -f full_commit_hash (git rev-parse $abbreviated_commit_hash) + set -f --append commit_hashes $full_commit_hash + end + commandline --current-token --replace (string join ' ' $commit_hashes) + end + end + + commandline --function repaint +end diff --git a/functions/_fzf_search_git_status.fish b/functions/_fzf_search_git_status.fish new file mode 100644 index 0000000..358f88c --- /dev/null +++ b/functions/_fzf_search_git_status.fish @@ -0,0 +1,41 @@ +function _fzf_search_git_status --description "Search the output of git status. Replace the current token with the selected file paths." + if not git rev-parse --git-dir >/dev/null 2>&1 + echo '_fzf_search_git_status: Not in a git repository.' >&2 + else + set -f preview_cmd '_fzf_preview_changed_file {}' + if set --query fzf_diff_highlighter + set preview_cmd "$preview_cmd | $fzf_diff_highlighter" + end + + set -f selected_paths ( + # Pass configuration color.status=always to force status to use colors even though output is sent to a pipe + git -c color.status=always status --short | + _fzf_wrapper --ansi \ + --multi \ + --prompt="Git Status> " \ + --query=(commandline --current-token) \ + --preview=$preview_cmd \ + --nth="2.." \ + $fzf_git_status_opts + ) + if test $status -eq 0 + # git status --short automatically escapes the paths of most files for us so not going to bother trying to handle + # the few edges cases of weird file names that should be extremely rare (e.g. "this;needs;escaping") + set -f cleaned_paths + + for path in $selected_paths + if test (string sub --length 1 $path) = R + # path has been renamed and looks like "R LICENSE -> LICENSE.md" + # extract the path to use from after the arrow + set --append cleaned_paths (string split -- "-> " $path)[-1] + else + set --append cleaned_paths (string sub --start=4 $path) + end + end + + commandline --current-token --replace -- (string join ' ' $cleaned_paths) + end + end + + commandline --function repaint +end diff --git a/functions/_fzf_search_history.fish b/functions/_fzf_search_history.fish new file mode 100644 index 0000000..cafbce9 --- /dev/null +++ b/functions/_fzf_search_history.fish @@ -0,0 +1,39 @@ +function _fzf_search_history --description "Search command history. Replace the command line with the selected command." + # history merge incorporates history changes from other fish sessions + # it errors out if called in private mode + if test -z "$fish_private_mode" + builtin history merge + end + + if not set --query fzf_history_time_format + # Reference https://devhints.io/strftime to understand strftime format symbols + set -f fzf_history_time_format "%m-%d %H:%M:%S" + end + + # Delinate time from command in history entries using the vertical box drawing char (U+2502). + # Then, to get raw command from history entries, delete everything up to it. The ? on regex is + # necessary to make regex non-greedy so it won't match into commands containing the char. + set -f time_prefix_regex '^.*? │ ' + # Delinate commands throughout pipeline using null rather than newlines because commands can be multi-line + set -f commands_selected ( + builtin history --null --show-time="$fzf_history_time_format │ " | + _fzf_wrapper --read0 \ + --print0 \ + --multi \ + --scheme=history \ + --prompt="History> " \ + --query=(commandline) \ + --preview="string replace --regex '$time_prefix_regex' '' -- {} | fish_indent --ansi" \ + --preview-window="bottom:3:wrap" \ + $fzf_history_opts | + string split0 | + # remove timestamps from commands selected + string replace --regex $time_prefix_regex '' + ) + + if test $status -eq 0 + commandline --replace -- $commands_selected + end + + commandline --function repaint +end diff --git a/functions/_fzf_search_processes.fish b/functions/_fzf_search_processes.fish new file mode 100644 index 0000000..133a880 --- /dev/null +++ b/functions/_fzf_search_processes.fish @@ -0,0 +1,32 @@ +function _fzf_search_processes --description "Search all running processes. Replace the current token with the pid of the selected process." + # Directly use ps command because it is often aliased to a different command entirely + # or with options that dirty the search results and preview output + set -f ps_cmd (command -v ps || echo "ps") + # use all caps to be consistent with ps default format + # snake_case because ps doesn't seem to allow spaces in the field names + set -f ps_preview_fmt (string join ',' 'pid' 'ppid=PARENT' 'user' '%cpu' 'rss=RSS_IN_KB' 'start=START_TIME' 'command') + set -f processes_selected ( + $ps_cmd -A -opid,command | \ + _fzf_wrapper --multi \ + --prompt="Processes> " \ + --query (commandline --current-token) \ + --ansi \ + # first line outputted by ps is a header, so we need to mark it as so + --header-lines=1 \ + # ps uses exit code 1 if the process was not found, in which case show an message explaining so + --preview="$ps_cmd -o '$ps_preview_fmt' -p {1} || echo 'Cannot preview {1} because it exited.'" \ + --preview-window="bottom:4:wrap" \ + $fzf_processes_opts + ) + + if test $status -eq 0 + for process in $processes_selected + set -f --append pids_selected (string split --no-empty --field=1 -- " " $process) + end + + # string join to replace the newlines outputted by string split with spaces + commandline --current-token --replace -- (string join ' ' $pids_selected) + end + + commandline --function repaint +end diff --git a/functions/_fzf_search_variables.fish b/functions/_fzf_search_variables.fish new file mode 100644 index 0000000..52a7c70 --- /dev/null +++ b/functions/_fzf_search_variables.fish @@ -0,0 +1,47 @@ +# This function expects the following two arguments: +# argument 1 = output of (set --show | psub), i.e. a file with the scope info and values of all variables +# argument 2 = output of (set --names | psub), i.e. a file with all variable names +function _fzf_search_variables --argument-names set_show_output set_names_output --description "Search and preview shell variables. Replace the current token with the selected variable." + if test -z "$set_names_output" + printf '%s\n' '_fzf_search_variables requires 2 arguments.' >&2 + + commandline --function repaint + return 22 # 22 means invalid argument in POSIX + end + + # Exclude the history variable from being piped into fzf because + # 1. it's not included in $set_names_output + # 2. it tends to be a very large value => increases computation time + # 3._fzf_search_history is a much better way to examine history anyway + set -f all_variable_names (string match --invert history <$set_names_output) + + set -f current_token (commandline --current-token) + # Use the current token to pre-populate fzf's query. If the current token begins + # with a $, remove it from the query so that it will better match the variable names + set -f cleaned_curr_token (string replace -- '$' '' $current_token) + + set -f variable_names_selected ( + printf '%s\n' $all_variable_names | + _fzf_wrapper --preview "_fzf_extract_var_info {} $set_show_output" \ + --prompt="Variables> " \ + --preview-window="wrap" \ + --multi \ + --query=$cleaned_curr_token \ + $fzf_variables_opts + ) + + if test $status -eq 0 + # If the current token begins with a $, do not overwrite the $ when + # replacing the current token with the selected variable. + # Uses brace expansion to prepend $ to each variable name. + commandline --current-token --replace ( + if string match --quiet -- '$*' $current_token + string join " " \${$variable_names_selected} + else + string join " " $variable_names_selected + end + ) + end + + commandline --function repaint +end diff --git a/functions/_fzf_wrapper.fish b/functions/_fzf_wrapper.fish new file mode 100644 index 0000000..486e36c --- /dev/null +++ b/functions/_fzf_wrapper.fish @@ -0,0 +1,21 @@ +function _fzf_wrapper --description "Prepares some environment variables before executing fzf." + # Make sure fzf uses fish to execute preview commands, some of which + # are autoloaded fish functions so don't exist in other shells. + # Use --function so that it doesn't clobber SHELL outside this function. + set -f --export SHELL (command --search fish) + + # If neither FZF_DEFAULT_OPTS nor FZF_DEFAULT_OPTS_FILE are set, then set some sane defaults. + # See https://github.com/junegunn/fzf#environment-variables + set --query FZF_DEFAULT_OPTS FZF_DEFAULT_OPTS_FILE + if test $status -eq 2 + # cycle allows jumping between the first and last results, making scrolling faster + # layout=reverse lists results top to bottom, mimicking the familiar layouts of git log, history, and env + # border shows where the fzf window begins and ends + # height=90% leaves space to see the current command and some scrollback, maintaining context of work + # preview-window=wrap wraps long lines in the preview window, making reading easier + # marker=* makes the multi-select marker more distinguishable from the pointer (since both default to >) + set --export FZF_DEFAULT_OPTS '--cycle --layout=reverse --border --height=90% --preview-window=wrap --marker="*"' + end + + fzf $argv +end diff --git a/functions/_puffer_fish_expand_bang.fish b/functions/_puffer_fish_expand_bang.fish new file mode 100644 index 0000000..ea3f9ea --- /dev/null +++ b/functions/_puffer_fish_expand_bang.fish @@ -0,0 +1,10 @@ +function _puffer_fish_expand_bang + if commandline --search-field >/dev/null + commandline --search-field --insert '!' + else if string match --quiet -- '!' "$(commandline --current-token)" + commandline --current-token $history[1] + else + commandline --insert '!' + end +end + diff --git a/functions/_puffer_fish_expand_buck.fish b/functions/_puffer_fish_expand_buck.fish new file mode 100644 index 0000000..260d3b4 --- /dev/null +++ b/functions/_puffer_fish_expand_buck.fish @@ -0,0 +1,10 @@ +function _puffer_fish_expand_buck + if commandline --search-field >/dev/null + commandline --search-field --insert '$' + else if string match --quiet '!' -- "$(commandline --current-token)" + commandline --current-token '' + commandline -f history-token-search-backward + else + commandline --insert '$' + end +end diff --git a/functions/_puffer_fish_expand_dot.fish b/functions/_puffer_fish_expand_dot.fish new file mode 100644 index 0000000..c6f2483 --- /dev/null +++ b/functions/_puffer_fish_expand_dot.fish @@ -0,0 +1,9 @@ +function _puffer_fish_expand_dot + if commandline --search-field >/dev/null + commandline --search-field --insert '.' + else if string match --quiet --regex -- '^(\.\./)*\.\.$' "$(commandline --current-token)" + commandline --insert '/..' + else + commandline --insert '.' + end +end diff --git a/functions/_puffer_fish_expand_star.fish b/functions/_puffer_fish_expand_star.fish new file mode 100644 index 0000000..b74c382 --- /dev/null +++ b/functions/_puffer_fish_expand_star.fish @@ -0,0 +1,15 @@ +function _puffer_fish_expand_star + if commandline --search-field >/dev/null + commandline --search-field --insert '*' + else if string match --quiet -- '!' "$(commandline --current-token)" + set -l prev_cmd $history[1] + set -l prev_args (string split ' ' $prev_cmd) + set -e prev_args[1] # remove command name + set -l arg_str (string join ' ' $prev_args) + # replace !* with all arguments + commandline --current-token '' + commandline --insert $arg_str + else + commandline --insert '*' + end +end diff --git a/functions/antigravity.fish b/functions/antigravity.fish new file mode 100644 index 0000000..68f43fb --- /dev/null +++ b/functions/antigravity.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function antigravity + # In fish, we pipe stderr using '2>|' to another command + command antigravity $argv 2>| grep -v "'app' is not in the list of known options" >&2 +end diff --git a/functions/bash.fish b/functions/bash.fish new file mode 100644 index 0000000..570ef48 --- /dev/null +++ b/functions/bash.fish @@ -0,0 +1,8 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function bash --wraps=bash --description 'bash switches to bash shell' + set SHELL $(which bash) # Set shell to bash + command bash $argv # run bash + set SHELL $(which fish) # Reset shell +end diff --git a/functions/bd-pull.fish b/functions/bd-pull.fish new file mode 100644 index 0000000..7e47a88 --- /dev/null +++ b/functions/bd-pull.fish @@ -0,0 +1,59 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function bd-pull -d "Pull new Gitea issues into local Beads and link them" + if not set -q argv[1]; echo "Need repo owner/name"; return 1; end + if not set -q GITEA_TOKEN; echo "\$GITEA_TOKEN not set"; return 1; end + + set -l REPO $argv[1] + set -l GITEA_URL "https://git.rootiest.dev" + set -l IMPORT_COUNT 0 + + echo (set_color blue)"📡 Checking Gitea: $REPO..."(set_color normal) + + # 1. Fetch issues (using jq to get BOTH title and number) + set -l gitea_json (curl -s -H "Authorization: token $GITEA_TOKEN" "$GITEA_URL/api/v1/repos/$REPO/issues?state=all") + + # 2. Iterate through issues using their index/number + for row in (echo $gitea_json | jq -r '.[] | @base64') + set -l _decode (echo $row | base64 --decode) + set -l title (echo $_decode | jq -r '.title') + set -l number (echo $_decode | jq -r '.number') + + # If it doesn't have [ID] brackets, it's a "Web-Original" issue + if not string match -qr "^\[.*\]" "$title" + echo (set_color yellow)"➕ Linking Web Issue #$number: $title"(set_color normal) + + # A. Create local Bead and capture the new ID + # This captures the output of bd create to find the ID it generated + set -l bd_output (bd create --title "$title") + set -l bid (echo $bd_output | string match -r "bd-[a-z0-9]+" | head -n 1) + + if test -z "$bid" + # Fallback: find the latest ID in the jsonl if regex fails + set bid (tail -n 1 .beads/issues.jsonl | jq -r '.id') + end + + # B. Update the Gitea Issue Title IMMEDIATELY via API + # This prevents the Gitea Action from creating a duplicate + set -l new_title "[$bid] $title" + curl -s -X PATCH -H "Authorization: token $GITEA_TOKEN" \ + -H "Content-Type: application/json" \ + -d "{\"title\":\"$new_title\"}" \ + "$GITEA_URL/api/v1/repos/$REPO/issues/$number" > /dev/null + + set IMPORT_COUNT (math $IMPORT_COUNT + 1) + end + end + + if test "$IMPORT_COUNT" -gt 0 + echo (set_color green)"✅ Linked $IMPORT_COUNT issues."(set_color normal) + bd sync + # We don't even necessarily need to push now, because the titles match! + git add .beads/issues.jsonl + git commit -m "chore: sync local IDs for web issues" + git push + else + echo "⠿ No unlinked issues found." + end +end diff --git a/functions/bkg.fish b/functions/bkg.fish new file mode 100644 index 0000000..d405316 --- /dev/null +++ b/functions/bkg.fish @@ -0,0 +1,26 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Function to run a command in the background, detached from the terminal. +# This prevents the command from being terminated when the terminal is closed. +# All output (stdout and stderr) is discarded. +# +# Usage: +# bkg [arguments...] +# +# Example: +# bkg firefox +# bkg code . +# +function bkg + # Check if a command was provided as an argument. + if test -z "$argv[1]" + echo "Usage: bkg [arguments...]" + return 1 + end + + # Run the command using nohup to make it immune to hangups (like closing the terminal). + # Redirect both stdout and stderr to /dev/null to discard all output. + # The final ampersand (&) sends the entire process to the background. + nohup $argv &>/dev/null & +end diff --git a/functions/cat.fish b/functions/cat.fish new file mode 100644 index 0000000..1561319 --- /dev/null +++ b/functions/cat.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function cat --wraps=bat --description 'alias cat=bat' + bat --plain --no-pager $argv +end diff --git a/functions/cb.fish b/functions/cb.fish new file mode 100644 index 0000000..4fd1b9a --- /dev/null +++ b/functions/cb.fish @@ -0,0 +1,21 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function cb --description 'Copy to clipboard' + if type -q wl-copy + if set -q argv[1] + echo $argv | wl-copy + else + wl-copy + end + else if type -q xclip + if set -q argv[1] + echo $argv | xclip -selection clipboard + else + xclip -selection clipboard + end + else + echo "Error: No clipboard provider found." >&2 + return 1 + end +end diff --git a/functions/cffetch.fish b/functions/cffetch.fish new file mode 100644 index 0000000..255c18b --- /dev/null +++ b/functions/cffetch.fish @@ -0,0 +1,19 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function cffetch --wraps='clear;fastfetch' --description 'alias cffetch=clear;fastfetch' + clear + if which fastfetch >/dev/null 2>&1 + if ls ~/.fastfetch.jsonc >/dev/null 2>&1 + fastfetch --config ~/.fastfetch.jsonc $argv + else + fastfetch $argv + end + else + if which neofetch >/dev/null 2>&1 + command neofetch $argv + else + echo "fetch not found" + end + end +end diff --git a/functions/cheat.fish b/functions/cheat.fish new file mode 100644 index 0000000..b807c58 --- /dev/null +++ b/functions/cheat.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function cheat --description 'alias cheat=cheat -c' + command cheat -c $argv + +end diff --git a/functions/claude-resume.fish b/functions/claude-resume.fish new file mode 100644 index 0000000..675ac66 --- /dev/null +++ b/functions/claude-resume.fish @@ -0,0 +1,12 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function claude-resume + if test -f .claude_session + set -l sid (cat .claude_session) + claude --resume $sid + else + echo "No saved session found in this directory." + claude --resume # Fallback to the interactive picker + end +end diff --git a/functions/cleanup.fish b/functions/cleanup.fish new file mode 100644 index 0000000..42071e8 --- /dev/null +++ b/functions/cleanup.fish @@ -0,0 +1,16 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function cleanup -d "Log orphans to ~/.removed_orphans and remove them" + set -l orphans (pacman -Qtdq) + if test -n "$orphans" + echo "📝 Logging orphans to ~/.removed_orphans..." + echo "--- Removed on $(date) ---" >> ~/.removed_orphans + pacman -Qi $orphans | grep -E '^(Name|Version)' >> ~/.removed_orphans + + echo "🧹 Removing orphans..." + sudo pacman -Rns $orphans + else + echo "✨ No orphans to clean up!" + end +end diff --git a/functions/clone.fish b/functions/clone.fish new file mode 100644 index 0000000..6ac66ba --- /dev/null +++ b/functions/clone.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function clone --wraps=clone-in-kitty --description 'alias clone=clone-in-kitty' + clone-in-kitty $argv + +end diff --git a/functions/clonet.fish b/functions/clonet.fish new file mode 100644 index 0000000..33cdded --- /dev/null +++ b/functions/clonet.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function clonet --wraps='clone-in-kitty --type=tab' --description 'alias clonet=clone-in-kitty --type=tab' + clone-in-kitty --type=tab $argv + +end diff --git a/functions/code-resume.fish b/functions/code-resume.fish new file mode 100644 index 0000000..921a9b7 --- /dev/null +++ b/functions/code-resume.fish @@ -0,0 +1,17 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function code-resume + if test -f .claude_session + set -l sid (cat .claude_session) + echo "Resuming Claude session: $sid" + claude --resume $sid + else if test -f .gemini_session + set -l sid (cat .gemini_session) + echo "Resuming Gemini session: $sid" + gemini --resume $sid + else + echo "No local AI session found. Opening picker..." + claude --resume # Default to Claude picker + end +end diff --git a/functions/detach.fish b/functions/detach.fish new file mode 100644 index 0000000..00e0bd1 --- /dev/null +++ b/functions/detach.fish @@ -0,0 +1,45 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function detach + set -l show_help 0 + set -l args + + for arg in $argv + switch $arg + case -h --help + set show_help 1 + case --version + echo "detach 1.0.0" + return + case '-*' '--*' + echo "❌ Unknown option: $arg" + echo "Run 'detach --help' for usage." + return 1 + case '*' + set args $args $arg + end + end + + if test $show_help -eq 1 + echo "Usage: detach [command...]" + echo "Runs a command in the background, fully detached from the terminal." + echo + echo "Options:" + echo " -h, --help Show this help message" + echo " --version Show version information" + echo + echo "Example:" + echo " detach firefox" + echo " detach rsync -a ./data remote:/backup/" + return + end + + if test (count $args) -eq 0 + echo "❌ No command provided." + echo "Run 'detach --help' for usage." + return 1 + end + + nohup $args >/dev/null 2>&1 & +end diff --git a/functions/dng2avif.fish b/functions/dng2avif.fish new file mode 100644 index 0000000..27e1840 --- /dev/null +++ b/functions/dng2avif.fish @@ -0,0 +1,100 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function dng2avif --description 'Convert DNG raw to 10-bit HDR AVIF' + set -l options (fish_opt -s h -l help) + set -a options (fish_opt -s i -l input -r) + set -a options (fish_opt -s o -l output -r) + set -a options (fish_opt -s q -l quality -r) + set -a options (fish_opt -s s -l speed -r) + + argparse $options -- $argv + or return + + # Help Screen + if set -q _flag_help; or test (count $argv) -eq 0 -a -z "$_flag_input" + echo "Usage: dng2avif [options] [input.dng]" + echo "" + echo "Options:" + echo " -i, --input FILE Input DNG file" + echo " -o, --output FILE Output AVIF file (defaults to input name)" + echo " -q, --quality N Encoding quality 0-100 (default: 92)" + echo " -s, --speed N Encoder speed 0-10 (default: 3, 0=slowest)" + echo " -h, --help Show this help message" + return 0 + end + + # Handle Input Source + set -l input "" + if set -q _flag_input + set input $_flag_input + else + set input $argv[1] + end + + if not test -f "$input" + echo (set_color red)"Error: File '$input' not found."(set_color normal) + return 1 + end + + # Configuration + set -l quality 92 + if set -q _flag_quality + set quality $_flag_quality + end + + set -l speed 3 + if set -q _flag_speed + set speed $_flag_speed + end + + set -l basename (string replace -r '\.dng$' '' $input) + set -l output "$basename.avif" + if set -q _flag_output + set output $_flag_output + end + + set -l temp_pnm "$basename"_temp.pnm + + # Dependency check + for cmd in magick ffmpeg avifenc exiftool + if not type -q $cmd + echo (set_color red)"Error: $cmd is not installed."(set_color normal) + return 1 + end + end + + # Step 1: Develop + echo -n (set_color blue)"Step 1/3: Developing... "(set_color normal) + magick "$input" -depth 16 pnm:"$temp_pnm" &>/dev/null + if test $status -ne 0 + echo (set_color red)"FAILED" + return 1 + end + echo (set_color green)"DONE"(set_color normal) + + # Step 2: Encode + echo -n (set_color blue)"Step 2/3: Encoding (Q:$quality, S:$speed)... "(set_color normal) + ffmpeg -i "$temp_pnm" -vf "format=yuv444p10le" -f yuv4mpegpipe -strict -1 - 2>/dev/null | avifenc -q $quality -s $speed --stdin -d 10 -y 444 --cicp 12/1/1 -r f -o "$output" &>/dev/null + if test $status -ne 0 + echo (set_color red)"FAILED" + rm -f "$temp_pnm" + return 1 + end + echo (set_color green)"DONE"(set_color normal) + + # Step 3: Metadata + echo -n (set_color blue)"Step 3/3: Syncing Metadata... "(set_color normal) + exiftool -tagsFromFile "$input" -all:all "$output" -overwrite_original &>/dev/null + if test $status -ne 0 + echo (set_color red)"FAILED" + else + echo (set_color green)"DONE"(set_color normal) + end + + # Final Cleanup + test -f "$temp_pnm"; and rm "$temp_pnm" + + set -l size (stat -c '%s' "$output" | numfmt --to=iec) + echo (set_color yellow)"Complete: $output ($size)"(set_color normal) +end diff --git a/functions/dockup.fish b/functions/dockup.fish new file mode 100644 index 0000000..c78e683 --- /dev/null +++ b/functions/dockup.fish @@ -0,0 +1,57 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function dockup --description 'Pull and restart docker compose containers' + # Define colors + set -l clr_error (set_color red) + set -l clr_info (set_color -b blue white) + set -l clr_success (set_color green) + set -l clr_off (set_color normal) + + # Handle help flags + if contains -- -h $argv; or contains -- --help $argv + echo (set_color -o yellow)"Usage:"$clr_off" dockup [DIRECTORY]" + echo "" + echo (set_color -u)"Options:"$clr_off + echo " -h, --help Show this help message" + echo "" + echo (set_color -u)"Arguments:"$clr_off + echo " DIRECTORY Optional path to the compose project (defaults to current dir)" + return 0 + end + + # Handle directory navigation + if count $argv >/dev/null + set -l target_dir $argv[1] + if test -d $target_dir + pushd $target_dir >/dev/null + else + echo $clr_error"Error: Directory '$target_dir' not found."$clr_off + return 1 + end + end + + # Check for compose file + if not test -f docker-compose.yml -o -f docker-compose.yaml + echo $clr_error"Error: No docker-compose.yml found in "(pwd)$clr_off + if count $argv >/dev/null + popd >/dev/null + end + return 1 + end + + # Execution + echo $clr_info" UPDATING "$clr_off" Containers in "(set_color -o)(pwd)$clr_off"..." + + if docker compose pull && docker compose up -d --remove-orphans + echo $clr_success"✔ Upgrade complete!"$clr_off + docker image prune -f + else + echo $clr_error"✘ Upgrade failed."$clr_off + end + + # Cleanup directory state + if count $argv >/dev/null + popd >/dev/null + end +end diff --git a/functions/dops.fish b/functions/dops.fish new file mode 100644 index 0000000..c17e519 --- /dev/null +++ b/functions/dops.fish @@ -0,0 +1,13 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function docker + if test -n "$argv[1]" + switch $argv[1] + case ps + dops $argv[2..-1] + case '*' + command docker $argv[1..-1] + end + end +end diff --git a/functions/du.fish b/functions/du.fish new file mode 100644 index 0000000..3ecba51 --- /dev/null +++ b/functions/du.fish @@ -0,0 +1,63 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function du + set cmd "" + set args + + # Parse override flags and gather remaining args + for arg in $argv + switch $arg + case --disk + set cmd duf + case --dir + set cmd dust + case --dua + set cmd dua + case '*' + set args $args $arg + end + end + + # Autodetect if no override flag given + if test -z "$cmd" + if count $args + set first_arg $args[1] + if test -d "$first_arg" -o -f "$first_arg" + set cmd dust + else + set cmd duf + end + else + set cmd duf + end + end + + # Tool execution with graceful fallback + switch $cmd + case duf + if type -q duf + duf $args + else + echo "(duf not found — falling back to du)" + command du -sh $args + end + case dust + if type -q dust + dust $args + else + echo "(dust not found — falling back to du)" + command du -sh $args + end + case dua + if type -q dua + dua $args + else + echo "(dua not found — falling back to du)" + command du -sh $args + end + case '*' + # This shouldn't happen, but just in case + command du -sh $args + end +end diff --git a/functions/dusize.fish b/functions/dusize.fish new file mode 100644 index 0000000..446ab65 --- /dev/null +++ b/functions/dusize.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function dusize + du -sh (test -n "$argv[1]"; and echo $argv[1]; or echo .) +end diff --git a/functions/edit.fish b/functions/edit.fish new file mode 100644 index 0000000..dc95932 --- /dev/null +++ b/functions/edit.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function edit --wraps=nvim --description 'alias edit=nvim' + nvim $argv +end diff --git a/functions/ffetch.fish b/functions/ffetch.fish new file mode 100644 index 0000000..67cd120 --- /dev/null +++ b/functions/ffetch.fish @@ -0,0 +1,18 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function ffetch --wraps='fastfetch' --description 'alias ffetch=fastfetch' + if which fastfetch >/dev/null 2>&1 + if ls ~/.fastfetch.jsonc >/dev/null 2>&1 + fastfetch --config ~/.fastfetch.jsonc $argv + else + fastfetch $argv + end + else + if which neofetch >/dev/null 2>&1 + command neofetch $argv + else + echo "fetch not found" + end + end +end diff --git a/functions/fish_right_prompt.fish b/functions/fish_right_prompt.fish new file mode 100644 index 0000000..583f911 --- /dev/null +++ b/functions/fish_right_prompt.fish @@ -0,0 +1,22 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function fish_right_prompt + # 1. Docker Context in Blue + set -l docker_ctx (docker context show 2>/dev/null) + if test -n "$docker_ctx"; and test "$docker_ctx" != default + set_color blue + echo -n "󰡨 $docker_ctx " + set_color normal + end + + # 2. Timestamp Logic with Fallback + set_color brblack + if type -q __bobthefish_timestamp + __bobthefish_timestamp + else + # Manual fallback format: Wed Feb 11 15:04:28 2026 + date "+%a %b %d %H:%M:%S %Y" + end + set_color normal +end diff --git a/functions/fisher.fish b/functions/fisher.fish new file mode 100644 index 0000000..24f7b54 --- /dev/null +++ b/functions/fisher.fish @@ -0,0 +1,254 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function fisher --argument-names cmd --description "A plugin manager for Fish" + set --query fisher_path || set --local fisher_path $__fish_config_dir + set --local fisher_version 4.4.8 + set --local fish_plugins $__fish_config_dir/fish_plugins + + switch "$cmd" + case -v --version + echo "fisher, version $fisher_version" + case "" -h --help + echo "Usage: fisher install Install plugins" + echo " fisher remove Remove installed plugins" + echo " fisher uninstall Remove installed plugins (alias)" + echo " fisher update Update installed plugins" + echo " fisher update Update all installed plugins" + echo " fisher list [] List installed plugins matching regex" + echo "Options:" + echo " -v, --version Print version" + echo " -h, --help Print this help message" + echo "Variables:" + echo " \$fisher_path Plugin installation path. Default: $__fish_config_dir" | string replace --regex -- $HOME \~ + case ls list + string match --entire --regex -- "$argv[2]" $_fisher_plugins + case install update remove uninstall + isatty || read --local --null --array stdin && set --append argv $stdin + + test "$cmd" = uninstall && set cmd remove + + set --local install_plugins + set --local update_plugins + set --local remove_plugins + set --local arg_plugins $argv[2..-1] + set --local old_plugins $_fisher_plugins + set --local new_plugins + + test -e $fish_plugins && set --local file_plugins (string match --regex -- '^[^\s]+$' <$fish_plugins | string replace -- \~ ~) + + if ! set --query argv[2] + if test "$cmd" != update + echo "fisher: Not enough arguments for command: \"$cmd\"" >&2 && return 1 + else if ! set --query file_plugins + echo "fisher: \"$fish_plugins\" file not found: \"$cmd\"" >&2 && return 1 + end + set arg_plugins $file_plugins + else if test "$cmd" = install && ! set --query old_plugins[1] + set --append arg_plugins $file_plugins + end + + for plugin in $arg_plugins + set plugin (test -e "$plugin" && realpath $plugin || string lower -- $plugin) + contains -- "$plugin" $new_plugins || set --append new_plugins $plugin + end + + if set --query argv[2] + for plugin in $new_plugins + if contains -- "$plugin" $old_plugins + test "$cmd" = remove && + set --append remove_plugins $plugin || + set --append update_plugins $plugin + else if test "$cmd" = install + set --append install_plugins $plugin + else + echo "fisher: Plugin not installed: \"$plugin\"" >&2 && return 1 + end + end + else + for plugin in $new_plugins + contains -- "$plugin" $old_plugins && + set --append update_plugins $plugin || + set --append install_plugins $plugin + end + + for plugin in $old_plugins + contains -- "$plugin" $new_plugins || set --append remove_plugins $plugin + end + end + + set --local pid_list + set --local source_plugins + set --local fetch_plugins $update_plugins $install_plugins + set --local fish_path (status fish-path) + + echo (set_color --bold)fisher $cmd version $fisher_version(set_color normal) + + for plugin in $fetch_plugins + set --local source (command mktemp -d) + set --append source_plugins $source + + command mkdir -p $source/{completions,conf.d,themes,functions} + + $fish_path --command " + if test -e $plugin + command cp -Rf $plugin/* $source + else + set resp (command mktemp) + set temp (command mktemp -d) + set repo (string split -- \@ $plugin) || set repo[2] HEAD + + if set path (string replace --regex -- '^(https://)?gitlab.com/' '' \$repo[1]) + set name (string split -- / \$path)[-1] + set url https://gitlab.com/\$path/-/archive/\$repo[2]/\$name-\$repo[2].tar.gz + else + set url https://api.github.com/repos/\$repo[1]/tarball/\$repo[2] + end + + echo Fetching (set_color --underline)\$url(set_color normal) + + set http (command curl -q --silent -L -o \$resp -w %{http_code} \$url) + + if test \"\$http\" = 200 && command tar -xzC \$temp -f \$resp 2>/dev/null + command cp -Rf \$temp/*/* $source + else if test \"\$http\" = 403 + echo fisher: GitHub API rate limit exceeded \(HTTP 403\) >&2 + command rm -rf $source + else + echo fisher: Invalid plugin name or host unavailable: \\\"$plugin\\\" >&2 + command rm -rf $source + end + + command rm -rf \$temp + end + + set files $source/* && string match --quiet --regex -- .+\.fish\\\$ \$files + " & + + set --append pid_list (jobs --last --pid) + end + + wait $pid_list 2>/dev/null + + for plugin in $fetch_plugins + if set --local source $source_plugins[(contains --index -- "$plugin" $fetch_plugins)] && test ! -e $source + if set --local index (contains --index -- "$plugin" $install_plugins) + set --erase install_plugins[$index] + else + set --erase update_plugins[(contains --index -- "$plugin" $update_plugins)] + end + end + end + + for plugin in $update_plugins $remove_plugins + if set --local index (contains --index -- "$plugin" $_fisher_plugins) + set --local plugin_files_var _fisher_(string escape --style=var -- $plugin)_files + + if contains -- "$plugin" $remove_plugins + for name in (string replace --filter --regex -- '.+/conf\.d/([^/]+)\.fish$' '$1' $$plugin_files_var) + emit {$name}_uninstall + end + printf "%s\n" Removing\ (set_color red --bold)$plugin(set_color normal) " "$$plugin_files_var | string replace -- \~ ~ + set --erase _fisher_plugins[$index] + end + + command rm -rf (string replace -- \~ ~ $$plugin_files_var) + + functions --erase (string replace --filter --regex -- '.+/functions/([^/]+)\.fish$' '$1' $$plugin_files_var) + + for name in (string replace --filter --regex -- '.+/completions/([^/]+)\.fish$' '$1' $$plugin_files_var) + complete --erase --command $name + end + + set --erase $plugin_files_var + end + end + + if set --query update_plugins[1] || set --query install_plugins[1] + command mkdir -p $fisher_path/{functions,themes,conf.d,completions} + end + + for plugin in $update_plugins $install_plugins + set --local source $source_plugins[(contains --index -- "$plugin" $fetch_plugins)] + set --local files $source/{functions,themes,conf.d,completions}/* + + if set --local index (contains --index -- $plugin $install_plugins) + set --local user_files $fisher_path/{functions,themes,conf.d,completions}/* + set --local conflict_files + + for file in (string replace -- $source/ $fisher_path/ $files) + contains -- $file $user_files && set --append conflict_files $file + end + + if set --query conflict_files[1] && set --erase install_plugins[$index] + echo -s "fisher: Cannot install \"$plugin\": please remove or move conflicting files first:" \n" "$conflict_files >&2 + continue + end + end + + for file in (string replace -- $source/ "" $files) + command cp -RLf $source/$file $fisher_path/$file + end + + set --local plugin_files_var _fisher_(string escape --style=var -- $plugin)_files + + set --query files[1] && set --universal $plugin_files_var (string replace -- $source $fisher_path $files | string replace -- ~ \~) + + contains -- $plugin $_fisher_plugins || set --universal --append _fisher_plugins $plugin + contains -- $plugin $install_plugins && set --local event install || set --local event update + + printf "%s\n" Installing\ (set_color --bold)$plugin(set_color normal) " "$$plugin_files_var | string replace -- \~ ~ + + for file in (string match --regex -- '.+/[^/]+\.fish$' $$plugin_files_var | string replace -- \~ ~) + source $file + if set --local name (string replace --regex -- '.+conf\.d/([^/]+)\.fish$' '$1' $file) + emit {$name}_$event + end + end + end + + command rm -rf $source_plugins + + if set --query _fisher_plugins[1] + set --local commit_plugins + + for plugin in $file_plugins + contains -- (string lower -- $plugin) (string lower -- $_fisher_plugins) && set --append commit_plugins $plugin + end + + for plugin in $_fisher_plugins + contains -- (string lower -- $plugin) (string lower -- $commit_plugins) || set --append commit_plugins $plugin + end + + string replace --regex -- $HOME \~ $commit_plugins >$fish_plugins + else + set --erase _fisher_plugins + command rm -f $fish_plugins + end + + set --local total (count $install_plugins) (count $update_plugins) (count $remove_plugins) + + test "$total" != "0 0 0" && echo (string join ", " ( + test $total[1] = 0 || echo "Installed $total[1]") ( + test $total[2] = 0 || echo "Updated $total[2]") ( + test $total[3] = 0 || echo "Removed $total[3]") + ) plugin/s + case \* + echo "fisher: Unknown command: \"$cmd\"" >&2 && return 1 + end +end + +if ! set --query _fisher_upgraded_to_4_4 + set --universal _fisher_upgraded_to_4_4 + if functions --query _fisher_list + set --query XDG_DATA_HOME[1] || set --local XDG_DATA_HOME ~/.local/share + command rm -rf $XDG_DATA_HOME/fisher + functions --erase _fisher_{list,plugin_parse} + fisher update >/dev/null 2>/dev/null + else + for var in (set --names | string match --entire --regex '^_fisher_.+_files$') + set $var (string replace -- ~ \~ $$var) + end + functions --erase _fisher_fish_postexec + end +end diff --git a/functions/fzf_configure_bindings.fish b/functions/fzf_configure_bindings.fish new file mode 100644 index 0000000..e1a4a0a --- /dev/null +++ b/functions/fzf_configure_bindings.fish @@ -0,0 +1,49 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Always installs bindings for insert and default mode for simplicity and b/c it has almost no side-effect +# https://gitter.im/fish-shell/fish-shell?at=60a55915ee77a74d685fa6b1 +function fzf_configure_bindings --description "Installs the default key bindings for fzf.fish with user overrides passed as options." + # no need to install bindings if not in interactive mode or running tests + status is-interactive || test "$CI" = true; or return + + set -f options_spec h/help 'directory=?' 'git_log=?' 'git_status=?' 'history=?' 'processes=?' 'variables=?' + argparse --max-args=0 --ignore-unknown $options_spec -- $argv 2>/dev/null + if test $status -ne 0 + echo "Invalid option or a positional argument was provided." >&2 + _fzf_configure_bindings_help + return 22 + else if set --query _flag_help + _fzf_configure_bindings_help + return + else + # Initialize with default key sequences and then override or disable them based on flags + # index 1 = directory, 2 = git_log, 3 = git_status, 4 = history, 5 = processes, 6 = variables + set -f key_sequences ctrl-alt-f ctrl-alt-l ctrl-alt-s ctrl-r ctrl-alt-p ctrl-v + set --query _flag_directory && set key_sequences[1] "$_flag_directory" + set --query _flag_git_log && set key_sequences[2] "$_flag_git_log" + set --query _flag_git_status && set key_sequences[3] "$_flag_git_status" + set --query _flag_history && set key_sequences[4] "$_flag_history" + set --query _flag_processes && set key_sequences[5] "$_flag_processes" + set --query _flag_variables && set key_sequences[6] "$_flag_variables" + + # If fzf bindings already exists, uninstall it first for a clean slate + if functions --query _fzf_uninstall_bindings + _fzf_uninstall_bindings + end + + for mode in default insert + test -n $key_sequences[1] && bind --mode $mode $key_sequences[1] _fzf_search_directory + test -n $key_sequences[2] && bind --mode $mode $key_sequences[2] _fzf_search_git_log + test -n $key_sequences[3] && bind --mode $mode $key_sequences[3] _fzf_search_git_status + test -n $key_sequences[4] && bind --mode $mode $key_sequences[4] _fzf_search_history + test -n $key_sequences[5] && bind --mode $mode $key_sequences[5] _fzf_search_processes + test -n $key_sequences[6] && bind --mode $mode $key_sequences[6] "$_fzf_search_vars_command" + end + + function _fzf_uninstall_bindings --inherit-variable key_sequences + bind --erase -- $key_sequences + bind --erase --mode insert -- $key_sequences + end + end +end diff --git a/functions/gemini-resume.fish b/functions/gemini-resume.fish new file mode 100644 index 0000000..ec37be8 --- /dev/null +++ b/functions/gemini-resume.fish @@ -0,0 +1,13 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function gemini-resume + if test -f .gemini_session + set -l sid (cat .gemini_session) + # Use --resume (or -r) to jump back in + gemini --resume $sid + else + # Fallback to the interactive session browser + gemini --resume + end +end diff --git a/functions/get-color.fish b/functions/get-color.fish new file mode 100644 index 0000000..da25cc9 --- /dev/null +++ b/functions/get-color.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function get-color --wraps='/home/rootiest/scripts/hex2rgb.sh "$(wl-colorpicker-plasma)"' --description 'alias get-color=/home/rootiest/scripts/hex2rgb.sh "$(wl-colorpicker-plasma)"' + /home/rootiest/scripts/hex2rgb.sh "$(wl-colorpicker-plasma)" $argv + +end diff --git a/functions/gip.fish b/functions/gip.fish new file mode 100644 index 0000000..0fdb8b9 --- /dev/null +++ b/functions/gip.fish @@ -0,0 +1,9 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function gip -d "Show all public IP addresses" + echo -n "IPv4: " + curl -4 -s --max-time 2 https://icanhazip.com || echo "Not detected" + echo -n "IPv6: " + curl -6 -s --max-time 2 https://icanhazip.com || echo "Not detected" +end diff --git a/functions/gip4.fish b/functions/gip4.fish new file mode 100644 index 0000000..3a0dfbc --- /dev/null +++ b/functions/gip4.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function gip4 -d "Get public IPv4 address" + curl -4 -s https://icanhazip.com +end diff --git a/functions/gip6.fish b/functions/gip6.fish new file mode 100644 index 0000000..480968a --- /dev/null +++ b/functions/gip6.fish @@ -0,0 +1,14 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function gip6 -d "Get public IPv6 address" + # Use -6 to force IPv6 and --fail to catch network errors + set -l ip (curl -6 -s --fail https://icanhazip.com 2>/dev/null) + + if test $status -eq 0 + echo $ip + else + echo "❌ IPv6 is currently unavailable or not supported on this network." + return 1 + end +end diff --git a/functions/git-clean.fish b/functions/git-clean.fish new file mode 100644 index 0000000..3bc901a --- /dev/null +++ b/functions/git-clean.fish @@ -0,0 +1,56 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function git-clean --description 'Sync main, prune remotes, and delete orphaned branches' + set -l options h/help f/force + argparse $options -- $argv + or return + + if set -q _flag_help + echo (set_color --bold blue)"Usage: "(set_color normal)"git-clean [OPTIONS]" + echo + echo "Steps taken:" + echo " 1. Fetches and prunes to find deleted remote branches." + echo " 2. Switches to main if you are on an orphaned branch." + echo " 3. Pulls the latest changes from the remote." + echo " 4. Deletes local orphaned branches." + return 0 + end + + # 1. Fetch and prune (Quietly) + echo (set_color blue)"Fetching and pruning remote tracking..."(set_color normal) + git fetch --prune --quiet + + # 2. Identify orphaned branches and current branch + set -l gone_branches (git branch -vv | awk '/: gone\]/ {gsub(/\*/, ""); print $1}') + set -l current_branch (git branch --show-current) + + if test -n "$gone_branches" + # 3. Move to safety if needed (Quietly) + if contains "$current_branch" $gone_branches + echo (set_color yellow)"Current branch '$current_branch' was deleted on remote. Moving to main..."(set_color normal) + # Redirecting output to /dev/null to hide the 'Switched to branch' message + git checkout main >/dev/null 2>&1; or git checkout master >/dev/null 2>&1 + end + end + + # 4. Update the current branch (Quietly) + echo (set_color blue)"Updating current branch..."(set_color normal) + git pull --quiet + + # 5. Final cleanup + if test -n "$gone_branches" + set -l delete_flag -d + if set -q _flag_force + set -l delete_flag -D + end + + echo (set_color red)"Deleting orphaned local branches ($delete_flag):"(set_color normal) + for branch in $gone_branches + # We keep this output so you can see confirmation of which hashes were deleted + git branch $delete_flag $branch + end + else + echo (set_color green)"Everything is tidy. No orphaned branches found."(set_color normal) + end +end diff --git a/functions/gitui.fish b/functions/gitui.fish new file mode 100644 index 0000000..60ce31d --- /dev/null +++ b/functions/gitui.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function gitui --description 'alias gitui=gitui -t mocha.ron' + command gitui -t frappe.ron $argv + +end diff --git a/functions/hist.fish b/functions/hist.fish new file mode 100644 index 0000000..be80483 --- /dev/null +++ b/functions/hist.fish @@ -0,0 +1,14 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function hist -d "Search fish history and put it in the prompt" + set -l selected (history | fzf --reverse --height 40% --with-nth 3..) + + if test -n "$selected" + # Strip the timestamp for the final output + set -l command (echo $selected | string replace -r '^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} ' '') + + echo $command | wl-copy 2>/dev/null + commandline -r $command + end +end diff --git a/functions/l.fish b/functions/l.fish new file mode 100644 index 0000000..b2dabf2 --- /dev/null +++ b/functions/l.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function l --wraps='lsd --almost-all --long' --description 'alias l=lsd --almost-all --long' + if which lsd >/dev/null 2>&1 + lsd --almost-all --long --git --header --hyperlink=auto $argv + else + command ls --color=auto --almost-all -l $argv + end +end diff --git a/functions/lS.fish b/functions/lS.fish new file mode 100644 index 0000000..46acb2a --- /dev/null +++ b/functions/lS.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function lS --wraps='lsd --oneline --classic' --description 'alias lS=lsd --oneline --classic' + if which lsd >/dev/null 2>&1 + lsd --oneline --classic $argv + else + command ls $argv + end +end diff --git a/functions/less.fish b/functions/less.fish new file mode 100644 index 0000000..8815457 --- /dev/null +++ b/functions/less.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function less --wraps=most --description 'alias less=most' + if which most >/dev/null 2>&1 + most $argv + else + command less $argv + end +end diff --git a/functions/limine-edit.fish b/functions/limine-edit.fish new file mode 100644 index 0000000..804b780 --- /dev/null +++ b/functions/limine-edit.fish @@ -0,0 +1,22 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function limine-edit --description 'Safely edit and re-verify Limine configuration' + # 1. Open the config with sudoedit + sudoedit /boot/limine.conf + + # 2. Re-enroll the config hash (This prevents the Checksum Panic) + echo "Enrolling Limine config..." + sudo limine-enroll-config + + # 3. Run the CachyOS boot hooks (Updates snapshots/kernel links) + echo "Running CachyOS boot hooks..." + sudo limine-mkinitcpio + + # 4. Sign any unsigned files tracked by sbctl + # 'sbctl sign-all' will re-apply signatures to everything in the database + echo "Verifying Secure Boot signatures..." + sudo sbctl sign-all + + echo "✅ Limine config updated and verified. Ready for reboot." +end diff --git a/functions/llm.fish b/functions/llm.fish new file mode 100644 index 0000000..0dbcce3 --- /dev/null +++ b/functions/llm.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function llm --wraps='lsd --timesort --long' --description 'alias llm=lsd --timesort --long' + if which lsd >/dev/null 2>&1 + lsd --timesort --long --git --header --hyperlink=auto $argv + else + command ls color=auto -l $argv + end +end diff --git a/functions/lock.fish b/functions/lock.fish new file mode 100644 index 0000000..e0577d1 --- /dev/null +++ b/functions/lock.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function lock + loginctl lock-session +end diff --git a/functions/ls.fish b/functions/ls.fish new file mode 100644 index 0000000..e00f49c --- /dev/null +++ b/functions/ls.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function ls --wraps=lsd --wraps='lsd --hyperlink=auto' --description 'alias ls=lsd' + if which lsd >/dev/null 2>&1 + lsd --hyperlink=auto $argv + else + command ls --color=auto $argv + end +end diff --git a/functions/lstree.fish b/functions/lstree.fish new file mode 100644 index 0000000..417378e --- /dev/null +++ b/functions/lstree.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function lstree --wraps='ls --tree' --description 'alias lstree=ls --tree' + if which lsd >/dev/null 2>&1 + lsd --tree --hyperlink=auto $argv + else + command ls --color=auto -R $argv + end +end diff --git a/functions/lt.fish b/functions/lt.fish new file mode 100644 index 0000000..da7b0b9 --- /dev/null +++ b/functions/lt.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function lt --wraps='lsd --tree --depth=2' --description 'alias lt=lsd --tree --depth=2' + if which lsdq >/dev/null 2>&1 + lsd --tree --depth=2 --hyperlink=auto $argv + else + command ls --color=auto -R $argv + end +end diff --git a/functions/ltr.fish b/functions/ltr.fish new file mode 100644 index 0000000..977de61 --- /dev/null +++ b/functions/ltr.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function ltr --wraps='lsd -ltr' --description 'alias ltr=lsd -ltr' + lsd -ltr $argv +end diff --git a/functions/mkdir.fish b/functions/mkdir.fish new file mode 100644 index 0000000..9c3a659 --- /dev/null +++ b/functions/mkdir.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function mkdir + if status is-interactive + command mkdir -p $argv + else + command mkdir $argv + end +end diff --git a/functions/monitors.fish b/functions/monitors.fish new file mode 100644 index 0000000..52bb6a6 --- /dev/null +++ b/functions/monitors.fish @@ -0,0 +1,29 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function monitors + # 0. Capture the ID of the pane we are starting in (Top-Left) + set local_id $KITTY_WINDOW_ID + + # Define remote commands + set racknerd_cmd "ssh -t racknerd btop" + set server_cmd "ssh -t rootiest-server btop" + set mini_cmd "ssh -t racknerd-mini btop" + + # 1. Split vertically from Top-Left to create Top-Right (RackNerd) + set rack_id (kitty @ launch --location=vsplit --cwd=current sh -c "$racknerd_cmd") + + # 2. Return focus to Top-Left and split horizontally to create Bottom-Left (Server) + kitty @ focus-window --match "id:$local_id" + set server_id (kitty @ launch --location=hsplit --cwd=current sh -c "$server_cmd") + + # 3. Focus Top-Right (RackNerd) and split horizontally to create Bottom-Right (Mini) + kitty @ focus-window --match "id:$rack_id" + set mini_id (kitty @ launch --location=hsplit --cwd=current sh -c "$mini_cmd") + + # 4. Final focus back to the Top-Left to launch local btop + kitty @ focus-window --match "id:$local_id" + + # Run local btop + btop +end diff --git a/functions/nlazyup.fish b/functions/nlazyup.fish new file mode 100644 index 0000000..850ab30 --- /dev/null +++ b/functions/nlazyup.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function nlazyup --wraps='nvim --headless "+Lazy! sync" +qa' --description 'alias nlazyup=nvim --headless "+Lazy! sync" +qa' + nvim --headless "+Lazy! sync" +qa $argv + +end diff --git a/functions/nnotes.fish b/functions/nnotes.fish new file mode 100644 index 0000000..f5da56e --- /dev/null +++ b/functions/nnotes.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function nnotes --wraps='cd~/vaults/Rootiest Notes/;nvim' --wraps='cd ~/vaults/Rootiest Notes/;nvim' --wraps='cd ~/vaults/Rootiest\\ Notes/;nvim' --description 'alias nnotes=cd ~/vaults/Rootiest\\ Notes/;nvim' + cd ~/vaults/Rootiest\ Notes/;nvim $argv + +end diff --git a/functions/nvimup.fish b/functions/nvimup.fish new file mode 100644 index 0000000..1a5c020 --- /dev/null +++ b/functions/nvimup.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function nvimup --wraps='nvim "+UpdateNeovim"' --wraps='NVIMUPDATER_HEADLESS=1 nvim "+UpdateNeovim"' --description 'alias nvimup=NVIMUPDATER_HEADLESS=1 nvim "+UpdateNeovim"' + NVIMUPDATER_HEADLESS=1 nvim "+UpdateNeovim" $argv + +end diff --git a/functions/p.fish b/functions/p.fish new file mode 100644 index 0000000..c931074 --- /dev/null +++ b/functions/p.fish @@ -0,0 +1,33 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function p --description 'Put from clipboard' + # Check for help flag + if contains -- -h $argv; or contains -- --help $argv + echo "Usage: p [OPTIONS]" + echo "" + echo "Description:" + echo " Pastes content from the system clipboard to stdout." + echo "" + echo "Examples:" + echo " p Print clipboard content" + echo " p > file.txt Save clipboard to a file" + echo " p | grep 'foo' Pipe clipboard content to another command" + echo " cat (p) Use clipboard content as a filename for cat" + return 0 + end + + # Determine the clipboard provider + set -l paste_cmd + if type -q wl-paste + set paste_cmd wl-paste + else if type -q xclip + set paste_cmd xclip -selection clipboard -o + else + echo "Error: No clipboard provider (wl-paste or xclip) found." >&2 + return 1 + end + + # Execute the paste command with any provided arguments + $paste_cmd $argv +end diff --git a/functions/parur.fish b/functions/parur.fish new file mode 100644 index 0000000..5a3baad --- /dev/null +++ b/functions/parur.fish @@ -0,0 +1,18 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function parur --description 'Interactively search and remove an installed package using fzf' + # 1. Use command substitution to get the package list from fzf + set -l pkg_list ( + pacman -Qqs \ + | fzf --preview 'pacman -Qi {}' --multi + ) + + # 2. Check if a package was selected. + if test (count $pkg_list) -gt 0 + # 3. Pass the selected packages directly to paru -R + paru -R $pkg_list + else + echo "No packages selected for removal." + end +end diff --git a/functions/paste.fish b/functions/paste.fish new file mode 100644 index 0000000..e22ea80 --- /dev/null +++ b/functions/paste.fish @@ -0,0 +1,13 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function paste --description 'Paste from clipboard' + if type -q wl-paste + wl-paste $argv + else if type -q xclip + xclip -selection clipboard -o $argv + else + echo "Error: Neither wl-paste nor xclip found." >&2 + return 1 + end +end diff --git a/functions/ping.fish b/functions/ping.fish new file mode 100644 index 0000000..d4be869 --- /dev/null +++ b/functions/ping.fish @@ -0,0 +1,16 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function ping --description 'prettyping with default nolegend' + if command -q prettyping + # Check if the user specifically asked for the legend + if contains -- --legend $argv + command prettyping $argv + else + command prettyping --nolegend $argv + end + else + # Fallback to standard ping if prettyping isn't installed + command ping $argv + end +end diff --git a/functions/pkg.fish b/functions/pkg.fish new file mode 100644 index 0000000..a0619bd --- /dev/null +++ b/functions/pkg.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Function for installing packages with paru. +# This runs `paru` with the `-S` flag to install one or more packages. +# The `$argv` variable passes all arguments given to the `pkg` function +# directly to the `paru` command. +function pkg + paru -S $argv +end diff --git a/functions/ports.fish b/functions/ports.fish new file mode 100644 index 0000000..8efb8c9 --- /dev/null +++ b/functions/ports.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function ports -d "Show active network listeners" + sudo lsof -iTCP -sTCP:LISTEN -P -n +end diff --git a/functions/push-vim.fish b/functions/push-vim.fish new file mode 100644 index 0000000..d2bc968 --- /dev/null +++ b/functions/push-vim.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function push-vim --wraps=/home/rootiest/projects/propogate-vim/send.sh --description 'alias push-vim=/home/rootiest/projects/propogate-vim/send.sh' + /home/rootiest/projects/propogate-vim/send.sh $argv + +end diff --git a/functions/qr.fish b/functions/qr.fish new file mode 100644 index 0000000..9959d10 --- /dev/null +++ b/functions/qr.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function qr -d "Generate a QR code from text or pipe" + if set -q argv[1] + echo $argv | qrencode -t utf8 + else + cat | qrencode -t utf8 + end +end diff --git a/functions/rawfish.fish b/functions/rawfish.fish new file mode 100644 index 0000000..bb227cb --- /dev/null +++ b/functions/rawfish.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function rawfish --wraps='env NO_TMUX=1 fish' --description 'alias rawfish=env NO_TMUX=1 fish' + env NO_TMUX=1 fish $argv + +end diff --git a/functions/replay.fish b/functions/replay.fish new file mode 100644 index 0000000..412ca4d --- /dev/null +++ b/functions/replay.fish @@ -0,0 +1,51 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function replay --description "Run Bash commands replaying changes in Fish" + switch "$argv" + case -v --version + echo "replay, version 1.2.1" + case "" -h --help + echo "Usage: replay Run Bash commands replaying changes in Fish" + echo "Options:" + echo " -v or --version Print version" + echo " -h or --help Print this help message" + case \* + set --local env + set --local sep @$fish_pid(random)(command date +%s) + set --local argv $argv[1] (string escape -- $argv[2..-1]) + set --local out (command bash -c " + $argv + status=\$? + [ \$status -gt 0 ] && exit \$status + + command compgen -e | command awk -v sep=$sep '{ + gsub(/\n/, \"\\\n\", ENVIRON[\$0]) + print \$0 sep ENVIRON[\$0] + }' && alias + ") || return + + string replace --all -- \\n \n ( + for line in $out + if string split -- $sep $line | read --local --line name value + set --append env $name + + contains -- $name SHLVL PS1 BASH_FUNC || test "$$name" = "$value" && continue + + if test "$name" = PATH + echo set PATH (string split -- : $value | string replace --regex --all -- '(^.*$)' '"$1"') + else if test "$name" = PWD + echo builtin cd "\"$value\"" + else + echo "set --global --export $name "(string escape -- $value) + end + else + set --query env[1] && string match --entire --regex -- "^alias" $line || echo "echo \"$line\"" + end + end | string replace --all -- \$ \\\$ + for name in (set --export --names) + contains -- $name $env || echo "set --erase $name" + end + ) | source + end +end diff --git a/functions/rg.fish b/functions/rg.fish new file mode 100644 index 0000000..0d89bf9 --- /dev/null +++ b/functions/rg.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function rg --description 'alias rg=rg --hyperlink-format=kitty' + command rg --hyperlink-format=kitty $argv + +end diff --git a/functions/rm.fish b/functions/rm.fish new file mode 100644 index 0000000..8b12ea3 --- /dev/null +++ b/functions/rm.fish @@ -0,0 +1,79 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function rm --description 'Ultimate rm: trash, list, empty, and secure-erase' + # 1. No arguments: Show the Trash contents (Quick view) + if not set -q argv[1] + if type -q trash + echo "🗑️ Current Trash Contents:" + trash list + else + command rm + end + return + end + + # 2. Flag: Empty the Trash (-e / --empty) + # We check if the FIRST argument is -e or --empty + if string match -rq -- '^-e$|^--empty$' -- $argv[1] + if type -q trash + # Check if there are arguments after -e (like --within 2weeks) + if set -q argv[2] + # Pass everything after the first argument to trash empty + trash empty $argv[2..-1] + else + # Default behavior if no extra args provided + echo "🧹 Emptying all trash..." + trash empty --all + end + else + echo "Error: 'trash' command not found." + end + return + end + + # 3. Flag: Secure Delete (-S / --secure) + if contains -- -S $argv; or contains -- --secure $argv + set -l clean_args + for arg in $argv + if not string match -rq -- '^-S$|^--secure$|^-r$|^-R$|^--recursive$' -- "$arg" + set -a clean_args $arg + end + end + echo "🛡️ Permanent delete + SSD Zeroing..." + command rm -rf $clean_args + fstrim --all 2>/dev/null & + return + end + + # 4. Logic: Use 'trash' for simple paths OR recursive flags (-r, -R) + # Bail to real rm if ANY other flags (like -f) are detected + set -l is_safe_for_trash true + for arg in $argv + if string match -q -- "-*" $arg + if not string match -rq -- '^-r$|^-R$|^--recursive$' -- "$arg" + set is_safe_for_trash false + break + end + end + end + + if test "$is_safe_for_trash" = true + set -l trash_paths + for arg in $argv + if not string match -rq -- '^-r$|^-R$|^--recursive$' -- "$arg" + set -a trash_paths $arg + end + end + + if type -q trash; and set -q trash_paths[1] + trash put $trash_paths + # Refresh Dolphin view + dbus-send --type=signal /OrgKdeKDirNotify org.kde.KDirNotify.FilesChanged stringArray:"trash:/" >/dev/null 2>&1 & + return + end + end + + # 5. Fallback: Standard rm for everything else (including -rf) + command rm $argv +end diff --git a/functions/save_claude_session.fish b/functions/save_claude_session.fish new file mode 100755 index 0000000..0ff43dc --- /dev/null +++ b/functions/save_claude_session.fish @@ -0,0 +1,31 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +#!/usr/bin/env fish + +# 1. Read input and extract session ID +set -l input (cat) +set -l sid (echo $input | python3 -c "import sys,json; print(json.load(sys.stdin).get('session_id', ''))") +set -l session_file ".claude_session" + +if test -n "$sid" + # 2. Save the session ID locally + echo "$sid" >$session_file + + # 3. Smart .gitignore check + # We check if git even knows about this file. + # If 'git check-ignore' returns 1, it means the file is NOT ignored. + if git rev-parse --is-inside-work-tree >/dev/null 2>&1 + if not git check-ignore -q $session_file + # Append the filename to .gitignore + echo -e "\n# AI Session IDs\n$session_file" >>.gitignore + echo "Added $session_file to .gitignore" + end + end + + # 4. Update universal variable + set -U LAST_CLAUDE_SESSION "$sid" +end + +# MANDATORY: Every hook must output valid JSON +echo '{}' diff --git a/functions/save_gemini_session.fish b/functions/save_gemini_session.fish new file mode 100755 index 0000000..843a149 --- /dev/null +++ b/functions/save_gemini_session.fish @@ -0,0 +1,32 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +#!/usr/bin/env fish + +# 1. Read the JSON from stdin +set -l input (cat) + +# 2. Extract session_id using Python +set -l sid (echo $input | python3 -c "import sys,json; print(json.load(sys.stdin).get('session_id', ''))") +set -l session_file ".gemini_session" + +if test -n "$sid" + # 3. Save the session ID locally + echo "$sid" >$session_file + + # 4. Smart .gitignore check + # We only attempt this if we are inside a Git repository + if git rev-parse --is-inside-work-tree >/dev/null 2>&1 + # If 'git check-ignore' fails, it means the file is NOT currently ignored + if not git check-ignore -q $session_file + # Append a labeled entry to .gitignore + echo -e "\n# AI Session IDs\n$session_file" >>.gitignore + end + end + + # 5. Update universal variable for cross-terminal access + set -U LAST_GEMINI_SESSION "$sid" +end + +# MANDATORY: Every hook must output valid JSON or an empty object +echo '{}' diff --git a/functions/sbver.fish b/functions/sbver.fish new file mode 100644 index 0000000..3e5699a --- /dev/null +++ b/functions/sbver.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function sbver --wraps='~/sbctl-verify-clean.sh' --description 'alias sbver=~/sbctl-verify-clean.sh' + ~/sbctl-verify-clean.sh $argv + +end diff --git a/functions/screensleep.fish b/functions/screensleep.fish new file mode 100644 index 0000000..f67dada --- /dev/null +++ b/functions/screensleep.fish @@ -0,0 +1,12 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function screensleep --description 'Turn off the display using KDE PowerDevil' + # Optional: 1-second delay to ensure no keystrokes wake it immediately + sleep 1 + busctl --user call \ + org.kde.kglobalaccel \ + /component/org_kde_powerdevil \ + org.kde.kglobalaccel.Component \ + invokeShortcut s "Turn Off Screen" +end diff --git a/functions/search.fish b/functions/search.fish new file mode 100644 index 0000000..2402cba --- /dev/null +++ b/functions/search.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Function for searching for packages to install with paru. +# This runs `paru` with the search flags. +# The `$argv` variable passes all arguments given to the `search` function +# directly to the `paru` command. +function search + paru $argv +end diff --git a/functions/spark.fish b/functions/spark.fish new file mode 100644 index 0000000..25b0dc7 --- /dev/null +++ b/functions/spark.fish @@ -0,0 +1,36 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function spark --description Sparklines + argparse --ignore-unknown --name=spark v/version h/help m/min= M/max= -- $argv || return + + if set --query _flag_version[1] + echo "spark, version 1.1.0" + else if set --query _flag_help[1] + echo "Usage: spark " + echo " stdin | spark" + echo "Options:" + echo " --min= Minimum range" + echo " --max= Maximum range" + echo " -v or --version Print version" + echo " -h or --help Print this help message" + echo "Examples:" + echo " spark 1 1 2 5 14 42" + echo " seq 64 | sort --random-sort | spark" + else if set --query argv[1] + printf "%s\n" $argv | spark --min="$_flag_min" --max="$_flag_max" + else + command awk -v min="$_flag_min" -v max="$_flag_max" ' + { + m = min == "" ? m == "" ? $0 : m > $0 ? $0 : m : min + M = max == "" ? M == "" ? $0 : M < $0 ? $0 : M : max + nums[NR] = $0 + } + END { + n = split("▁ ▂ ▃ ▄ ▅ ▆ ▇ █", sparks, " ") - 1 + while (++i <= NR) + printf("%s", sparks[(M == m) ? 3 : sprintf("%.f", (1 + (nums[i] - m) * n / (M - m)))]) + } + ' && echo + end +end diff --git a/functions/split.fish b/functions/split.fish new file mode 100644 index 0000000..29f0283 --- /dev/null +++ b/functions/split.fish @@ -0,0 +1,22 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function split --description 'Run a command in a new Kitty split' + set -l location hsplit + + switch $argv[1] + case -h --horizontal + set location hsplit + set -e argv[1] + case -v --vertical + set location vsplit + set -e argv[1] + end + + if test (count $argv) -gt 0 + # Set HIDE_GREETING=1 before fish starts + kitty @ launch --location=$location --cwd=$PWD env HIDE_GREETING=1 fish -c "$argv; exec fish" + else + kitty @ launch --location=$location --cwd=$PWD env HIDE_GREETING=1 fish -c "exec fish" + end +end diff --git a/functions/spwin.fish b/functions/spwin.fish new file mode 100644 index 0000000..fe1ecd9 --- /dev/null +++ b/functions/spwin.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function spwin --wraps='~/.config/kitty/spawn-window.sh' --description 'alias spwin=~/.config/kitty/spawn-window.sh' + ~/.config/kitty/spawn-window.sh $argv +end diff --git a/functions/ssh.fish b/functions/ssh.fish new file mode 100644 index 0000000..1fe69a6 --- /dev/null +++ b/functions/ssh.fish @@ -0,0 +1,10 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function ssh --description 'Alias ssh to kitten ssh when using Kitty terminal' + if test "$TERM" = xterm-kitty + kitten ssh $argv + else + command ssh $argv + end +end diff --git a/functions/steam-dl.fish b/functions/steam-dl.fish new file mode 100644 index 0000000..ca58407 --- /dev/null +++ b/functions/steam-dl.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function steam-dl --description 'Run Steam while inhibiting system sleep' + echo "Inhibiting sleep while Steam downloads..." + systemd-inhibit --why="Active Download" --who="User" --what=idle:sleep steam +end diff --git a/functions/superpowers.fish b/functions/superpowers.fish new file mode 100644 index 0000000..85b209f --- /dev/null +++ b/functions/superpowers.fish @@ -0,0 +1,49 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function superpowers --description "Toggle superpowers extension for Gemini and Claude" + set -l scope_gemini workspace + set -l scope_claude project + set -l mode "" + set -l help_text " +Usage: superpowers [on|off] [options] + +Commands: + on Enable superpowers for Gemini and Claude + off Disable superpowers for Gemini and Claude + +Options: + -g, --global Apply settings to the user/global scope + -h, --help Show this help message +" + + # Parse arguments + for arg in $argv + switch $arg + case on + set mode enable + case off + set mode disable + case -g --global + set scope_gemini user + set scope_claude user + case -h --help + echo $help_text + return 0 + end + end + + # Handle no arguments or invalid mode + if test -z "$mode" + echo $help_text + return 1 + end + + echo "Setting superpowers to: $mode (Scope: Gemini=$scope_gemini, Claude=$scope_claude)..." + + # Execute Gemini command + gemini extensions $mode superpowers --scope $scope_gemini + + # Execute Claude command + claude plugins $mode superpowers --scope $scope_claude +end diff --git a/functions/swapstat.fish b/functions/swapstat.fish new file mode 100644 index 0000000..e134434 --- /dev/null +++ b/functions/swapstat.fish @@ -0,0 +1,37 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function swapstat --description "View colorized zRAM and swappiness status" + set -l swappiness (sysctl -n vm.swappiness) + set -l zdata (zramctl --bytes --noheadings --output DATA,TOTAL /dev/zram0 2>/dev/null) + + echo (set_color --bold blue)"── Memory & zRAM Report ──"(set_color normal) + + # Kernel & Compression Stats + if test -n "$zdata" + set -l raw (echo $zdata | awk '{print $1}') + set -l compressed (echo $zdata | awk '{print $2}') + if test "$compressed" -gt 0 + set -l ratio (math -s2 "$raw / $compressed") + echo (set_color yellow)"Compression Ratio: "(set_color normal)"$ratio:1" + end + end + echo (set_color yellow)"Kernel Swappiness: "(set_color normal)"$swappiness" + echo "" + + # Colorized zRAM Table + # Colors the header green and the device path (/dev/...) cyan + echo (set_color --bold --underline magenta)"zRAM Device Details"(set_color normal) + zramctl --output NAME,ALGORITHM,DISKSIZE,DATA,COMPR,TOTAL,STREAMS | sed \ + -e "1s/.*/$(set_color --bold green)&$(set_color normal)/" \ + -e "s|/dev/zram[0-9]|$(set_color cyan)&$(set_color normal)|" + + echo "" + + # Colorized Swapon Table + # Colors the header green and the device path cyan + echo (set_color --bold --underline magenta)"Active Swap Priority"(set_color normal) + swapon --show | sed \ + -e "1s/.*/$(set_color --bold green)&$(set_color normal)/" \ + -e "s|/dev/[^ ]*|$(set_color cyan)&$(set_color normal)|" +end diff --git a/functions/tab.fish b/functions/tab.fish new file mode 100644 index 0000000..2fdac95 --- /dev/null +++ b/functions/tab.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function tab --wraps='konsole --new-tab --workdir "$cdto"' --description 'alias tab=konsole --new-tab --workdir "$cdto"' + konsole --new-tab --workdir "$cdto" $argv + +end diff --git a/functions/tailart.fish b/functions/tailart.fish new file mode 100644 index 0000000..376a3ee --- /dev/null +++ b/functions/tailart.fish @@ -0,0 +1,6 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function tailart --wraps='tailscale file get ~/Pictures/Art/' --description 'alias tailart=tailscale file get ~/Pictures/Art/' + tailscale file get ~/Pictures/Art/ $argv +end diff --git a/functions/tmux-clean.fish b/functions/tmux-clean.fish new file mode 100644 index 0000000..80f2e3a --- /dev/null +++ b/functions/tmux-clean.fish @@ -0,0 +1,17 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function tmux-clean --description 'Kill all tmux sessions except the current one' + # Get a list of all session names that are NOT currently attached + set sessions (tmux list-sessions -F '#{session_name} #{session_attached}' | string match -rv ' 1$' | string split -f1 ' ') + + if test -n "$sessions" + for session in $sessions + echo "Stopping session: $session" + tmux kill-session -t "$session" + end + echo "Clean-up complete." + else + echo "No detached sessions to clean." + end +end diff --git a/functions/top.fish b/functions/top.fish new file mode 100644 index 0000000..d9e15c4 --- /dev/null +++ b/functions/top.fish @@ -0,0 +1,14 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function top --wraps=btop --description 'Use btop as a modern replacement for top' + # 1. Check if btop is actually installed + if type -q btop + # 2. Launch btop with any arguments passed + btop $argv + else + # 3. Fallback to the original system top if btop is missing + echo "btop not found, falling back to classic top..." + command top $argv + end +end diff --git a/functions/upgrade.fish b/functions/upgrade.fish new file mode 100644 index 0000000..153e1a6 --- /dev/null +++ b/functions/upgrade.fish @@ -0,0 +1,9 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +# Function for upgrading the system with paru. +# This runs `paru` with the `-Syu` flags to sync, refresh, and upgrade all +# packages, and adds `--no-confirm` to bypass the confirmation prompt. +function upgrade + paru -Syu --noconfirm +end diff --git a/functions/view.fish b/functions/view.fish new file mode 100644 index 0000000..3d6f051 --- /dev/null +++ b/functions/view.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function view --wraps='neovim -R' --wraps='nvim -R' --description 'alias view=nvim -R' + nvim -R $argv + +end diff --git a/functions/wake-lock.fish b/functions/wake-lock.fish new file mode 100644 index 0000000..ad44235 --- /dev/null +++ b/functions/wake-lock.fish @@ -0,0 +1,15 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function wake-lock --description 'Run a command while inhibiting system sleep' + if test (count $argv) -eq 0 + echo "Usage: wake-lock [command] [args...]" + return 1 + end + + echo "Running '$argv' with sleep inhibition active..." + + # --what=idle:sleep prevents the system from auto-sleeping or being suspended + # --who identifies your function in 'systemd-inhibit --list' + systemd-inhibit --why="Manual task inhibition" --who="wake-lock" --what=idle:sleep $argv +end diff --git a/functions/y.fish b/functions/y.fish new file mode 100644 index 0000000..70140bd --- /dev/null +++ b/functions/y.fish @@ -0,0 +1,36 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function y --description 'Yank to clipboard' + # Check for help flag + if contains -- -h $argv; or contains -- --help $argv + echo "Usage: y [TEXT] or [COMMAND] | y" + echo "" + echo "Examples:" + echo " y \"hello world\" Copy a string directly" + echo " ls | y Copy output of a command" + echo " y < file.txt Copy contents of a file" + echo " cat file.txt | y Another way to copy a file" + return 0 + end + + # Determine the clipboard provider + set -l copy_cmd + if type -q wl-copy + set copy_cmd wl-copy + else if type -q xclip + set copy_cmd xclip -selection clipboard + else + echo "Error: No clipboard provider (wl-copy or xclip) found." >&2 + return 1 + end + + # Handle input + if set -q argv[1] + # If arguments are provided, echo them to the clipboard + echo $argv | eval $copy_cmd + else + # If no arguments, read from stdin (pipes/redirects) + eval $copy_cmd + end +end diff --git a/functions/zellij.fish b/functions/zellij.fish new file mode 100644 index 0000000..c82bc8e --- /dev/null +++ b/functions/zellij.fish @@ -0,0 +1,7 @@ +# Copyright (C) 2026 Rootiest +# SPDX-License-Identifier: AGPL-3.0-or-later + +function zellij --description 'alias zellij=zellij options --theme catppuccin-mocha' + command zellij options --theme catppuccin-mocha $argv + +end diff --git a/integrations/fzf.fish b/integrations/fzf.fish new file mode 100644 index 0000000..b76ce16 --- /dev/null +++ b/integrations/fzf.fish @@ -0,0 +1,531 @@ +### key-bindings.fish ### +# ____ ____ +# / __/___ / __/ +# / /_/_ / / /_ +# / __/ / /_/ __/ +# /_/ /___/_/ key-bindings.fish +# +# - $FZF_TMUX_OPTS +# - $FZF_CTRL_T_COMMAND +# - $FZF_CTRL_T_OPTS +# - $FZF_CTRL_R_COMMAND +# - $FZF_CTRL_R_OPTS +# - $FZF_ALT_C_COMMAND +# - $FZF_ALT_C_OPTS + + +# Key bindings +# ------------ +# The oldest supported fish version is 3.1b1. To maintain compatibility, the +# command substitution syntax $(cmd) should never be used, even behind a version +# check, otherwise the source command will fail on fish versions older than 3.4.0. +function fzf_key_bindings + + # Check fish version + if set -l -- fish_ver (string match -r '^(\d+)\.(\d+)' $version 2>/dev/null) + and test "$fish_ver[2]" -lt 3 -o "$fish_ver[2]" -eq 3 -a "$fish_ver[3]" -lt 1 + echo "This script requires fish version 3.1b1 or newer." >&2 + return 1 + else if not type -q fzf + echo "fzf was not found in path." >&2 + return 1 + end + +#----BEGIN INCLUDE common.fish +# NOTE: Do not directly edit this section, which is copied from "common.fish". +# To modify it, one can edit "common.fish" and run "./update.sh" to apply +# the changes. See code comments in "common.fish" for the implementation details. + + function __fzf_defaults + test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40% + string join ' ' -- \ + "--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \ + (test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \ + $FZF_DEFAULT_OPTS $argv[2..-1] + end + + function __fzfcmd + test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40% + if test -n "$FZF_TMUX_OPTS" + echo "fzf-tmux $FZF_TMUX_OPTS -- " + else if test "$FZF_TMUX" = "1" + echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- " + else + echo "fzf" + end + end + + function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes' + set -l tokens + if test (string match -r -- '^\d+' $version) -ge 4 + set -- tokens (commandline -xpc) + else + set -- tokens (commandline -opc) + end + + set -l -- var_count 0 + for i in $tokens + if string match -qr -- '^[\w]+=' $i + set var_count (math $var_count + 1) + else + break + end + end + set -e -- tokens[0..$var_count] + + while true + switch "$tokens[1]" + case builtin command + set -e -- tokens[1] + test "$tokens[1]" = "--"; and set -e -- tokens[1] + case env + set -e -- tokens[1] + test "$tokens[1]" = "--"; and set -e -- tokens[1] + while string match -qr -- '^[\w]+=' "$tokens[1]" + set -e -- tokens[1] + end + case '*' + break + end + end + + string escape -n -- $tokens + end + + function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix' + set -l fzf_query '' + set -l prefix '' + set -l dir '.' + + set -l -- fish_major (string match -r -- '^\d+' $version) + set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2] + + set -l -- match_regex '(?[\s\S]*?(?=\n?$)$)' + set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S' + if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3 + or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p)) + set -- match_regex "(?$prefix_regex)?$match_regex" + end + + if test "$fish_major" -ge 4 + string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N) + else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 + string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N) + eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '') + else + set -l -- cl_token (commandline --current-token --tokenize | string collect -N) + set -- prefix (string match -r -- $prefix_regex $cl_token) + set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N) + eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '') + end + + if test -n "$fzf_query" + if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \) + set -- fzf_query (path normalize -- $fzf_query) + set -- dir $fzf_query + while not path is -d $dir + set -- dir (path dirname $dir) + end + else + if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 + string match -q -r -- '(?^[\s\S]*?(?=\n?$)$)' \ + (string replace -r -a -- '(?<=/)/|(?[\s\S]*)' $fzf_query + else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 + string match -q -r -- '^/?(?[\s\S]*?(?=\n?$)$)' \ + (string replace -- "$dir" '' $fzf_query | string collect -N) + else + set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N) + eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '') + end + end + end + + string escape -n -- "$dir" "$fzf_query" "$prefix" + end +#----END INCLUDE + + # Store current token in $dir as root for the 'find' command + function fzf-file-widget -d "List files and folders" + set -l commandline (__fzf_parse_commandline) + set -lx dir $commandline[1] + set -l fzf_query $commandline[2] + set -l prefix $commandline[3] + + set -lx FZF_DEFAULT_OPTS (__fzf_defaults \ + "--reverse --walker=file,dir,follow,hidden --scheme=path" \ + "--multi $FZF_CTRL_T_OPTS --print0") + + set -lx FZF_DEFAULT_COMMAND "$FZF_CTRL_T_COMMAND" + set -lx FZF_DEFAULT_OPTS_FILE + + set -l result (eval (__fzfcmd) --walker-root=$dir --query=$fzf_query | string split0) + and commandline -rt -- (string join -- ' ' $prefix(string escape --no-quoted -- $result))' ' + + commandline -f repaint + end + + function fzf-history-widget -d "Show command history" + set -l -- command_line (commandline) + set -l -- current_line (commandline -L) + set -l -- total_lines (count $command_line) + set -l -- fzf_query (string escape -- $command_line[$current_line]) + + set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults '' \ + '--nth=2..,.. --scheme=history --multi --no-multi-line --no-wrap --wrap-sign="\t\t\t↳ " --preview-wrap-sign="↳ "' \ + '--bind=\'shift-delete:execute-silent(for i in (string split0 -- <{+f}); eval builtin history delete --exact --case-sensitive -- (string escape -n -- $i | string replace -r "^\d*\\\\\\t" ""); end)+reload(eval $FZF_DEFAULT_COMMAND)\'' \ + '--bind="alt-enter:become(string join0 -- (string collect -- {+2..} | fish_indent -i))"' \ + "--bind=ctrl-r:toggle-sort,alt-r:toggle-raw --highlight-line $FZF_CTRL_R_OPTS" \ + '--accept-nth=2.. --delimiter="\t" --tabstop=4 --read0 --print0 --with-shell='(status fish-path)\\ -c) + + # Add dynamic preview options if preview command isn't already set by user + if string match -qvr -- '--preview[= ]' "$FZF_DEFAULT_OPTS" + # Convert the highlighted timestamp using the date command if available + set -l -- date_cmd '{1}' + if type -q date + if date -d @0 '+%s' 2>/dev/null | string match -q 0 + # GNU date + set -- date_cmd '(date -d @{1} \\"+%F %a %T\\")' + else if date -r 0 '+%s' 2>/dev/null | string match -q 0 + # BSD date + set -- date_cmd '(date -r {1} \\"+%F %a %T\\")' + end + end + + # Prepend the options to allow user customizations + set -p -- FZF_DEFAULT_OPTS \ + '--bind="focus,resize:bg-transform:if test \\"$FZF_COLUMNS\\" -gt 100 -a \\\\( \\"$FZF_SELECT_COUNT\\" -gt 0 -o \\\\( -z \\"$FZF_WRAP\\" -a (string length -- {}) -gt (math $FZF_COLUMNS - 4) \\\\) -o (string collect -- {2..} | fish_indent | count) -gt 1 \\\\); echo show-preview; else echo hide-preview; end"' \ + '--preview="string collect -- (test \\"$FZF_SELECT_COUNT\\" -gt 0; and string collect -- {+2..}) \\"\\n# \\"'$date_cmd' {2..} | fish_indent --ansi"' \ + '--preview-window="right,50%,wrap-word,follow,info,hidden"' + end + + set -lx FZF_DEFAULT_OPTS_FILE + + set -lx -- FZF_DEFAULT_COMMAND 'builtin history -z --show-time="%s%t"' + + # Enable syntax highlighting colors on fish v4.3.3 and newer + if set -l -- v (string match -r -- '^(\d+)\.(\d+)(?:\.(\d+))?' $version) + and test "$v[2]" -gt 4 -o "$v[2]" -eq 4 -a \ + \( "$v[3]" -gt 3 -o "$v[3]" -eq 3 -a \ + \( -n "$v[4]" -a "$v[4]" -ge 3 \) \) + + set -a -- FZF_DEFAULT_OPTS '--ansi' + set -a -- FZF_DEFAULT_COMMAND '--color=always' + end + + # Merge history from other sessions before searching + test -z "$fish_private_mode"; and builtin history merge + + if set -l result (eval $FZF_DEFAULT_COMMAND \| (__fzfcmd) --query=$fzf_query | string split0) + if test "$total_lines" -eq 1 + commandline -- $result + else + set -l a (math $current_line - 1) + set -l b (math $current_line + 1) + commandline -- $command_line[1..$a] $result + commandline -a -- '' $command_line[$b..-1] + end + end + + commandline -f repaint + end + + function fzf-cd-widget -d "Change directory" + set -l commandline (__fzf_parse_commandline) + set -lx dir $commandline[1] + set -l fzf_query $commandline[2] + set -l prefix $commandline[3] + + set -lx FZF_DEFAULT_OPTS (__fzf_defaults \ + "--reverse --walker=dir,follow,hidden --scheme=path" \ + "$FZF_ALT_C_OPTS --no-multi --print0") + + set -lx FZF_DEFAULT_OPTS_FILE + set -lx FZF_DEFAULT_COMMAND "$FZF_ALT_C_COMMAND" + + if set -l result (eval (__fzfcmd) --query=$fzf_query --walker-root=$dir | string split0) + cd -- $result + commandline -rt -- $prefix + end + + commandline -f repaint + end + + if not set -q FZF_CTRL_R_COMMAND; or test -n "$FZF_CTRL_R_COMMAND" + if test -n "$FZF_CTRL_R_COMMAND" + echo "warning: FZF_CTRL_R_COMMAND is set to a custom command, but custom commands are not yet supported for CTRL-R" >&2 + end + bind \cr fzf-history-widget + bind -M insert \cr fzf-history-widget + end + + if not set -q FZF_CTRL_T_COMMAND; or test -n "$FZF_CTRL_T_COMMAND" + bind \ct fzf-file-widget + bind -M insert \ct fzf-file-widget + end + + if not set -q FZF_ALT_C_COMMAND; or test -n "$FZF_ALT_C_COMMAND" + bind \ec fzf-cd-widget + bind -M insert \ec fzf-cd-widget + end + +end + +# Run setup +fzf_key_bindings +### end: key-bindings.fish ### +### completion.fish ### +# ____ ____ +# / __/___ / __/ +# / /_/_ / / /_ +# / __/ / /_/ __/ +# /_/ /___/_/ completion.fish +# +# - $FZF_COMPLETION_OPTS (default: empty) + +function fzf_completion_setup + +#----BEGIN INCLUDE common.fish +# NOTE: Do not directly edit this section, which is copied from "common.fish". +# To modify it, one can edit "common.fish" and run "./update.sh" to apply +# the changes. See code comments in "common.fish" for the implementation details. + + function __fzf_defaults + test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40% + string join ' ' -- \ + "--height $FZF_TMUX_HEIGHT --min-height=20+ --bind=ctrl-z:ignore" $argv[1] \ + (test -r "$FZF_DEFAULT_OPTS_FILE"; and string join -- ' ' <$FZF_DEFAULT_OPTS_FILE) \ + $FZF_DEFAULT_OPTS $argv[2..-1] + end + + function __fzfcmd + test -n "$FZF_TMUX_HEIGHT"; or set -l FZF_TMUX_HEIGHT 40% + if test -n "$FZF_TMUX_OPTS" + echo "fzf-tmux $FZF_TMUX_OPTS -- " + else if test "$FZF_TMUX" = "1" + echo "fzf-tmux -d$FZF_TMUX_HEIGHT -- " + else + echo "fzf" + end + end + + function __fzf_cmd_tokens -d 'Return command line tokens, skipping leading env assignments and command prefixes' + set -l tokens + if test (string match -r -- '^\d+' $version) -ge 4 + set -- tokens (commandline -xpc) + else + set -- tokens (commandline -opc) + end + + set -l -- var_count 0 + for i in $tokens + if string match -qr -- '^[\w]+=' $i + set var_count (math $var_count + 1) + else + break + end + end + set -e -- tokens[0..$var_count] + + while true + switch "$tokens[1]" + case builtin command + set -e -- tokens[1] + test "$tokens[1]" = "--"; and set -e -- tokens[1] + case env + set -e -- tokens[1] + test "$tokens[1]" = "--"; and set -e -- tokens[1] + while string match -qr -- '^[\w]+=' "$tokens[1]" + set -e -- tokens[1] + end + case '*' + break + end + end + + string escape -n -- $tokens + end + + function __fzf_parse_commandline -d 'Parse the current command line token and return split of existing filepath, fzf query, and optional -option= prefix' + set -l fzf_query '' + set -l prefix '' + set -l dir '.' + + set -l -- fish_major (string match -r -- '^\d+' $version) + set -l -- fish_minor (string match -r -- '^\d+\.(\d+)' $version)[2] + + set -l -- match_regex '(?[\s\S]*?(?=\n?$)$)' + set -l -- prefix_regex '^-[^\s=]+=|^-(?!-)\S' + if test "$fish_major" -eq 3 -a "$fish_minor" -lt 3 + or string match -q -v -- '* -- *' (string sub -l (commandline -Cp) -- (commandline -p)) + set -- match_regex "(?$prefix_regex)?$match_regex" + end + + if test "$fish_major" -ge 4 + string match -q -r -- $match_regex (commandline --current-token --tokens-expanded | string collect -N) + else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 + string match -q -r -- $match_regex (commandline --current-token --tokenize | string collect -N) + eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)' '') + else + set -l -- cl_token (commandline --current-token --tokenize | string collect -N) + set -- prefix (string match -r -- $prefix_regex $cl_token) + set -- fzf_query (string replace -- "$prefix" '' $cl_token | string collect -N) + eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^\\\(?=~)|\\\(?=\$\w)|\\\n\\\n$' '') + end + + if test -n "$fzf_query" + if test \( "$fish_major" -ge 4 \) -o \( "$fish_major" -eq 3 -a "$fish_minor" -ge 5 \) + set -- fzf_query (path normalize -- $fzf_query) + set -- dir $fzf_query + while not path is -d $dir + set -- dir (path dirname $dir) + end + else + if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 + string match -q -r -- '(?^[\s\S]*?(?=\n?$)$)' \ + (string replace -r -a -- '(?<=/)/|(?[\s\S]*)' $fzf_query + else if test "$fish_major" -eq 3 -a "$fish_minor" -ge 2 + string match -q -r -- '^/?(?[\s\S]*?(?=\n?$)$)' \ + (string replace -- "$dir" '' $fzf_query | string collect -N) + else + set -- fzf_query (string replace -- "$dir" '' $fzf_query | string collect -N) + eval set -- fzf_query (string escape -n -- $fzf_query | string replace -r -a '^/?|\\\n$' '') + end + end + end + + string escape -n -- "$dir" "$fzf_query" "$prefix" + end +#----END INCLUDE + + # Use complete builtin for specific commands + function __fzf_complete_native + set -l -- token (commandline -t) + set -l -- completions (eval complete -C \"$argv[1]\") + test -n "$completions"; or begin commandline -f repaint; return; end + + # Calculate tabstop based on longest completion item (sample first 500 for performance) + set -l -- tabstop 20 + set -l -- sample_size (math "min(500, "(count $completions)")") + for c in $completions[1..$sample_size] + set -l -- len (string length -V -- (string split -- \t $c)) + test -n "$len[2]" -a "$len[1]" -gt "$tabstop" + and set -- tabstop $len[1] + end + # limit to 120 to prevent long lines + set -- tabstop (math "min($tabstop + 4, 120)") + + set -l result + set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults \ + "--reverse --delimiter=\\t --nth=1 --tabstop=$tabstop --color=fg:dim,nth:regular" \ + $FZF_COMPLETION_OPTS $argv[2..-1] --accept-nth=1 --read0 --print0) + set -- result (string join0 -- $completions | eval (__fzfcmd) | string split0) + and begin + set -l -- tail ' ' + # Append / to bare ~username results (fish omits it unlike other shells) + set -- result (string replace -r -- '^(~\w+)\s?$' '$1/' $result) + # Don't add trailing space if single result is a directory + test (count $result) -eq 1 + and string match -q -- '*/' "$result"; and set -- tail '' + + set -l -- result (string escape -n -- $result) + + string match -q -- '~*' "$token" + and set result (string replace -r -- '^\\\\~' '~' $result) + + string match -q -- '$*' "$token" + and set result (string replace -r -- '^\\\\\$' '\$' $result) + + commandline -rt -- (string join ' ' -- $result)$tail + end + commandline -f repaint + end + + function _fzf_complete + set -l -- args (string escape -- $argv | string join ' ' | string split -- ' -- ') + set -l -- post_func (status function)_(string split -- ' ' $args[2])[1]_post + set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS $args[1]) + set -lx FZF_DEFAULT_OPTS_FILE + set -lx FZF_DEFAULT_COMMAND + set -l -- fzf_query (commandline -t | string escape) + set -l result + eval (__fzfcmd) --query=$fzf_query | while read -l r; set -a -- result $r; end + and if functions -q $post_func + commandline -rt -- (string collect -- $result | eval $post_func $args[2] | string join ' ')' ' + else + commandline -rt -- (string join -- ' ' (string escape -- $result))' ' + end + commandline -f repaint + end + + # Kill completion (process selection) + function _fzf_complete_kill + set -l -- fzf_query (commandline -t | string escape) + set -lx -- FZF_DEFAULT_OPTS (__fzf_defaults --reverse $FZF_COMPLETION_OPTS \ + --accept-nth=2 -m --header-lines=1 --no-preview --wrap) + set -lx FZF_DEFAULT_OPTS_FILE + if type -q ps + set -l -- ps_cmd 'begin command ps -eo user,pid,ppid,start,time,command 2>/dev/null;' \ + 'or command ps -eo user,pid,ppid,time,args 2>/dev/null;' \ + 'or command ps --everyone --full --windows 2>/dev/null; end' + set -l -- result (eval $ps_cmd \| (__fzfcmd) --query=$fzf_query) + and commandline -rt -- (string join ' ' -- $result)" " + else + __fzf_complete_native "kill " --multi --query=$fzf_query + end + commandline -f repaint + end + + # Main completion function + function fzf-completion + set -l -- tokens (__fzf_cmd_tokens) + set -l -- current_token (commandline -t) + set -l -- cmd_name $tokens[1] + + # Route to appropriate completion function + if test -n "$tokens"; and functions -q _fzf_complete_$cmd_name + _fzf_complete_$cmd_name $tokens + else + set -l -- fzf_opt --query=$current_token --multi + __fzf_complete_native "$tokens $current_token" $fzf_opt + end + end + + # Bind Shift-Tab to fzf-completion (Tab retains native Fish behavior) + if test (string match -r -- '^\d+' $version) -ge 4 + bind shift-tab fzf-completion + bind -M insert shift-tab fzf-completion + else + bind -k btab fzf-completion + bind -M insert -k btab fzf-completion + end +end + +# Run setup +fzf_completion_setup +### end: completion.fish ### diff --git a/requirements.md b/requirements.md new file mode 100644 index 0000000..ed9eb40 --- /dev/null +++ b/requirements.md @@ -0,0 +1,119 @@ +# Fish Config Requirements + +Non-standard applications required by this fish shell configuration. + +## Terminal Emulators + +| Tool | Description | Notes | +|------|-------------|-------| +| `kitty` | GPU-accelerated terminal emulator | Primary terminal; used heavily for tab/window management via `kitty @` | +| `wezterm` | Cross-platform GPU-accelerated terminal | Referenced as alternative | +| `konsole` | KDE terminal emulator | Used in `tab.fish` as fallback | + +## File & Directory Tools + +| Tool | Description | Notes | +|------|-------------|-------| +| `lsd` | `ls` replacement with icons and colors | Wraps `ls`, `l`, `ll`, `lt`, `lstree`, `lS`, `ltr` | +| `bat` | `cat` replacement with syntax highlighting | Wraps `cat` | +| `dust` | Disk usage analyzer (Rust) | Used in `du.fish` | +| `duf` | Disk usage/free utility | Used in `du.fish` | +| `dua` | Fast disk space analyzer | Used in `du.fish` | +| `trash` / `trash-cli` | Safe file deletion to trash | Wraps `rm` | + +## Editors + +| Tool | Description | Notes | +|------|-------------|-------| +| `nvim` | Neovim text editor | Primary editor; used in `edit`, `view`, `nlazyup`, `nvimup` | +| `kate` | KDE text editor | Abbreviation target | + +## Git & Version Control + +| Tool | Description | Notes | +|------|-------------|-------| +| `lazygit` | Terminal Git UI | Abbreviation `lg` | +| `gitui` | Fast terminal Git UI | `gitui.fish` wrapper | +| `clone-in-kitty` | Clone repos into a Kitty window | Used in `clone.fish`, `clonet.fish` | + +## System & Process Monitoring + +| Tool | Description | Notes | +|------|-------------|-------| +| `btop` | Modern resource monitor | Wraps `top` | +| `fastfetch` | System info display | `ffetch.fish`, `cffetch.fish` | +| `neofetch` | System info display (fallback) | Fallback in `ffetch.fish` | + +## Package Management + +| Tool | Description | Notes | +|------|-------------|-------| +| `paru` | AUR helper (Arch Linux) | Used in `install`, `search`, `upgrade`, `parur` | + +## Navigation & Search + +| Tool | Description | Notes | +|------|-------------|-------| +| `zoxide` | Smart `cd` with frecency | Initialized in `zoxide.fish` | +| `fzf` | Fuzzy finder | Used in `parur.fish` and elsewhere | +| `ripgrep` / `rg` | Fast line-oriented search | Wrapped in `rg.fish` | + +## Shell Prompt & Theme + +| Tool | Description | Notes | +|------|-------------|-------| +| `starship` | Cross-shell prompt | Initialized in `config.fish` | + +## Terminal Multiplexers + +| Tool | Description | Notes | +|------|-------------|-------| +| `zellij` | Terminal workspace/multiplexer | `zellij.fish` wrapper | + +## Network & Remote + +| Tool | Description | Notes | +|------|-------------|-------| +| `tailscale` | VPN client | `tailscale.fish`, `tailart.fish` | +| `curl` | HTTP/data transfer | Used in `bd-pull.fish` | + +## Developer & Productivity Tools + +| Tool | Description | Notes | +|------|-------------|-------| +| `chezmoi` | Dotfile manager | Numerous abbreviations | +| `llm` | CLI for LLM interaction | `llm.fish` wrapper | +| `cheat` | Cheatsheet viewer | `cheat.fish`, `cheat.conf.d` | +| `wakatime` | Developer time tracking | Initialized in `wakatime.fish` | +| `bd` / `beads` | Lightweight issue tracker | `bd-pull.fish`, abbreviations | +| `lazybeads` | Terminal UI for beads | Abbreviation target | +| `antigravity` | Custom launcher tool | `antigravity.fish`, abbreviations | + +## Container Tools + +| Tool | Description | Notes | +|------|-------------|-------| +| `docker` | Container platform | `dops.fish`, abbreviations | +| `lazydocker` | Terminal Docker UI | `ld` function in `config.fish` | + +## Command Replacements / Enhancements + +| Tool | Description | Notes | +|------|-------------|-------| +| `most` | Advanced pager | Wraps `less` | +| `prettyping` | Pretty `ping` wrapper | Wraps `ping` | +| `sudo-rs` | Rust rewrite of sudo | Wraps `sudo` | + +## Data & System Utilities + +| Tool | Description | Notes | +|------|-------------|-------| +| `jq` | JSON processor | Used in `bd-pull.fish` | +| `wl-colorpicker-plasma` | Wayland color picker (KDE) | `get-color.fish` | + +## Fish Shell Framework & Plugins + +| Tool | Description | Notes | +|------|-------------|-------| +| `fisher` | Fish plugin manager | Manages plugins via `fish_plugins` | +| `catppuccin/fish` | Catppuccin theme for Fish | Listed in `fish_plugins` | diff --git a/themes/Catppuccin Frappe.theme b/themes/Catppuccin Frappe.theme new file mode 100644 index 0000000..b4c1994 --- /dev/null +++ b/themes/Catppuccin Frappe.theme @@ -0,0 +1,30 @@ +# name: 'Catppuccin Frappé' +# url: 'https://github.com/catppuccin/fish' +# preferred_background: 303446 + +fish_color_normal c6d0f5 +fish_color_command 8caaee +fish_color_param eebebe +fish_color_keyword e78284 +fish_color_quote a6d189 +fish_color_redirection f4b8e4 +fish_color_end ef9f76 +fish_color_comment 838ba7 +fish_color_error e78284 +fish_color_gray 737994 +fish_color_selection --background=414559 +fish_color_search_match --background=414559 +fish_color_option a6d189 +fish_color_operator f4b8e4 +fish_color_escape ea999c +fish_color_autosuggestion 737994 +fish_color_cancel e78284 +fish_color_cwd e5c890 +fish_color_user 81c8be +fish_color_host 8caaee +fish_color_host_remote a6d189 +fish_color_status e78284 +fish_pager_color_progress 737994 +fish_pager_color_prefix f4b8e4 +fish_pager_color_completion c6d0f5 +fish_pager_color_description 737994 \ No newline at end of file diff --git a/themes/Catppuccin Latte.theme b/themes/Catppuccin Latte.theme new file mode 100644 index 0000000..002dace --- /dev/null +++ b/themes/Catppuccin Latte.theme @@ -0,0 +1,30 @@ +# name: 'Catppuccin Latte' +# url: 'https://github.com/catppuccin/fish' +# preferred_background: eff1f5 + +fish_color_normal 4c4f69 +fish_color_command 1e66f5 +fish_color_param dd7878 +fish_color_keyword d20f39 +fish_color_quote 40a02b +fish_color_redirection ea76cb +fish_color_end fe640b +fish_color_comment 8c8fa1 +fish_color_error d20f39 +fish_color_gray 9ca0b0 +fish_color_selection --background=ccd0da +fish_color_search_match --background=ccd0da +fish_color_option 40a02b +fish_color_operator ea76cb +fish_color_escape e64553 +fish_color_autosuggestion 9ca0b0 +fish_color_cancel d20f39 +fish_color_cwd df8e1d +fish_color_user 179299 +fish_color_host 1e66f5 +fish_color_host_remote 40a02b +fish_color_status d20f39 +fish_pager_color_progress 9ca0b0 +fish_pager_color_prefix ea76cb +fish_pager_color_completion 4c4f69 +fish_pager_color_description 9ca0b0 \ No newline at end of file diff --git a/themes/Catppuccin Macchiato.theme b/themes/Catppuccin Macchiato.theme new file mode 100644 index 0000000..c8be912 --- /dev/null +++ b/themes/Catppuccin Macchiato.theme @@ -0,0 +1,30 @@ +# name: 'Catppuccin Macchiato' +# url: 'https://github.com/catppuccin/fish' +# preferred_background: 24273a + +fish_color_normal cad3f5 +fish_color_command 8aadf4 +fish_color_param f0c6c6 +fish_color_keyword ed8796 +fish_color_quote a6da95 +fish_color_redirection f5bde6 +fish_color_end f5a97f +fish_color_comment 8087a2 +fish_color_error ed8796 +fish_color_gray 6e738d +fish_color_selection --background=363a4f +fish_color_search_match --background=363a4f +fish_color_option a6da95 +fish_color_operator f5bde6 +fish_color_escape ee99a0 +fish_color_autosuggestion 6e738d +fish_color_cancel ed8796 +fish_color_cwd eed49f +fish_color_user 8bd5ca +fish_color_host 8aadf4 +fish_color_host_remote a6da95 +fish_color_status ed8796 +fish_pager_color_progress 6e738d +fish_pager_color_prefix f5bde6 +fish_pager_color_completion cad3f5 +fish_pager_color_description 6e738d \ No newline at end of file diff --git a/themes/Catppuccin Mocha.theme b/themes/Catppuccin Mocha.theme new file mode 100644 index 0000000..892a000 --- /dev/null +++ b/themes/Catppuccin Mocha.theme @@ -0,0 +1,30 @@ +# name: 'Catppuccin Mocha' +# url: 'https://github.com/catppuccin/fish' +# preferred_background: 1e1e2e + +fish_color_normal cdd6f4 +fish_color_command 89b4fa +fish_color_param f2cdcd +fish_color_keyword f38ba8 +fish_color_quote a6e3a1 +fish_color_redirection f5c2e7 +fish_color_end fab387 +fish_color_comment 7f849c +fish_color_error f38ba8 +fish_color_gray 6c7086 +fish_color_selection --background=313244 +fish_color_search_match --background=313244 +fish_color_option a6e3a1 +fish_color_operator f5c2e7 +fish_color_escape eba0ac +fish_color_autosuggestion 6c7086 +fish_color_cancel f38ba8 +fish_color_cwd f9e2af +fish_color_user 94e2d5 +fish_color_host 89b4fa +fish_color_host_remote a6e3a1 +fish_color_status f38ba8 +fish_pager_color_progress 6c7086 +fish_pager_color_prefix f5c2e7 +fish_pager_color_completion cdd6f4 +fish_pager_color_description 6c7086 \ No newline at end of file