第一次寫 Omarchy plugin 就上手:用 oma.spectra 當例子,從 clone 內建時鐘到上架 marketplace
我把 Spectra 做成 Omarchy 的 bar 外掛 oma.spectra,從第一個 commit 到可以送 marketplace 的 v0.3.2 是同一個下午的兩個半小時,整包 41 個 commit。這篇把過程寫成別人也走得通的路:Omarchy plugin 有六種 kind、每種對應一個 entryPoints 鍵與一個固定檔名,官方建議的起手式不是從空資料夾開始而是 omarchy plugin clone omarchy.clock --edit 把內建時鐘複製成自己的,因為時鐘就是「一個 bar 圖示加一個面板」這個最常見的形狀。manifest.json 只有六個必填欄位,但 barWidget 區塊裡的 schema 陣列才是讓使用者能在 shell 設定裡改參數的關鍵。驗證分兩層:omarchy plugin validate 看 manifest 與資料夾結構,qmllint -I $OMARCHY_PATH/shell 看 QML 對不對得上已安裝的 shell imports,而且 validate 不吃 symlink,要指到 ~/.config/omarchy/plugins 底下的真實路徑。上架只有三步:公開 repo、根目錄放合格 manifest、開 issue 表單送審,自動檢查會綁定一個 commit 快照。後半是 oma.spectra 本身:面板讀 Spectra CLI 的 list 與 status JSON、渲染 proposal/design/specs/tasks、可改 .spectra.yaml 五個鍵,其餘一律唯讀,new/apply/archive/park 留在終端機。踩到的坑有四個是有紀錄的:mise/nvm 裝的 spxa 只在互動 shell 的 PATH 上、登入 profile 印任何東西都會污染 CLI 回來的 JSON、validator 拒絕 symlink、Markdown 裡的圖片與角括號要先消毒再丟給 Qt 的渲染器。
oma.spectra 是我寫的第一個 Omarchy 外掛,現在掛在官方 marketplace 上,id 就是 oma.spectra,MIT,分類 DEVELOPER TOOLS。它做的事很單純:在 bar 上放一個圖示,點開是一個面板,把這台機器上所有 Spectra 專案、每個專案開著的 change、每個 change 的 proposal / design / specs / tasks 都攤在同一個畫面裡。
這篇不是使用說明,是把「第一次寫 Omarchy plugin」的路線寫下來,讓下一個人不用重新摸。內容來自官方的 Develop / Publish 兩份文件、losehrt/oma.spectra 這個公開 repo 的 README 與 commit 紀錄,以及 marketplace 的上架頁。

