---
title: 🌀 What can Annexes do?
description: Annex Introduction
canonical_url: https://wiki.zshell.dev/ecosystem/annexes/overview
markdown_url: https://wiki.zshell.dev/ecosystem/annexes/overview/index.md
locale: en
source_path: ecosystem/annexes/0_overview.mdx
---

<!-- Generated from the canonical Z-Shell Wiki build. The linked human-facing page remains the editorial source of truth. -->

# 🌀 What can Annexes do?

1.  Add a new Zi subcommand (i.e. the [command](https://wiki.zshell.dev/docs/guides/commands/index.md) that’s placed after the function `zi …` when calling Zi).
    
2.  Add new [ice-modifiers](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md).
    
3.  Register four types of hooks:
    
    3.1. `atclone` hook – run after cloning any plugin or downloading any snippet.
    
    3.2. `atpull` hook – run after pulling new commits (i.e. updating) for any plugin/snippet.
    
    3.3. `atinit` hook – run before loading any plugin/snippet, after it has been set up (i.e. downloaded).
    
    3.4. `atload` hook – run after loading any plugin/snippet.
    
4.  Register hooks for generating help text, shown by the `zi icemods` subcommand.
    

<a id="recommended-annexes"></a>

## Recommended annexes

<a id="common"></a>

### Common

1.  [z-a-bin-gem-node](https://github.com/z-shell/z-a-bin-gem-node)
2.  [z-a-readurl](https://github.com/z-shell/z-a-readurl)
3.  [z-a-patch-dl](https://github.com/z-shell/z-a-patch-dl)
4.  [z-a-rust](https://github.com/z-shell/z-a-rust)

<a id="additional"></a>

### Additional

1.  [z-a-submods](https://github.com/z-shell/z-a-submods)
2.  [z-a-unscope](https://github.com/z-shell/z-a-unscope)
3.  [z-a-test](https://github.com/z-shell/z-a-test)

tip

Use [meta-plugins](https://wiki.zshell.dev/ecosystem/annexes/meta-plugins/index.md) to install common annexes as a group:

```zi
zi light-mode for z-shell/z-a-meta-plugins @annexes
```

To install common and additional annexes:

```zi
zi light-mode for z-shell/z-a-meta-plugins @annexes+rec
```

<a id="how-to-code-them"></a>

## How to code them?

Below is an example body of an `atclone` hook taken from [submods](https://github.com/z-shell/z-a-submods) annex.

It shows how to:

1.  Obtain the arguments passed to the hook.
2.  Use an [ice-modifier](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md).
3.  It also shows a useful snippet that will trim the whitespace in array elements (see `# (4) …` in the code).
4.  Utilize the last hook argument – the plugin’s/snippet’s containing directory.

```zi
emulate -L zsh -o extended_glob -o warn_create_global -o typeset_silent

[[ -z "${ZI_ICE[submods]}" ]] && return 0

# (1) – get arguments
[[ "$1" = plugin ]] && \
local type="$1" user="$2" plugin="$3" id_as="$4" dir="$5" hook="$6" || \
local type="$1" url="$2" id_as="$3" dir="$4" hook="$6" # type: snippet

# (2) – we're interested only in plugins/snippets
# which have the submods'' ice in their load command
[[ -z ${ZI_ICE[submods]} ]] && return 0

local -a mods parts
local mod from

# (3) – process the submods'' ice
mods=( ${(@s.;.)ZI_ICE[submods]} )
for mod in "${mods[@]}"; do
  parts=( "${(@s:->:)mod}" )
  # (4) Remove only leading and trailing whitespace
  parts=( "${parts[@]//((#s)[[:space:]]##|[[:space:]]##(#e))/}" )

  print "\nCloning submodule: ${parts[1]} to dir: ${parts[2]}"
  from="https://github.com"
  parts[1]="${from}/${parts[1]}"
  # (5) – the use of the input argument: `$dir'
  command git -C "$dir" clone --progress "${parts[1]}" "${parts[2]}"
done
```

The recommended method of creating a hook is to place its body into a file that starts with a right arrow `→` ([more information](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#the-proposed-function-name-prefixes), and also a `za-` prefix, e.g. `→za-myproject-atclone-hook` and then to mark it for autoloading via `autoload -Uz →za-myproject-atclone-hook`. Then register the hook, presumably in the `myproject.plugin.zsh` file, with the API call:

`@zi-register-annex`:

```zi
@zi-register-annex myproject hook:atclone \
  →za-myproject-atclone-handler \
  →za-myproject-atclone-help-handler \
  "submods''" # register a new ice-modifier: submods''
```

The general syntax of the API call is:

```zi
@zi-register-annex {project-name} \
  {hook: \
  {name-of-the-handler-function} \
  {name-of-the-HELP-handler-function} \
  "{ice-mod1}|{ice-mod2}|…" < hook-type >| subcommand: < new-subcommand-name > }
```

The last argument, i.e. the `|`\-separated ice list, is optional. That’s all! After this loading the plugin `myproject` will set up the new [ice-modifier](https://wiki.zshell.dev/docs/guides/syntax/ice-modifiers/index.md) `submods` that will have syntax `submods'{user}/{plugin} –> {output-dir}; …'` and will clone submodules when installing the original plugin or snippet!

Example of the [submods](https://github.com/z-shell/z-a-submods) ice-modifier to load a plugin with additional submodules:

```zi
zi ice submods'zsh-users/zsh-autosuggestions -> external'
zi load some/plugin
```

Check out the project which fully implements this idea, [z-a-submods](https://github.com/z-shell/z-a-submods). It e.g. also implements the `atpull` hook, i.e. supports the automatic update of the submodules. The `z-a-*` prefix is recommended for projects which indicate annexes.

<a id="unregistering-an-annex"></a>

## Unregistering an annex

An annex that follows the [Zsh Plugin Standard](https://wiki.zshell.dev/community/zsh_plugin_standard/index.md#unload-function) unload contract removes its handler functions when it is unloaded. Removing a handler without also removing its registration leaves Zi dispatching to a function that no longer exists, so the registration has to go first:

`@zi-unregister-annex`:

```zi
myproject_plugin_unload() {
  @zi-unregister-annex myproject hook:atclone
  unfunction →za-myproject-atclone-handler \
    →za-myproject-atclone-help-handler \
    myproject_plugin_unload
}
```

The general syntax mirrors the registration call, using the same project name and hook type that were given to `@zi-register-annex`:

```zi
@zi-unregister-annex {project-name} { hook: < hook-type >| subcommand: < subcommand-name > }
```

Unregistering something that was never registered is a no-op, not an error, so an unload function does not need to guard the call.

warning

Removing a handler without unregistering it corrupts later loads. Zi calls the stored handler unconditionally, a missing function returns `127`, and Zi folds that error into its return value and shifts its argument list. Every plugin loaded afterwards in that session is affected, and the first one fails with `Error: No plugin or snippet ID given.`

info

The ice-modifiers an annex contributed at registration are not withdrawn. Registration appends them to a shared list without recording which annex contributed which entry, so a leftover ice name stays recognised but dispatches to nothing.

<a id="summary"></a>

## Summary

There are 2 or 3 subtypes for each of the hooks:

1.  `atinit` or `!atinit` – the `!` version is run before the `atinit` ice-modifier (i.e. before `zi ice atinit'echo this!'; …`), while the normal version runs after it.
2.  `atload` or `!atload` – analogous to the `atinit` case: the `!` version runs before the `atload` ice-modifier (while the normal version runs after it).
3.  `atclone` or `!atclone` – analogous to the `atinit` and `atload` cases.
4.  `atpull`, `!atpull`, or `%atpull` – the first two are being run **only when there are new commits to be downloaded** during the update. The `%` version is being **always** run, regardless of whether the update will pull any actual commits or not, and it is being run **after** the `atpull` ice-modifier.
