Prefix plugin clones with the username so shared clones don't collide

Clones of omarchy.clock become dhh.clock instead of local.clock, so a
published clone carries its author's namespace. The clone command owns
the id derivation and gains --edit to open the result in $EDITOR, and
the shell exposes clonedFrom in listPlugins so the plugin menu no longer
reconstructs clone ids.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
David Heinemeier Hansson
2026-08-01 13:13:39 -05:00
co-authored by Claude Fable 5
parent 2050f28d55
commit 1bc89105d2
9 changed files with 79 additions and 57 deletions
+16 -15
View File
@@ -70,15 +70,15 @@ pick() {
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": true},
{"id": "local.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
{"id": "tester.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
]
JSON
pick enable "Clock (local.clock)"
[[ $ROWS == *"Clock (omarchy.clock)"* && $ROWS == *"Clock (local.clock)"* ]] \
pick enable "Clock (tester.clock)"
[[ $ROWS == *"Clock (omarchy.clock)"* && $ROWS == *"Clock (tester.clock)"* ]] \
|| fail "picker tells two plugins of the same name apart" "$ROWS"
pass "picker tells two plugins of the same name apart"
[[ $CALLS == *"omarchy-plugin-enable local.clock"* ]] \
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|| fail "picker acts on the row that was picked, not the one that shares its name" "$CALLS"
pass "picker acts on the row that was picked, not the one that shares its name"
@@ -87,19 +87,19 @@ pass "picker acts on the row that was picked, not the one that shares its name"
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
{"id": "local.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
{"id": "tester.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
]
JSON
pick enable "Clock"
[[ $ROWS != *"("* ]] || fail "picker adorns a row only when its name is taken twice over" "$ROWS"
pass "picker adorns a row only when its name is taken twice over"
[[ $CALLS == *"omarchy-plugin-enable local.clock"* ]] \
[[ $CALLS == *"omarchy-plugin-enable tester.clock"* ]] \
|| fail "picker resolves a lone row to the plugin the verb offered, not a namesake it filtered out" "$CALLS"
pass "picker resolves a lone row to the plugin the verb offered, not a namesake it filtered out"
pick remove "Clock"
[[ $CALLS == *"omarchy-plugin-remove local.clock"* ]] \
[[ $CALLS == *"omarchy-plugin-remove tester.clock"* ]] \
|| fail "picker removes the plugin whose row was picked" "$CALLS"
pass "picker removes the plugin whose row was picked"
@@ -118,8 +118,8 @@ pass "picker leaves an unambiguous name unadorned"
|| fail "picker delegates plugin enablement to the plugin command" "$CALLS"
pass "picker delegates plugin enablement to the plugin command"
# Clone offers only first-party plugins without an existing local counterpart,
# then performs the clone and opens its deterministic path in $EDITOR.
# Clone offers only first-party plugins that no installed clone points back at,
# then hands the pick to the clone command, which opens the result in $EDITOR.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
@@ -131,15 +131,16 @@ pick clone "Clock"
[[ $ROWS == *"Clock"* && $ROWS != *"Weather"* ]] ||
fail "clone picker offers only built-in plugins" "$ROWS"
pass "clone picker offers built-in plugins"
[[ $CALLS == *'terminal: omarchy-plugin-clone omarchy.clock && exec $EDITOR '*"/.config/omarchy/plugins/local.clock" ]] ||
fail "clone picker opens the cloned path in EDITOR" "$CALLS"
pass "clone picker clones and opens the local plugin"
[[ $CALLS == *"terminal: omarchy-plugin-clone --edit omarchy.clock"* ]] ||
fail "clone picker delegates cloning and editing to the clone command" "$CALLS"
pass "clone picker clones and opens the personal plugin"
# Once local.<id> is discovered, the source no longer belongs in Clone.
# Once a clone pointing back at the source is discovered, whatever it is named,
# the source no longer belongs in Clone.
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.clock", "name": "Clock", "kinds": ["bar-widget"], "enabled": true, "active": false, "canDisable": true, "firstParty": true},
{"id": "local.clock", "name": "My Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false}
{"id": "tester.clock", "name": "My Clock", "kinds": ["bar-widget"], "enabled": false, "active": false, "canDisable": true, "firstParty": false, "clonedFrom": "omarchy.clock"}
]
JSON
@@ -179,7 +180,7 @@ pass "picker keeps a bar out of disable"
cat >"$TMPDIR/plugins.json" <<'JSON'
[
{"id": "omarchy.bar", "name": "Bar", "kinds": ["bar"], "enabled": false, "active": false, "canDisable": false, "firstParty": true},
{"id": "local.neon-bar", "name": "Neon Bar", "kinds": ["bar"], "enabled": true, "active": true, "canDisable": false, "firstParty": false}
{"id": "tester.neon-bar", "name": "Neon Bar", "kinds": ["bar"], "enabled": true, "active": true, "canDisable": false, "firstParty": false}
]
JSON
+27 -21
View File
@@ -31,7 +31,7 @@ fi
exit 0
SH
for command in omarchy-plugin-enable omarchy-notification-send; do
for command in omarchy-plugin-enable omarchy-notification-send fake-editor; do
cat >"$TMPDIR/bin/$command" <<'SH'
#!/bin/bash
printf '%s %s\n' "${0##*/}" "$*" >>"$FAKE_CALLS"
@@ -49,14 +49,14 @@ clone_plugin() {
}
}
}'
HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$TMPDIR/bin:$ROOT/bin:$PATH" \
HOME="$TMPDIR/home" USER=tester OMARCHY_PATH="$ROOT" PATH="$TMPDIR/bin:$ROOT/bin:$PATH" \
FAKE_CALLS="$CALLS" OMARCHY_TEST_ROOT="$ROOT" \
FAKE_SHELL_CONFIG="${FAKE_CLONE_CONFIG:-$default_config}" \
omarchy-plugin-clone "$@"
}
clone_plugin omarchy.clock >/dev/null
clock="$TMPDIR/home/.config/omarchy/plugins/local.clock"
clock="$TMPDIR/home/.config/omarchy/plugins/tester.clock"
for file in manifest.json BarWidget.qml Panel.qml Model.js; do
[[ -f $clock/$file ]] || fail "clock clone is missing $file"
@@ -73,7 +73,7 @@ rg -qF "omarchy.clock" "$clock" -g '*.qml' -g '*.js' ||
pass "clone preserves the built-in runtime IPC id"
jq -e '
.id == "local.clock" and
.id == "tester.clock" and
.name == "My Clock" and
.barWidget.displayName == "My Clock" and
.omarchy.clonedFrom == "omarchy.clock" and
@@ -82,53 +82,53 @@ jq -e '
' "$clock/manifest.json" >/dev/null || fail "clock clone manifest is incorrect"
pass "clone updates identity without replacing the manifest"
grep -qx 'omarchy-plugin-enable local.clock' "$CALLS" ||
grep -qx 'omarchy-plugin-enable tester.clock' "$CALLS" ||
fail "clone does not enable the editable copy"
grep -qx 'omarchy-notification-send -g 󰐱 Editing Cloned Plugin Original plugin has been replace by clone.' "$CALLS" ||
fail "clone does not notify that the editable clone is active"
pass "clone enables bar widgets and confirms the editable clone"
clone_plugin omarchy.keyboard-layout >/dev/null
grep -qx 'omarchy-plugin-enable local.keyboard-layout' "$CALLS" ||
grep -qx 'omarchy-plugin-enable tester.keyboard-layout' "$CALLS" ||
fail "clone does not enable a clone of a legacy string-form bar entry"
pass "clone enables clones of legacy string-form bar entries"
clone_plugin omarchy.menu >/dev/null
menu="$TMPDIR/home/.config/omarchy/plugins/local.menu"
menu="$TMPDIR/home/.config/omarchy/plugins/tester.menu"
for file in manifest.json Menu.qml MenuModel.js BarWidget.qml; do
[[ -f $menu/$file ]] || fail "menu clone is missing $file"
done
jq -e '
.id == "local.menu" and
.id == "tester.menu" and
.kinds == ["menu", "bar-widget"] and
.entryPoints.menu == "Menu.qml" and
.entryPoints.barWidget == "BarWidget.qml"
' "$menu/manifest.json" >/dev/null || fail "menu clone loses plugin kinds"
grep -qx 'omarchy-plugin-enable local.menu' "$CALLS" ||
grep -qx 'omarchy-plugin-enable tester.menu' "$CALLS" ||
fail "clone does not enable a multi-kind plugin"
pass "clone preserves and enables multi-kind plugins"
remove_output=$(HOME="$TMPDIR/home" OMARCHY_PATH="$ROOT" PATH="$TMPDIR/bin:$ROOT/bin:$PATH" \
FAKE_CALLS="$CALLS" OMARCHY_TEST_ROOT="$ROOT" \
omarchy-plugin-remove local.menu --yes)
grep -qx 'omarchy-shell shell setPluginEnabled local.menu false' "$CALLS" ||
omarchy-plugin-remove tester.menu --yes)
grep -qx 'omarchy-shell shell setPluginEnabled tester.menu false' "$CALLS" ||
fail "removing an enabled clone does not disable it first"
grep -q 'Restored omarchy.menu.' <<<"$remove_output" ||
fail "removing a clone does not report its restored source"
pass "removing an enabled clone goes through plugin disable and reports its source"
clone_plugin omarchy.active-window >/dev/null
[[ -f $TMPDIR/home/.config/omarchy/plugins/local.active-window/ActiveWindow.qml ]] ||
[[ -f $TMPDIR/home/.config/omarchy/plugins/tester.active-window/ActiveWindow.qml ]] ||
fail "flat bar plugin clone is incomplete"
pass "flat bar plugins clone from adjacent manifests"
grep -qx 'omarchy-plugin-enable local.active-window' "$CALLS" ||
grep -qx 'omarchy-plugin-enable tester.active-window' "$CALLS" ||
fail "clone does not activate a bar widget whose source is absent"
pass "clone activates an absent bar widget"
clone_plugin omarchy.indicators >/dev/null
indicators="$TMPDIR/home/.config/omarchy/plugins/local.indicators"
indicators="$TMPDIR/home/.config/omarchy/plugins/tester.indicators"
for file in Indicators.qml indicators/Dnd.qml indicators/Reminder.qml; do
[[ -f $indicators/$file ]] || fail "indicators clone is missing $file"
done
@@ -137,20 +137,26 @@ grep -q 'Qt.resolvedUrl("indicators/"' "$indicators/Indicators.qml" ||
pass "flat bar plugins declare extra clone dependencies"
clone_plugin omarchy.tray >/dev/null
[[ -f $TMPDIR/home/.config/omarchy/plugins/local.tray/TrayModel.js ]] ||
[[ -f $TMPDIR/home/.config/omarchy/plugins/tester.tray/TrayModel.js ]] ||
fail "tray clone is missing its model"
pass "flat bar plugins keep local script dependencies"
clone_plugin omarchy.bar >/dev/null
grep -qx 'omarchy-plugin-enable local.bar' "$CALLS" ||
grep -qx 'omarchy-plugin-enable tester.bar' "$CALLS" ||
fail "clone does not select a cloned bar"
pass "clone switches full bars"
clone_plugin omarchy.background >/dev/null
grep -qx 'omarchy-plugin-enable local.background' "$CALLS" ||
grep -qx 'omarchy-plugin-enable tester.background' "$CALLS" ||
fail "clone does not enable an ordinary cloned plugin"
pass "clone switches ordinary plugins"
EDITOR=fake-editor clone_plugin omarchy.weather --edit >/dev/null
grep -qx "fake-editor $TMPDIR/home/.config/omarchy/plugins/tester.weather" "$CALLS" ||
fail "clone --edit does not open the clone in EDITOR"
pass "clone --edit opens the clone in EDITOR"
rm -rf "$TMPDIR/home/.config/omarchy/plugins/tester.weather"
mkdir -p "$TMPDIR/home/.config/omarchy/plugins/acme.example"
cat >"$TMPDIR/home/.config/omarchy/plugins/acme.example/manifest.json" <<'JSON'
{"id":"acme.example","name":"Example","kinds":["bar-widget"],"entryPoints":{"barWidget":"Widget.qml"}}
@@ -163,14 +169,14 @@ pass "clone is limited to built-in plugins"
if clone_plugin omarchy.weather custom.weather >/dev/null 2>&1; then
fail "clone accepts a custom id"
fi
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/local.weather ]] ||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/tester.weather ]] ||
fail "rejected custom id leaves a clone behind"
pass "clone derives the local id"
pass "clone derives the personal id from the username"
if clone_plugin omarchy.weather --replace >/dev/null 2>&1; then
fail "clone still accepts bar layout actions"
fi
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/local.weather ]] ||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/tester.weather ]] ||
fail "rejected bar action leaves a clone behind"
pass "clone does not accept manual switch options"
@@ -182,6 +188,6 @@ pass "clone requires an explicit source id"
if FAKE_NO_DISCOVERY=1 clone_plugin omarchy.osd >/dev/null 2>&1; then
fail "clone succeeds before the shell discovers it"
fi
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/local.osd ]] ||
[[ ! -e $TMPDIR/home/.config/omarchy/plugins/tester.osd ]] ||
fail "failed clone discovery leaves a partial clone behind"
pass "clone removes a partial clone when switching fails"
+1 -1
View File
@@ -132,7 +132,7 @@ done
jq -e '
map(.id) as $ids |
all(["omarchy.menu", "omarchy.notifications", "omarchy.clock", "omarchy.osd"][]; $ids | index(.)) and
all(.[]; (.kinds | type == "array") and (.enabled | type == "boolean") and (.canDisable | type == "boolean") and (.firstParty | type == "boolean")) and
all(.[]; (.kinds | type == "array") and (.enabled | type == "boolean") and (.canDisable | type == "boolean") and (.firstParty | type == "boolean") and (.clonedFrom | type == "string")) and
([.[].name] == ([.[].name] | sort))
' <<<"$plugins" >/dev/null || {
printf 'Plugins:\n%s\n' "$plugins" | jq . >&2