一、先看時間軸:兩個半小時到可上架
整包 41 個 commit。以下是台北時間:
| 時間 | 版本 | 做到哪 |
|---|---|---|
| 2026-09-13 16:37 | 無 | 第一個 commit |
| 2026-09-13 16:53 | v0.1.0 | 補 author、LICENSE、安裝說明,準備公開 |
| 2026-09-13 17:28 | v0.2.0 | 支援多個專案根目錄,把 Spectra 自己的工作檔排除在 repo 外 |
| 2026-09-13 18:00 | v0.3.0 | 面板內可直接編輯專案根目錄,含一層資料夾瀏覽器 |
| 2026-09-13 18:48 | v0.3.1 | CLI 改走登入 shell,hero 顯示 plugin id 與 manifest 版本 |
| 2026-09-13 19:12 | v0.3.2 | 補 Uninstall 與 Dependencies 兩節、加 preview.png,對齊上架要求 |
| 2026-09-13 19:41 | 無 | README 開頭放桌面實拍截圖 |
| 2026-09-14 08:11 | v0.3.3 | 純粹為了讓 marketplace 驗證的 commit 有對應的 tag |
| 2026-09-16 21:32 | v0.3.4 | 渲染前先消毒 artifact 的 Markdown |
從第一個 commit 到「這包可以送審了」是 16:37 到 19:12,兩小時三十五分。這個速度不是因為 QML 好寫,是因為起手式不是空資料夾。
二、六種 kind,先挑對形狀
Omarchy plugin 的第一個決定是 kinds。文件列了六種,每種對應一個 entryPoints 的鍵和一個約定檔名:
| kind | entryPoints 鍵 | 載入的檔 | 用途 |
|---|---|---|---|
bar-widget |
barWidget |
BarWidget.qml |
bar 上的一個項目 |
panel |
panel |
Panel.qml |
浮動面板 |
overlay |
overlay |
Overlay.qml |
全螢幕覆蓋層 |
menu |
menu |
Menu.qml |
被叫出來的選單 |
service |
service |
Service.qml |
無畫面的常駐單例 |
bar |
bar |
Bar.qml |
整條 bar 換掉 |
容易搞混的是 bar-widget 跟 panel。文件講得很清楚:如果面板是這個 bar 圖示點開的東西,那它就是 bar-widget 的一部分,kinds 只寫 ["bar-widget"],entry point 指向 bar 的那支 QML,面板由它內部用 Loader 載進來,不要為了這個面板再多宣告一個 panel kind。
oma.spectra 走的是這條,但把 entry point 直接命名成 Panel.qml:
"kinds": ["bar-widget"],
"entryPoints": { "barWidget": "Panel.qml" }
檔名不是強制的,entryPoints 寫什麼就載什麼,只要大小寫跟磁碟上一致。
三、起手式:clone 內建時鐘,不要從零開始
官方 Develop 文件的第一步不是「建一個資料夾」,是:
omarchy plugin clone omarchy.clock --edit
理由很實際:內建時鐘就是「一個 bar 圖示加一個詳細面板」這個最常見的形狀,clone 出來的是一份可以跑的完整骨架。指令跑完會印出新的 plugin id、建好資料夾、用你設定的編輯器打開它,而且它會直接接手 bar 上原本時鐘的位置,所以你改一行存檔就看得到結果。
~/.config/omarchy/plugins/yourname.clock/
├── manifest.json
├── BarWidget.qml
├── Panel.qml
└── Model.js
開發期間就用它印出來的那個 id,不要急著改名。存檔會自動重載;只有在它沒發現檔案變動時才需要手動掃一次:
omarchy-shell shell rescanPlugins
正式的命名空間 id(例如 oma.spectra)留到要公開前才換,同時把 clone 產生的 omarchy.clonedFrom 欄位刪掉。那個欄位在開發期間要留著,因為停用或移除 clone 時它負責把內建時鐘還原回去。
第三方 id 不能用 omarchy.* 開頭,這是 validator 會擋的硬規則。
四、manifest.json:必填六欄,真正有用的是 schema
上架文件給的最小 manifest 只有六個必填欄位:schemaVersion、id、name、version、kinds、entryPoints(author、description、license 實務上一定要寫)。
但如果你的外掛需要使用者設定參數,關鍵在 barWidget 區塊裡的 schema 陣列。oma.spectra 只有一個設定,長這樣:
"barWidget": {
"displayName": "Spectra",
"category": "AI",
"aliases": ["spectra", "spxa"],
"allowMultiple": false,
"defaultSection": "right",
"defaults": { "projectsRoot": "~/projects" },
"schema": [
{
"key": "projectsRoot",
"type": "string",
"label": "Projects folders",
"defaultValue": "~/projects",
"description": "One or more folders separated by ':'; their direct subfolders containing a .spectra.yaml are listed."
}
]
}
宣告了 schema,這個設定就會出現在 shell 的 widget 設定介面裡,值寫進 ~/.config/omarchy/shell.json 的這個 widget 條目。使用者也可以直接在 bar 版面設定裡內嵌:
{ "id": "oma.spectra", "projectsRoot": "~/code:~/work" }
另外三個欄位值得記:activation 設 on-demand(面板關著就什麼都不跑)、defaultSection 決定圖示預設落在 bar 的哪一段、allowMultiple 設 false 表示同一個 bar 不能放兩個。
五、驗證分兩層,而且 validator 不吃 symlink
PLUGIN_ID="yourname.clock"
PLUGIN_DIR="$HOME/.config/omarchy/plugins/$PLUGIN_ID"
omarchy plugin validate "$PLUGIN_DIR"
qmllint -I "$OMARCHY_PATH/shell" "$PLUGIN_DIR/BarWidget.qml" "$PLUGIN_DIR/Panel.qml"
omarchy plugin validate 檢查四件事:manifest 是合法 JSON 且六個必填欄位都在、kind 與 entryPoints 對得上、entryPoints 指到的檔案真的存在而且是安全的相對路徑、id 沒有用保留的 omarchy.* 前綴且資料夾裡沒有 symlink。
qmllint 檢查的是另一件事:你的 QML 對不對得上已安裝的那套 shell imports。兩個都要跑,過不了的話錯誤訊息是可以直接動手的,例如 entry point file not found: 'BarWidget.qml'。
這裡有一個實際踩到的坑,README 也特別寫了一句:validate 跟其他 omarchy plugin 指令要指在 ~/.config/omarchy/plugins/<id> 這個真實路徑上,validator 拒絕從 symlink 出發。如果你跟我一樣習慣把 plugin 原始碼放在 ~/projects 底下再 symlink 過去,這條會直接擋住你。
裝好之後確認 shell 真的看得到它:
omarchy plugin list --json | jq --arg id "$PLUGIN_ID" '.[] | select(.id == $id)'
面板生命週期可以直接從指令列打:
omarchy-shell shell summon "$PLUGIN_ID" '{}'
omarchy-shell shell hide "$PLUGIN_ID"
送出去之前,文件要求測過這七種情況:點擊、Esc、shell 開、shell 關、停用、重新啟用、shell 重啟、移除。
六、上架只有三步
- 公開的 GitHub repo,根目錄放合格的
manifest.json,README 與 license 都要有,安裝與移除都要是安全的。preview.png選配,上傳後會自動壓縮。 - manifest 先在本機 validate 過。
- 開 issue 表單送審:填 repo 連結、分類、標籤。自動檢查會跑在當下那個 commit 上,然後由維護者核准上架。
marketplace 頁面上會顯示驗證狀態,oma.spectra 目前是:Compatibility PASSED、Verified snapshot dc30eaa(2026-09-17)、Branch main、Upstream changes 無。
要注意驗證的語意:marketplace 驗證的是那一個快照 commit,不是你的 HEAD。而安裝指令 omarchy plugin add <repo>.git --enable clone 的是 repo 當下的 HEAD,兩者可能不同。頁面上的 Security Notice 就是在講這件事,順帶提醒第三方 plugin 是不沙箱、以你的使用者權限執行的。這一點在官方文件裡重複了三次,寫自己的 plugin 時值得放在心上。
所以 v0.3.3 那個「純粹為了讓驗證的 commit 有對應 tag」的 bump 不是形式主義,它是讓「marketplace 驗過的東西」和「使用者裝到的東西」對得起來的唯一辦法。
七、oma.spectra 實際上在做什麼
載入鏈是四層:
flowchart TD
A["manifest.json<br/>entryPoints.barWidget"] --> B["Panel.qml<br/>bar 按鈕、彈出面板、所有渲染"]
B --> C["Projects.qml<br/>掃描 projectsRoot"]
C --> D["Project.qml<br/>單一專案的 .spectra.yaml、list、status、specs"]
D --> E["bash -l 執行 <cli><br/>spectra / specx / spxa"]
E --> F["JSON 回傳<br/>change 清單與進度"]
B --> G["Spectra.js<br/>純函式:設定解析、JSON 形狀、CLI 白名單"]
G -.被測試.-> H["Spectra.test.js"]
面板上有的東西:hero(logo 加目前專案名)、專案 chip、change 列(名稱、completed/total、進度條、狀態)、parked 的 change 以淡色排在後面、ARCHIVED (N) 折疊區、artifact 分頁(proposal / design / specs / tasks,CLI 沒回報 done 的那個分頁是淡的)、內容區用 Qt 的 Markdown 引擎渲染、SPECS (N) 折疊區、SETTINGS 折疊區。
鍵盤游標會依視覺順序走過每一個可點的項目,j/k 上下、h/l 左右、Enter 啟用、Tab 切分頁、r 重掃、Esc 逐層關閉。IPC 也開得很滿,omarchy-shell oma.spectra <fn> 支援 show / hide / toggle / refresh / next / project / select / tab / spec / terminal / cursor / setSetting / update / state 等等,state 會回一包 JSON 描述現在什麼開著、選到哪、游標在哪。
滑鼠三鍵各有分工:左鍵開關面板、中鍵切下一個專案、右鍵在選到的專案根目錄開一個終端機然後把面板關掉。
八、邊界寫在 README 裡,而不是留給使用者猜
這是我覺得寫 plugin 最該學起來的一段。README 有一節叫 Non-goals,另有一段明講「這個 plugin 會往磁碟寫什麼,除此之外什麼都不寫」:
- 只會改專案
.spectra.yaml裡的五個鍵:locale、tdd、audit、experience、cli_command,而且只在你於面板上改它的時候。 - 只會執行
<cli> update去重新產生該專案.claude/底下的檔案,而且要你按按鈕。 - 其餘全部唯讀,畫面上的東西都是從檔案和
<cli> list/<cli> status讀來的。 new、apply、archive、park、task一律留在 Claude Code 跟終端機裡,面板不做。
不做的還有:改 spec_dir 或 tools、程式碼區塊的語法高亮、Mermaid 圖渲染、檔案變動的即時監看、巢狀專案探索(只掃每個根目錄的直接子目錄)、封存 change 的任務進度條。
移除也寫清楚了。omarchy plugin remove oma.spectra 會刪掉 plugin 資料夾跟 bar 上的圖示,但有兩樣它不碰,因為那是你自己手動加的:~/.config/hypr/bindings.lua 裡那行快捷鍵綁定,和 bar 在你設定專案資料夾時寫進 ~/.config/omarchy/shell.json 的那個條目。
一個外掛在別人機器上跑,講清楚它碰什麼、不碰什麼、移除後留下什麼,比多做一個功能重要。
九、有紀錄的四個坑
一、版本管理器裝的 CLI 在 shell 裡找不到。 面板要執行專案指定的 cli_command,但 Quickshell 這個長時間跑的程序拿到的 PATH 是登入階段的 PATH,不是你 .bashrc 互動區段裡 mise / nvm / asdf 加上去的那份。修法是把 CLI 改成透過 bash -l 執行(v0.3.1 那個 commit)。使用者端的檢查指令:
bash -lc 'command -v spxa'
沒印東西就代表那個 PATH 只存在於互動 shell。解法是把工具啟用搬到 ~/.profile,或用 ~/.config/environment.d/mise.conf 塞進 session PATH 再重新登入。
二、登入 profile 印任何東西都會弄壞 JSON。 因為面板是把 CLI 的 stdout 當 JSON 解析的,登入 profile 印的歡迎訊息會混進去。README 直接寫了一句「Keep the login profile quiet」。這個坑很難自己想到,因為在終端機裡手動跑同一個指令是看不出問題的。
三、validator 不吃 symlink,前面第五節講過。
四、外部 Markdown 要先消毒再丟給渲染器。 最後一個 commit(v0.3.4)做的就是這件事:把 artifact 裡的圖片轉成連結、把 < 轉成 <,再交給 Qt 的 Markdown 引擎。面板渲染的是專案裡的檔案,那些檔案的內容不受 plugin 控制。
十、這篇沒有的東西
- 沒有 QML 程式碼的逐行解說。
Panel.qml有 75 KB,這篇只講它在架構上的位置。 - 沒有效能數字。 沒量過面板開啟時間、掃描大量專案的耗時、記憶體佔用。
- 沒有多機器驗證。 以上是單一 Omarchy Quattro 環境的經驗。
- 上架審核的實際等待時間沒記錄。 只知道送出後由維護者核准。
- marketplace 目前的數字是 21 次瀏覽、0 次指令複製(查詢時間 2026-09-17)。也就是說,還沒有可觀察到的第三方安裝。
十一、為什麼這件事值得花一個下午
三個理由。
第一,Spectra 的紀律本來就要求一件事先 discuss 再 propose 再 apply 再 archive,這條流程的狀態原本散在終端機的滾動輸出裡。把它變成 bar 上一個隨時可開的面板,等於把「我現在在哪一個 change 的哪一步」這個問題從記憶搬到畫面上。
第二,這是一次完整的 Omarchy 外掛端到端演練:manifest 契約、QML 元件、validate、qmllint、IPC、marketplace 上架流程全部走過一遍。我之前寫的 Omarchy 醫院工作站架構草案裡,第一層的「院內殼」就是要靠 Quickshell plugin 去做的,那一層要成立的前提是我自己得先能寫得出一個能上架、能被別人裝、能被驗證的 plugin。現在這個前提成立了。
第三,Spectra.test.js 那支測試檔存在本身就是重點。純函式的部分(設定解析、JSON 形狀、CLI 白名單)抽成 Spectra.js 再單獨測,讓「錯誤降級」那條路徑也能被單元測試蓋到。一個個人下午專案帶測試,看起來過頭,但那正是讓兩個半小時之內連發五個版本還不翻車的原因。
相關頁面
- Omarchy 4 用起來到底怎樣:優點、缺點,還有一張我自己機器產的快捷鍵速查表
- Omarchy 醫院工作站架構草案:Quickshell 院內殼、舊 Delphi 的 AI CI/CD、Rust 自動更新
- 用 Omarchy 同時跑好幾個專案:兩層分工,工作區管我人在哪、Herdr 管誰在等我
- 最佳化開發流程:grill-me 限縮、Spectra 立案、Open Design 出畫面、Codex 對抗式 review
- 我的完整 Agentic Workflow:NanoClaw + Claude + Spectra + Rails,一個人把想法送進 production
- Loop engineering 是什麼?
- FHIR Box × Omarchy 雙系統節點:分散式 FHIR matrix 的可行性分析