[
  {
    "path": ".github/workflows/ci.yaml",
    "content": "name: CI\n\non:\n  workflow_dispatch:\n  pull_request:\n    paths-ignore:\n      - '**.md'\n      - LICENSE\n  push:\n    branches: [master]\n    paths-ignore:\n      - '**.md'\n      - LICENSE\n\nconcurrency:\n  group: ${{ github.workflow }}-${{ github.ref }}\n  cancel-in-progress: true\n\njobs:\n  pre-commit:\n    runs-on: ubuntu-latest\n    timeout-minutes: 5\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n\n      - name: Setup Python\n        uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0\n        with:\n          python-version: 3.x\n\n      - name: Run pre-commit hooks\n        uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1\n\n  ci:\n    strategy:\n      fail-fast: false\n      matrix:\n        os: [ubuntu-latest, macos-15]\n    runs-on: ${{ matrix.os }}\n    timeout-minutes: 15\n\n    steps:\n      - name: Install zsh\n        if: runner.os == 'Linux'\n        shell: bash\n        run: sudo apt-get update && sudo apt-get -y install zsh\n\n      - name: Create zsh runner\n        shell: bash\n        run: |\n          cat > /usr/local/bin/zsh-runner << 'EOF'\n          #!/bin/bash\n          # Runs a script in an interactive zsh with a pseudo-TTY.\n          # Linux script(1) uses -c; macOS script(1) takes a positional command.\n          if [ \"$(uname)\" = \"Darwin\" ]; then\n            exec script -q -e -a /dev/null /bin/zsh \"$1\"\n          else\n            exec script -q -e -a /dev/null -c \"zsh -i $1\"\n          fi\n          EOF\n          chmod +x /usr/local/bin/zsh-runner\n\n      - name: Install oh-my-zsh\n        run: sh -c \"$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)\" \"\" --unattended\n\n      - name: Checkout plugin\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          path: autoupdate\n\n      - name: Checkout zsh-autosuggestions\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          repository: zsh-users/zsh-autosuggestions\n          path: zsh-autosuggestions\n          fetch-depth: 0\n\n      - name: Checkout zsh-syntax-highlighting\n        uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2\n        with:\n          repository: zsh-users/zsh-syntax-highlighting\n          path: zsh-syntax-highlighting\n          fetch-depth: 0\n\n      - name: Downgrade plugins to old commits\n        shell: bash\n        run: |\n          git -C zsh-autosuggestions reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9\n          ZSH_SYNTAX_OLD=$(git -C zsh-syntax-highlighting rev-list HEAD~50 -n1)\n          git -C zsh-syntax-highlighting reset --hard \"$ZSH_SYNTAX_OLD\"\n          echo \"ZSH_SYNTAX_OLD_SHA=$ZSH_SYNTAX_OLD\" >> \"$GITHUB_ENV\"\n\n      - name: Move plugins to oh-my-zsh custom directory\n        shell: bash\n        run: |\n          mkdir -p \"$HOME/.oh-my-zsh/custom/plugins\"\n          mv -v autoupdate zsh-autosuggestions zsh-syntax-highlighting \"$HOME/.oh-my-zsh/custom/plugins\"\n\n      - name: Create .zshrc\n        shell: bash\n        run: |\n          cat > \"$HOME/.zshrc\" << EOC\n          export ZSH=\"$HOME/.oh-my-zsh\"\n          ZSH_THEME=\"robbyrussell\"\n          plugins=(autoupdate)\n\n          # Always ignore the autoupdate plugin itself (detached HEAD in CI).\n          # Append to any ignore list already set by the test step env var.\n          export ZSH_CUSTOM_AUTOUPDATE_IGNORE=\"\\${ZSH_CUSTOM_AUTOUPDATE_IGNORE:+\\${ZSH_CUSTOM_AUTOUPDATE_IGNORE} }autoupdate\"\n\n          source $HOME/.oh-my-zsh/oh-my-zsh.sh\n          EOC\n\n      - name: Test basic plugin update\n        shell: zsh-runner {0}\n        env:\n          TERM: xterm-256color\n        run: |\n          source \"$HOME/.zshrc\"\n          upgrade_oh_my_zsh_custom\n\n      - name: Verify both plugins were updated\n        shell: bash\n        run: |\n          set -x\n          AUTOSUGGESTIONS_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" rev-parse HEAD)\n          SYNTAX_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" rev-parse HEAD)\n          if [ \"$AUTOSUGGESTIONS_HEAD\" = \"89c600c873a7e64b075c916850e80bfd62f81de9\" ]; then\n            echo \"ERROR: zsh-autosuggestions was not updated\"\n            exit 1\n          fi\n          if [ \"$SYNTAX_HEAD\" = \"$ZSH_SYNTAX_OLD_SHA\" ]; then\n            echo \"ERROR: zsh-syntax-highlighting was not updated\"\n            exit 1\n          fi\n          echo \"Both plugins updated successfully\"\n\n      - name: Re-downgrade plugins for quiet mode test\n        shell: bash\n        run: |\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" reset --hard \"$ZSH_SYNTAX_OLD_SHA\"\n\n      - name: Test quiet mode\n        shell: zsh-runner {0}\n        env:\n          TERM: xterm-256color\n          ZSH_CUSTOM_AUTOUPDATE_QUIET: \"true\"\n        run: |\n          source \"$HOME/.zshrc\"\n          upgrade_oh_my_zsh_custom > /tmp/quiet_output.txt 2>&1\n\n      - name: Verify quiet mode suppresses output\n        shell: bash\n        run: |\n          if grep -q \"Upgrading Custom Plugins\" /tmp/quiet_output.txt; then\n            echo \"ERROR: Quiet mode should suppress the upgrade banner\"\n            cat /tmp/quiet_output.txt\n            exit 1\n          fi\n          echo \"Quiet mode test passed\"\n\n      - name: Re-downgrade plugins for parallel workers test\n        shell: bash\n        run: |\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" reset --hard \"$ZSH_SYNTAX_OLD_SHA\"\n\n      - name: Test parallel workers\n        shell: zsh-runner {0}\n        env:\n          TERM: xterm-256color\n          ZSH_CUSTOM_AUTOUPDATE_NUM_WORKERS: \"2\"\n        run: |\n          source \"$HOME/.zshrc\"\n          upgrade_oh_my_zsh_custom\n\n      - name: Verify parallel update\n        shell: bash\n        run: |\n          set -x\n          AUTOSUGGESTIONS_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" rev-parse HEAD)\n          SYNTAX_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" rev-parse HEAD)\n          if [ \"$AUTOSUGGESTIONS_HEAD\" = \"89c600c873a7e64b075c916850e80bfd62f81de9\" ]; then\n            echo \"ERROR: zsh-autosuggestions was not updated in parallel mode\"\n            exit 1\n          fi\n          if [ \"$SYNTAX_HEAD\" = \"$ZSH_SYNTAX_OLD_SHA\" ]; then\n            echo \"ERROR: zsh-syntax-highlighting was not updated in parallel mode\"\n            exit 1\n          fi\n          echo \"Parallel workers test passed\"\n\n      - name: Re-downgrade plugins for ignore list test\n        shell: bash\n        run: |\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" reset --hard \"$ZSH_SYNTAX_OLD_SHA\"\n\n      - name: Test ignore list\n        shell: zsh-runner {0}\n        env:\n          TERM: xterm-256color\n          ZSH_CUSTOM_AUTOUPDATE_IGNORE: \"zsh-autosuggestions\"\n        run: |\n          source \"$HOME/.zshrc\"\n          upgrade_oh_my_zsh_custom\n\n      - name: Verify ignored plugin was skipped\n        shell: bash\n        run: |\n          set -x\n          AUTOSUGGESTIONS_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" rev-parse HEAD)\n          SYNTAX_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" rev-parse HEAD)\n          if [ \"$AUTOSUGGESTIONS_HEAD\" != \"89c600c873a7e64b075c916850e80bfd62f81de9\" ]; then\n            echo \"ERROR: zsh-autosuggestions should have been skipped but was updated\"\n            exit 1\n          fi\n          if [ \"$SYNTAX_HEAD\" = \"$ZSH_SYNTAX_OLD_SHA\" ]; then\n            echo \"ERROR: zsh-syntax-highlighting should have been updated\"\n            exit 1\n          fi\n          echo \"Ignore list test passed\"\n\n      - name: Re-downgrade plugins and create nested repo for nested git test\n        shell: bash\n        run: |\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" reset --hard 89c600c873a7e64b075c916850e80bfd62f81de9\n          git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" reset --hard \"$ZSH_SYNTAX_OLD_SHA\"\n          NESTED=\"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions/test-nested-dep\"\n          git init \"$NESTED\"\n          git -C \"$NESTED\" -c user.email=\"ci@test\" -c user.name=\"CI\" commit --allow-empty -m \"initial\"\n          NESTED_SHA=$(git -C \"$NESTED\" rev-parse HEAD)\n          git -C \"$NESTED\" checkout --detach HEAD\n          echo \"NESTED_SHA=$NESTED_SHA\" >> \"$GITHUB_ENV\"\n\n      - name: Test nested git directories are ignored\n        shell: zsh-runner {0}\n        env:\n          TERM: xterm-256color\n        run: |\n          source \"$HOME/.zshrc\"\n          upgrade_oh_my_zsh_custom\n\n      - name: Verify nested git test\n        shell: bash\n        run: |\n          set -x\n          AUTOSUGGESTIONS_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions\" rev-parse HEAD)\n          SYNTAX_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-syntax-highlighting\" rev-parse HEAD)\n          NESTED_HEAD=$(git -C \"$HOME/.oh-my-zsh/custom/plugins/zsh-autosuggestions/test-nested-dep\" rev-parse HEAD)\n          if [ \"$AUTOSUGGESTIONS_HEAD\" = \"89c600c873a7e64b075c916850e80bfd62f81de9\" ]; then\n            echo \"ERROR: zsh-autosuggestions was not updated\"\n            exit 1\n          fi\n          if [ \"$SYNTAX_HEAD\" = \"$ZSH_SYNTAX_OLD_SHA\" ]; then\n            echo \"ERROR: zsh-syntax-highlighting was not updated\"\n            exit 1\n          fi\n          if [ \"$NESTED_HEAD\" != \"$NESTED_SHA\" ]; then\n            echo \"ERROR: nested repo should not have been touched\"\n            exit 1\n          fi\n          echo \"Nested git test passed\"\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n- repo: https://github.com/pre-commit/pre-commit-hooks\n  rev: v5.0.0\n  hooks:\n  - id: check-yaml\n  - id: end-of-file-fixer\n  - id: trailing-whitespace\n  - id: mixed-line-ending\n\n- repo: https://github.com/Lucas-C/pre-commit-hooks\n  rev: v1.5.5\n  hooks:\n    - id: forbid-crlf\n    - id: remove-crlf\n    - id: forbid-tabs\n    - id: remove-tabs\n      args: [--whitespaces-count, '2']\n\n- repo: https://github.com/shellcheck-py/shellcheck-py\n  rev: v0.10.0.1\n  hooks:\n    - id: shellcheck\n      # .zsh files have no shebang; treat as bash for analysis. SC2059 is\n      # intentionally disabled: color escape codes in printf format strings\n      # are valid and deliberate throughout this plugin.\n      args: ['--shell=bash', '--severity=warning', '--exclude=SC2059']\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018-2022 Philipp Born\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "autoupdate-zsh-plugin\n====================\n\n[oh-my-zsh plugin](https://github.com/robbyrussell/oh-my-zsh) for auto updating of git-repositories in $ZSH_CUSTOM folder\n\n## Install\n\nCreate a new directory in `$ZSH_CUSTOM/plugins` called `autoupdate` and clone this repo into that directory. Note: it must be named `autoupdate` or oh-my-zsh won't recognize that it is a valid plugin directory.\n```\ngit clone https://github.com/TamCore/autoupdate-oh-my-zsh-plugins $ZSH_CUSTOM/plugins/autoupdate\n```\n\n## Usage\n\nAdd `autoupdate` to the `plugins=()` list in your `~/.zshrc` file and you're done.\n\n```bash\nplugins=(autoupdate)\n\n# Multiple plugins should be separated by space character\n# plugins=(somePlugin autoupdate)\n```\n\nBy default this will auto update both plugins and themes, found in the $ZSH_CUSTOM folder, every 13 days (which is the OhMyZsh default).\n\nIf you want to check for updates more or less often, you can export the `UPDATE_ZSH_DAYS` variable in your `~/.zshrc` file:\n```bash\n# to check for updates once a month\nexport UPDATE_ZSH_DAYS=30\n# or to check for updates daily\nexport UPDATE_ZSH_DAYS=1\n```\n\nAnother possibility is to use the provided upgrade function, which one may call\nat any time using `upgrade_oh_my_zsh_custom`. There shouldn't be any difference\nwith the automatic operation. Also, a convenient alias that calls the OhMyZsh\nupdate function `omz update` and then `upgrade_oh_my_zsh_custom`, called\n`upgrade_oh_my_zsh_all`, is available as well. However, running `omz update` directly **will not** trigger the this plugin.\n\n### Quiet mode\n\nTo turn off the \"Upgrading custom plugins\" message (for example, if you're using [Powerlevel10k's instant prompt](https://github.com/romkatv/powerlevel10k#instant-prompt)), add this to your `~/.zshrc` file:\n```shell\n# Uncomment the following line to change how often to auto-update (in days).\n# export UPDATE_ZSH_DAYS=13\nZSH_CUSTOM_AUTOUPDATE_QUIET=true\n```\n\n### Parallel downloads\n\nTo speed up updates by setting maximum number of parallel downloads, add this to your `~/.zshrc` file:\n```shell\n# Values accepted (min: 1, max: 16)\n# Parallel downloads will not be enabled if value is out-of-range\nZSH_CUSTOM_AUTOUPDATE_NUM_WORKERS=8\n```\n\n### Ignore Plugin List\n\nTo prevent some plugins from update, add this to your `~/.zshrc` file:\n```shell\n# support ',' ';' ':' as the split char\nZSH_CUSTOM_AUTOUPDATE_IGNORE=\"zsh-autosuggestions, autoupdate; zsh-autosuggestions\"\n```\n"
  },
  {
    "path": "autoupdate.plugin.zsh",
    "content": "# - - - - - - - - - - - - - - - - - - - - - - - - - - - -\n# self-update\n\nif which tput >/dev/null 2>&1; then\n  ncolors=$(tput colors)\nfi\n\nif [[ $- == *i* ]] && [ -n \"$ncolors\" ] && [ \"$ncolors\" -ge 8 ]; then\n  RED=\"$(tput setaf 1)\"\n  BLUE=\"$(tput setaf 4)\"\n  GREEN=\"$(tput setaf 2)\"\n  NORMAL=\"$(tput sgr0)\"\nelse\n  BLUE=\"\"\n  GREEN=\"\"\n  NORMAL=\"\"\nfi\n\nzmodload zsh/datetime\n\nfunction _current_epoch() {\n  echo $(( $EPOCHSECONDS / 60 / 60 / 24 ))\n}\n\nfunction _update_zsh_custom_update() {\n  echo \"LAST_EPOCH=$(_current_epoch)\" >| \"${ZSH_CACHE_DIR}/.zsh-custom-update\"\n}\n\nfunction _get_epoch_target() {\n  local epoch_target\n\n  zstyle -g epoch_target ':omz:update' frequency \\\n    || epoch_target=\"$UPDATE_ZSH_DAYS\"\n  if [[ -z \"$epoch_target\" ]]; then\n    # Default to old behavior\n    epoch_target=13\n  fi\n\n  echo \"$epoch_target\"\n}\n\nepoch_target=\"$(_get_epoch_target)\"\n\nfunction _upgrade_custom_plugin() {\n  # path of plugin/theme\n  p=$(dirname \"$d\")\n  # it's name\n  pn=$(basename \"$p\")\n  # it's type (plugin/theme)\n  pt=$(dirname \"$p\")\n  pt=$(basename ${pt:0:((${#pt} - 1))})\n\n  # Check if plugin should be ignored\n  if [[ -n \"$ZSH_CUSTOM_AUTOUPDATE_IGNORE\" ]]; then\n    echo \"$ZSH_CUSTOM_AUTOUPDATE_IGNORE\" | tr ',;: ' '\\n' | grep -v '^$' | while read ignored; do\n      if [[ \"$pn\" == \"$ignored\" ]]; then\n        printf \"${BLUE}%s${NORMAL}\\n\" \"Skipping $pn $pt (in ignore list)\"\n        return 0\n      fi\n    done\n  fi\n\n  last_head=$( git -C \"${p}\" rev-parse HEAD )\n  if git -C \"${p}\" pull --quiet --rebase --stat --autostash\n  then\n    curr_head=$( git -C \"${p}\" rev-parse HEAD )\n    if [ \"${last_head}\" != \"${curr_head}\" ]\n    then\n      printf \"${GREEN}%s${NORMAL}\\n\" \"Hooray! the $pn $pt has been updated.\"\n    else\n      printf \"${BLUE}%s${NORMAL}\\n\" \"The $pn $pt was already at the latest version.\"\n    fi\n  else\n    printf \"${RED}%s${NORMAL}\\n\" \"There was an error updating the $pn $pt. Try again later?\"\n  fi\n}\n\nfunction upgrade_oh_my_zsh_custom() {\n  if [[ -z \"$ZSH_CUSTOM_AUTOUPDATE_QUIET\" ]]; then\n    printf \"${BLUE}%s${NORMAL}\\n\" \"Upgrading Custom Plugins\"\n  fi\n\n  num_workers=$( printf \"%.0f\" \"$ZSH_CUSTOM_AUTOUPDATE_NUM_WORKERS\" )\n  set +m\n  find -L \"${ZSH_CUSTOM}\" -maxdepth 3 -name .git | while IFS= read -r d\n  do\n    if ! test \"$num_workers\" -gt 1 2> /dev/null || \\\n    test \"$num_workers\" -gt 16 2> /dev/null; then\n      _upgrade_custom_plugin \"${d}\"\n    else\n      ((i=(i+1)%$num_workers)) || wait\n      (_upgrade_custom_plugin \"${d}\") &\n    fi\n  done\n  wait\n  set -m\n}\n\nalias upgrade_oh_my_zsh_all='zsh \"$ZSH/tools/upgrade.sh\"; upgrade_oh_my_zsh_custom'\n\n\nif [ -f ~/.zsh-custom-update ]\nthen\n  mv ~/.zsh-custom-update \"${ZSH_CACHE_DIR}/.zsh-custom-update\"\nfi\n\nfunction _dispatch_update_mode() {\n  local mode\n\n  zstyle -g mode ':omz:update' mode\n  if [[ -z \"$mode\" ]]; then\n    if [[ \"$DISABLE_AUTO_UPDATE\" == \"true\" ]]; then\n      mode=\"disabled\"\n    elif [[ \"$DISABLE_UPDATE_PROMPT\" == \"true\" ]]; then\n      mode=\"auto\"\n    else\n      mode=\"prompt\"\n    fi\n  fi\n\n  echo \"$mode\"\n}\n\nupdate_mode=\"$(_dispatch_update_mode)\"\n\nif [[ \"$update_mode\" == \"disabled\" ]]\nthen\n  : # No updates\nelif [ -f \"${ZSH_CACHE_DIR}/.zsh-custom-update\" ]\nthen\n  . \"${ZSH_CACHE_DIR}/.zsh-custom-update\"\n\n  if [[ -z \"$LAST_EPOCH\" ]]\n  then\n    LAST_EPOCH=0\n  fi\n\n  epoch_diff=$(($(_current_epoch) - $LAST_EPOCH))\n  if [ \"$epoch_diff\" -gt \"$epoch_target\" ]\n  then\n    if [[ \"$update_mode\" == \"auto\" ]]\n    then\n      (upgrade_oh_my_zsh_custom)\n    elif [[ \"$update_mode\" == \"reminder\" ]]\n    then\n      echo \"[oh-my-zsh] It's time to update! You can do that by running \\`upgrade_oh_my_zsh_custom\\`\"\n    else\n      echo \"[oh-my-zsh] Would you like to check for custom plugin updates? [Y/n]: \\c\"\n      read line\n      if [[ \"$line\" == Y* ]] || [[ \"$line\" == y* ]] || [ -z \"$line\" ]\n      then\n        (upgrade_oh_my_zsh_custom)\n      fi\n    fi\n    _update_zsh_custom_update\n  fi\nelse\n  _update_zsh_custom_update\nfi\n\nunset -f _update_zsh_custom_update _current_epoch \\\n  _get_epoch_target _dispatch_update_mode\n"
  }
]