#!/bin/bash # omarchy:summary=Clone a built-in Omarchy shell plugin into your own config # omarchy:group=plugin # omarchy:args= set -euo pipefail PLUGINS_DIR="$HOME/.config/omarchy/plugins" fail() { echo "omarchy-plugin-clone: $*" >&2 exit 1 } copy_plugin() { local source_dir="$1" local manifest="$2" local target_dir="$3" if [[ ${manifest##*/} == "manifest.json" ]]; then cp -aL "$source_dir/." "$target_dir/" return fi cp -aL "$manifest" "$target_dir/manifest.json" local source target source_pattern file while IFS=$'\t' read -r source target; do [[ $target != /* && $target != *".."* ]] || fail "invalid clone target path: $target" if [[ -d $source_dir/$source ]]; then mkdir -p "$target_dir/$target" cp -aL "$source_dir/$source/." "$target_dir/$target/" else mkdir -p "$(dirname "$target_dir/$target")" cp -aL "$source_dir/$source" "$target_dir/$target" fi if [[ $source != "$target" ]]; then source_pattern=${source//./\\.} while IFS= read -r -d '' file; do sed -i "s|$source_pattern|$target|g" "$file" done < <(rg --files-with-matches --null --fixed-strings "$source" "$target_dir") fi done < <(jq -r ' [ (.entryPoints[] | {source: ., target: .}), (.omarchy.clonePaths[]? | {source: .source, target: .target}) ] | unique_by(.target)[] | [.source, .target] | @tsv ' "$manifest") } update_manifest() { local target_dir="$1" local source_id="$2" local new_id="$3" local display_name="$4" local manifest="$target_dir/manifest.json" # Keep built-in ids inside the plugin code as stable IPC targets. The shell # uses clonedFrom to route those calls to this local manifest id. jq \ --arg id "$new_id" \ --arg name "$display_name" \ --arg sourceId "$source_id" ' .id = $id | .name = $name | if (.barWidget | type) == "object" then .barWidget.displayName = $name else . end | .omarchy = ( (if (.omarchy | type) == "object" then .omarchy else {} end) + { clonedFrom: $sourceId } ) | del(.omarchy.clonePaths) ' "$manifest" >"$manifest.tmp" mv "$manifest.tmp" "$manifest" } usage() { cat < Copies a built-in plugin into ~/.config/omarchy/plugins/local./ so you can edit it freely. The clone keeps all of the source plugin's files and kinds and uses "My " as its display name, then replaces the built-in with the clone. USAGE } source_id="" if (( $# > 0 )) && [[ $1 != --* ]]; then source_id="$1" shift fi while (( $# > 0 )); do case "$1" in -h | --help) usage exit 0 ;; *) fail "unknown clone option: $1" ;; esac done [[ -n $source_id ]] || fail "clone source is required" source_info=$(omarchy-plugin-catalog | jq -r --arg id "$source_id" ' .[] | select(.firstParty and .id == $id) | [ .sourceDir, .manifestPath, (.name // .id) ] | @tsv ') [[ -n $source_info ]] || fail "unknown built-in plugin: $source_id" IFS=$'\t' read -r source_dir source_manifest source_name <<<"$source_info" new_id="local.${source_id#omarchy.}" display_name="My $source_name" target_dir="$PLUGINS_DIR/$new_id" [[ ! -e $target_dir && ! -L $target_dir ]] || fail "$target_dir already exists" mkdir -p "$PLUGINS_DIR" stage=$(mktemp -d "$PLUGINS_DIR/.clone.XXXXXX") clone_complete=0 cleanup() { [[ -d ${stage:-} ]] && rm -rf "$stage" (( clone_complete )) || rm -rf "$target_dir" } trap cleanup EXIT copy_plugin "$source_dir" "$source_manifest" "$stage" update_manifest "$stage" "$source_id" "$new_id" "$display_name" mv "$stage" "$target_dir" stage="" omarchy-shell shell rescanPlugins >/dev/null discovered=0 for (( attempt = 0; attempt < 40; attempt++ )); do if omarchy-plugin-list --json | jq -e --arg id "$new_id" 'any(.[]; .id == $id)' >/dev/null; then discovered=1 break fi sleep 0.05 done (( discovered )) || fail "cloned plugin '$new_id' was not discovered" omarchy-plugin-enable "$new_id" >/dev/null clone_complete=1 omarchy-notification-send -g 󰐱 \ "Editing Cloned Plugin" \ "Original plugin has been replace by clone." echo "Cloned $source_id to $target_dir and switched to $new_id"