{"id":23493458,"url":"https://github.com/moyiz/na","last_synced_at":"2026-05-06T08:31:31.540Z","repository":{"id":211085919,"uuid":"723608054","full_name":"moyiz/na","owner":"moyiz","description":"CLI tool to effortlessly manage context aware nested shortcuts for shell commands.","archived":false,"fork":false,"pushed_at":"2024-01-21T12:42:04.000Z","size":47,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-07-11T18:46:06.526Z","etag":null,"topics":["aliases","cli","go","golang","nested-aliases","nested-commands","shell","shortcuts","terminal"],"latest_commit_sha":null,"homepage":"https://github.com/moyiz/na","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/moyiz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-11-26T07:49:47.000Z","updated_at":"2024-04-12T08:20:03.000Z","dependencies_parsed_at":"2023-12-13T20:33:26.677Z","dependency_job_id":"a4f5c4a3-d061-4517-ac1c-ecc39ddb8c9f","html_url":"https://github.com/moyiz/na","commit_stats":null,"previous_names":["moyiz/na"],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/moyiz/na","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moyiz%2Fna","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moyiz%2Fna/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moyiz%2Fna/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moyiz%2Fna/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/moyiz","download_url":"https://codeload.github.com/moyiz/na/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/moyiz%2Fna/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32684596,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-06T02:33:58.958Z","status":"ssl_error","status_checked_at":"2026-05-06T02:33:39.611Z","response_time":117,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aliases","cli","go","golang","nested-aliases","nested-commands","shell","shortcuts","terminal"],"created_at":"2024-12-25T02:27:30.442Z","updated_at":"2026-05-06T08:31:31.523Z","avatar_url":"https://github.com/moyiz.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"```text\n     _______   ______  \n    |       \\ |      \\ \n    | ▓▓▓▓▓▓▓\\ \\▓▓▓▓▓▓\\\n    | ▓▓  | ▓▓/      ▓▓\n    | ▓▓  | ▓▓  ▓▓▓▓▓▓▓\n    | ▓▓  | ▓▓\\▓▓    ▓▓\n     \\▓▓   \\▓▓ \\▓▓▓▓▓▓▓\n```\n![GitHub go.mod Go version (subdirectory of monorepo)](https://img.shields.io/github/go-mod/go-version/moyiz/na)\n[![Go Reference](https://pkg.go.dev/badge/github.com/moyiz/na.svg)](https://pkg.go.dev/github.com/moyiz/na)\n![GitHub License](https://img.shields.io/github/license/moyiz/na)\n![GitHub release (with filter)](https://img.shields.io/github/v/release/moyiz/na)\n![GitHub all releases](https://img.shields.io/github/downloads/moyiz/na/total)\n\n\n**na** (aka: _nested-aliases_, _non-aliases_, _not-[an]-alias_) is a CLI tool to effortlessly manage context aware nested shortcuts for shell commands.\n\n\u003c!-- Demo here --\u003e\n\n## Contents\n- [Motivation](#motivation)\n- [Features](#features)\n- [Installation](#installation)\n    - [Binaries](#binaries)\n    - [AUR](#aur)\n    - [Homebrew](#homebrew)\n    - [Source](#source)\n    - [Build](#build)\n- [Usage](#usage)\n    - [Adding shortcuts](#adding-shortcuts)\n    - [Listing shortcuts](#listing-shortcuts)\n    - [Running shortcuts](#running-shortcuts)\n    - [Removing shortcuts](#removing-shortcuts)\n    - [Argument Placeholders](#argument-placeholders)\n- [Shell Completions](#shell-completions)\n    - [Bash](#bash)\n    - [Zsh](#zsh)\n    - [Fish](#fish)\n    - [Powershell](#powershell)\n- [Configuration](#configuration)\n    - [Showcase](#showcase)\n    - [Example](#example)\n- [Known Issues](#known-issues)\n- [Future Plans](#future-plans)\n- [License](#license)\n\n## Motivation\nShell aliases are fun. They provide an easy and straightforward way to create simple shortcuts without too much fuss, but not without few caveats:\n- *Aliases must be named with a single word* - It makes grouping related aliases within the same context a bit awkward, e.g:\n    ```sh\n    alias lab-password-sftpgo=\"k get secret -n sftpgo sftpgo-admin -ojsonpath='{.data.admin-password}' | base64 -d\"\n    alias lab-password-gitea=\"k get secret -n gitea gitea-secret -ojsonpath='{.data.password}' | base64 -d\"\n    alias lab-create-secret=\"k create secret --dry-run -oyaml\"\n    ```\n    Using dashes (or underscores) to separate conceptual subcommands interferes with the flow of shell completion and renaming the common prefixes of the aliases names is inconvenient as well. This limitation also prevent aliases from imitating commands.\n- Without implementing workarounds, *Aliases (or any shell configuration) are global for the current user*, and thus there is *no context aware toggling of aliases* - Some of the aliases might be relevant only for a single purpose or project.\n- *Aliases do not support passing non suffixed arguments* - Aliases are substituted. That is why there is no support for passing arguments that are not suffixed to the alias itself. A possible workaround is to use shell functions or other tools.\n\nLast but not least, this project is a great excuse for me to work with _Go_, as it is a great language for developing CLI tools. Single binaries for CLI tools are awesome.\n\n## Features\n- **N**esting **a**liases (ehm, shortcuts).\n- Supports passing arguments and argument substitutions.\n- Dynamic shell completions.\n- Simple and readable configuration.\n- Layered configuration (local and home config).\n\n## Installation\n\n### Binaries\nAvailable in [Releases](https://github.com/moyiz/na/releases) page.\n\n### AUR\nUse your favorite AUR helper:\n```sh\nyay -S na-bin\n```\n\n### Homebrew\nInstall from a tap:\n```sh\nbrew install moyiz/tap/na\n```\n\n### Source\n```sh\ngo install github.com/moyiz/na@latest\n```\n\n### Build\n```sh\ngit clone https://github.com/moyiz/na.git\ngo build .\n# And move it to your preferred `bin`\nmv na ~/.local/bin\n```\n\n## Usage\n\n### Adding shortcuts\nUse the `add` subcommand (or its shorter form: `a`) to add new shortcuts:\n```sh\nna add my shortcut cwd pwd\nna a my longcut cwd -- echo A shortcut to show current working directory, which is '$PWD'\nna a e echo\nna a swap -- echo %2% %1%\n```\nIn the first example (without double dashes), the last argument is the target of the shortcut, i.e `na run my shortcut cwd` will execute `pwd`. This is handy for single word commands.\n\nMulti word commands can be quoted (i.e `na add my shortcut \"ls -ltra\"`) but using a double dash is also a valid option. In the second example, anything after the double dash is considered target, thus `na run my longcut cwd` will run that long `echo`. Notice that `$PWD` is single quoted. This is to ensure the actual substitution will occur when the shortcut is called, rather than when it is being added.\n\nIf the config directory does not exist, `na` will create it.\n\n### Listing shortcuts\nRun `na` without arguments, or use the `list` / `ls` subcommand if you prefer.\n```sh\nna\nna ls\nna list\n```\n\n### Running shortcuts\nUse the `run` subcommand (or its shorted form: `r`).\n```sh\nna run my shortcut cwd\nna r e -- Hello World!\nna r swap -- a b  # output: b a\n```\nThe first example is self explanatory. Notice the double dash in the second example. In `run` it is mandatory in order to pass arguments to the shortcut itself.\n\n_na_ will try to auto-detect the current calling shell and invoke the command in it. If it fails to detect or current shell is not supported, `sh` will be used as fallback.\n\n### Removing shortcuts\nUse the `remove` subcommand (or its shorter form: `rm`).\n```sh\nna remove my shortcut cwd\nna remove my longcut\nna rm e\n```\nNotice that this command accepts partial shortcuts. It will remove the entire subtree of given shortcuts. The first two examples above can be reduced to a single `na rm my` to delete both.\n\n### Argument Placeholders\n`na` supports magic placeholders in commands. Magic placeholders are wrapped with `%` and allow passing dynamic arguments directly into the target command.\n\nWe will start with an example:\n```sh\n$ na add swap -- echo %2% %1%\n$ na run swap -- a b\nb a\n$ na run swap -- a b c d\nb a c d\n$ na add double -- echo %2% %2%\n$ na run double -- a b\nb b a\n$ na run double -- a b c d\nb b a c d\n```\n\nThese placeholders are \"popped\" from the given argument list into the target command. All arguments that are not referenced by a magic placeholder will be passed to the end of the command as regular arguments.\n\nThis behavior allows more flexibility in command generation, such as:\n```sh\nna add secret -- kubectl get secret -n %1% %2% -ojsonpath='{.data.%3%}' \\| base64 -d\nna r secret -- gitea gitea-admin-secret password\nna r secret -- gitea gitea-admin-secret username\n```\n\n_Tip_: Argument placeholders can also be negative to reference arguments backwards.\n```sh\n$ na add last is first -- echo %-1%\n$ na run last is first -- a b c d e\ne a b c d\n```\n\n## Shell Completions\nSome installation methods already setup completions. To activate them manually, add the following to your shell's configuration.\n### Bash\nAdd this to your `~/.bashrc`:\n```sh\nsource \u003c(na completion bash)\n```\n### Zsh\nAdd this to your `~/.zshrc`:\n```sh\nsource \u003c(na completion zsh)\n```\nIn case of `command not found: compdef`, add these too:\n```sh\nautoload -Uz compinit\ncompinit\n```\n\n### Fish\nAdd this to `~/.config/fish/config.fish`\n```sh\nna completion fish | source\n```\n\n### Powershell\nExist but untested.\n\n## Configuration\n_na_ looks for configuration files in few locations:\n- Local directory (`.na.yaml`)\n- Current user home config directory (`~/.config/na/na.yaml`)\n- XDG config directory (`/etc/xdg/na/na.yaml`)\n\nBy default, `na` will merge these configs for `list` and `run`, and use current user's home config directory for `add` and `remove`. \n\nThis behavior will be overridden by passing either:\n- `--config FILE` or `-c FILE`: set the only configuration file to use.\n- `--local` or `-l`: synonymous to `-c .na.yaml`.\n- `--user` or `-u`: synonymous to `-c ${XDG_CONFIG_HOME}/na/na.yaml`.\n\n_na_ configuration is a simple dictionary, mapping shortcut names to either commands or other subcommands.\n\n### Showcase\n```sh\n$ cat .na.yaml\ncat: .na.yaml: No such file or directory\n$ cat ~/.config/na/na.yaml\ncat: /home/moyiz/.config/na/na.yaml: No such file or directory\n$ na add my global -- echo Global\n$ cat ~/.config/na/na.yaml\nmy:\n    global: echo Global\n$ na add -l my local -- echo Local\n$ cat .na.yaml \nmy:\n    local: echo Local\n$ na\nmy global -- echo Global\nmy local -- echo Local\n$ na run my local\nLocal\n$ na run my global\nGlobal\n$ na rm -l my\n$ cat .na.yaml\n{}\n$ cat ~/.config/na/na.yaml\nmy:\n    global: echo Global\n\n```\n\n### Example\n```yaml\nlab:\n  secret:\n    gitea: k get secret -n gitea gitea-admin-secret -ojsonpath='{.data.password}' | base64 -d\n    nextcloud:  get secret -n gitea gitea-admin-secret -ojsonpath='{.data.password}' | base64 -d\n    new: k create secret generic --dry-run -oyaml \u003e secret.yaml\n    seal: kubeseal --scope cluster-wide -oyaml \u003c secret.yaml \u003e sealed-secret.yaml\n    sealete: na r lab secret seal \u0026\u0026 rm secret.yaml\n  renovate: k delete job -n renovate renovate-manual; k create -n renovate job --from cronjob/renovate renovate-manual\n  backup:\n    prepare: sudo mount -t nfs nas:/pool/backups /media/backups \n    vm: ssh proxmox.home ./backup.sh\n    local:\n        volumes: na r backup prepare \u0026\u0026 rsync ...\n        home: na r backup prepare \u0026\u0026 rsync ...\nrandom: echo $RANDOM\n```\n\n## Known Issues\n- Since `na` spawns a new shell to run commands, some builtin commands will\n  have no effect on current shell, e.g: `alias`, `cd`, `declare`, `export`,\n  `pushd`, `read`, `set`, `shopt`, `source` and so on.\n\n## Future Plans\n- Read aliases from environment variables (and `.env`).\n- Rename aliases.\n- Dynamically creates actual commands, e.g.\n  ```sh\n  my alias\n  # Instead of:\n  na run my alias\n  ```\n\n## License\nSee [LICENSE](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoyiz%2Fna","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmoyiz%2Fna","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmoyiz%2Fna/lists"}