Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 3 additions & 6 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"name": "parallel-ai-agents",
"source": "./plugins/parallel-ai-agents",
"description": "v2.21.0: 三層 lens 疊加 built-in → lens pack → user (#29) — lens 可由外部 pai-lenses plugin 與 ~/.claude/pai-lenses/ 疊加,新增一條 lens 從「改 JS + bump plugin」降為「改 CSV」;撞名需顯式 override,報表附 provenance 行。v2.20.1: codex-call 補上 SSE error 事件的 message 提取路徑 (#25)。v2.20.0: first-party codex-pro governance deep-integration (#23). v2.19.0: codexModel/codexEffort contract args (#22, caller-governed cross-model leg). 平行派發任務給多個 AI agent(Claude + Codex),獨立執行後交叉比對結果。Codex 改走直接 HTTP wrapper(bin/codex-call,Swift script)取代 codex exec subprocess,解決 hang 問題且避開 Python 版本飄移",
"version": "2.22.0",
"version": "2.23.0",
"author": {
"name": "Che Cheng"
},
Expand All @@ -25,12 +25,9 @@
},
{
"name": "pai-lenses",
"source": {
"source": "github",
"repo": "PsychQuant/pai-lenses"
},
"source": "./plugins/pai-lenses",
"description": "parallel-ai-agents 的 lens pack(層 ②):以 CSV 提供可疊加的 reviewer lens。裝了之後四個 ensemble skill 的 lens 集合會自動疊上;撞名需在 CSV 標 override 才取代。新增一條 lens = 改 CSV + bump 版本,不必動 plugin 程式碼。",
"version": "0.1.0",
"version": "0.3.0",
"author": {
"name": "Che Cheng"
},
Expand Down
39 changes: 37 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,12 @@ jobs:
# shellcheck 已預裝在 ubuntu-latest runner

- name: shellcheck
run: shellcheck bin/pai-build-diff bin/pai-parse-verdict bin/pai-iter-commit
# 寫死清單的形式本身是問題(下一個新增的 script 預設不會被檢查)—— 追蹤於 #30。
# 這裡先把本 PR 新增的 pai-list-profiles 補進來,不留一支未檢查的新腳本。
run: shellcheck bin/pai-build-diff bin/pai-parse-verdict bin/pai-iter-commit bin/pai-list-profiles

- name: py_compile
run: python3 -m py_compile bin/pai-parse-lens-csv bin/pai-collect-lens-layers
run: python3 -m py_compile bin/pai-parse-lens-csv bin/pai-collect-lens-layers bin/pai-contribute-lenses

# builtin-lenses.csv is generated from the harness PROFILES. It drives nothing at runtime
# (#29 keeps the built-in baseline inside the harness), so a stale catalog is a DOCS defect,
Expand Down Expand Up @@ -98,3 +100,36 @@ jobs:
fi
grep -q '^ok' "$TAP" || { echo "::error::no tests ran (empty glob or bats bail-out)"; exit 1; }
exit "$rc"

pai-lenses-validate:
# 併回本 repo 前,這個檢查住在 PsychQuant/pai-lenses 自己的 validate.yml。
# 併入後該檔落在 plugins/pai-lenses/.github/ —— GitHub 只執行 repo root 的
# .github/workflows/,所以它形同失效。改掛成本 repo 的獨立 job:兩個 plugin
# 的 CI 職責分離,且 scripts/validate.py 本身不動(貢獻者仍可本機跑同一支)。
runs-on: ubuntu-latest
defaults:
run:
working-directory: plugins/pai-lenses
steps:
- uses: actions/checkout@v4
with:
# fetch-depth: 0 是必要的 —— 預設 shallow clone 只有一個 commit,
# 下面的 `git diff <base>...HEAD` 會因為 base SHA 不在本地歷史裡而失敗,
# 而 validate.py 對 git 失敗是「印出略過」而非報錯 —— 檢查會安靜地不存在。
fetch-depth: 0

# 檢查的內容不寫進 workflow 而放在 scripts/validate.py:`run: |` 區塊裡的
# heredoc 一旦把內容放在第 0 欄就會跳出 YAML block scalar,workflow 靜默停止解析。
- name: validate lens pack (version sync + bump-on-change + profile names + CSV shape)
# --base 讓 validate 能判斷「改了 lenses/*.csv 卻沒 bump 版本」——
# 只驗「兩處一致」守不住這個(#33 verify R2 H5/H10)。PR 事件用 base ref,
# push 到 main 時沒有 base,validate 會明確印出略過而非假裝檢查過。
# base SHA 走 env 而非直接內插進 run(workflow-injection 的標準防護形狀)。
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
run: |
if [ -n "$BASE_SHA" ]; then
python3 scripts/validate.py --base "$BASE_SHA"
else
python3 scripts/validate.py
fi
19 changes: 13 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
## 結構

- `.claude-plugin/marketplace.json` — marketplace manifest
- `plugins/parallel-ai-agents/` — 唯一的 plugin(含 skills、`bin/codex-call` wrapper 等所有實作)
- `plugins/parallel-ai-agents/` — 主 plugin(含 skills、`bin/codex-call` wrapper 等所有實作)
- `plugins/pai-lenses/` — **第二個 plugin**:官方 lens pack(三層疊加的層 ②,見 `plugins/parallel-ai-agents/references/lens-layers.md`)。只有 CSV 與 validator,無程式碼
- `README.md` — marketplace 對外介紹
- `LICENSE` — MIT

## 給未來 Claude 的 note

當使用者要求改 ensemble 審閱邏輯、調整 agent 派發、改 Codex wrapper 時:**所有實作都在 `plugins/parallel-ai-agents/` 底下**,root 只保留 marketplace metadata 與整體文件。plugin 內部開發指引見 `plugins/parallel-ai-agents/CLAUDE.md`。
當使用者要求改 ensemble 審閱邏輯、調整 agent 派發、改 Codex wrapper 時:**實作都在 `plugins/parallel-ai-agents/` 底下**,root 只保留 marketplace metadata 與整體文件。plugin 內部開發指引見 `plugins/parallel-ai-agents/CLAUDE.md`。

要**新增或修改一條 lens** 則看目標層:既有 profile 加 lens → `plugins/pai-lenses/lenses/<profile>.csv`;需要新 profile → `plugins/parallel-ai-agents/workflows/ensemble-workflow.js` 的 `PROFILES`。判準與完整流程見 `references/lens-layers.md` 與 `/ensemble-contribute-lenses`。**`references/builtin-lenses.csv` 是 generated 的唯讀投影,編它不改變任何行為。**

## 重要區分

Expand All @@ -22,9 +25,13 @@

## 版本同步(CRITICAL)

bump 版本時兩處必須一致:
bump 版本時兩處必須一致。**這條對每一個 plugin 各自成立**,現在有兩個:

| plugin | plugin.json | marketplace.json entry |
|---|---|---|
| `parallel-ai-agents` | `plugins/parallel-ai-agents/.claude-plugin/plugin.json` | `name: "parallel-ai-agents"` |
| `pai-lenses` | `plugins/pai-lenses/.claude-plugin/plugin.json` | `name: "pai-lenses"` |

- `plugins/parallel-ai-agents/.claude-plugin/plugin.json` 的 `version`
- `.claude-plugin/marketplace.json` 對應 plugin entry 的 `version`
兩者不同步 → 使用者 `/plugin update` 會看到舊版或裝不到新功能,**而且沒有任何錯誤訊息**。

兩者不同步 → 使用者 `/plugin update` 會看到舊版或裝不到新功能
`pai-lenses` 有機械閘門守這條(`plugins/pai-lenses/scripts/validate.py` 的 `check_marketplace_sync`,CI job `pai-lenses-validate` 會跑)。`parallel-ai-agents` 目前沒有 —— 改它的版本時要自己記得兩處都改
56 changes: 46 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,49 @@

Claude Code marketplace,散發 **平行多 AI agent 審閱** plugin。

目前只裝一個 plugin:`parallel-ai-agents`。把任務平行派發給多個 AI agent(Claude teammates + Codex GPT-5.5),各自**獨立執行**後交叉比對,找出共識與盲點。
散發**兩個** plugin。把任務平行派發給多個 AI agent(Claude teammates + Codex),各自**獨立執行**後交叉比對,找出共識與盲點。

| Plugin | 是什麼 |
|---|---|
| **`parallel-ai-agents`** | 主 plugin —— ensemble 審閱的 skill、harness、`bin/` 工具 |
| **`pai-lenses`** | 官方 **lens pack**(三層疊加的層 ②):以 CSV 提供可疊加的 reviewer lens。純資料,無程式碼 |

## 安裝

```bash
/plugin marketplace add PsychQuant/parallel-ai-agents
/plugin install parallel-ai-agents@parallel-ai-agents
/plugin install pai-lenses@parallel-ai-agents # 官方 lens pack(建議一併安裝)
```

安裝後即可使用三個 skill:
> **`pai-lenses` 是選配但建議裝。** 沒裝時 ensemble 只會用 harness 內建的 lens ——
> 不會報錯、不會警告(缺席是靜默的,這是刻意設計),所以**「沒裝」與「裝了但沒生效」
> 從輸出上看不出差別**。報表的 provenance 行會列出實際載入了哪幾層,可據此確認。

安裝後可用的 skill:

| Skill | 用途 |
|-------|------|
| `/ensemble-code-review` | 程式碼/技術文件審閱:4 個 Claude teammates(architecture、correctness、securitydevils-advocate+ Codex 獨立審一遍,最後合成比較表 |
| `/ensemble-code-review` | 程式碼/技術文件審閱:architecture、correctness、security + devils-advocate + Codex 獨立審一遍,最後合成比較表 |
| `/ensemble-academic-review` | 學術論文審閱:methodology、writing、reference verification(che-zotero-mcp 抓幻覺文獻)、number-verification(R/Python 重跑 ground-truth 抓幻覺數字)、devils-advocate。支援 independent/hybrid/mix N 三種模式 |
| `/ensemble-lecture-review` | 教學講義審閱:4 個 Claude teammates 各自獨立審閱講義品質(可帶對應逐字稿 `--srt`) |
| `/ensemble-lecture-review` | 教學講義審閱:內容正確性/可讀性/逐字稿覆蓋率(可帶 `--srt`) |
| `/ensemble-compose` | 自由組合:跨 profile 挑 lens + 自訂 reviewer(`--include` / `--lens` / `--lens-file`)|
| `/ensemble-contribute-lenses` | 把本機 `~/.claude/pai-lenses/` 的 lens 送回公共層並開 PR |
| `/ensemble-eval` | **dev 工具**:對埋好缺陷的 fixture 跑 K 次真 ensemble,量偵測率 |

## 三層 lens 疊加

reviewer 的 lens 由三層疊出來,順序即優先序:

| 層 | 來源 | 誰能改 |
|---|---|---|
| ① built-in | 主 plugin 的 `PROFILES`(harness 內) | 改 code + 發版 |
| ② lens pack | `pai-lenses` 的 `lenses/<profile>.csv` | 改 CSV + bump 版本 |
| ③ user | `~/.claude/pai-lenses/<profile>.csv` | 直接編,立即生效、不必發布 |

撞名時預設 first-wins,CSV 標了 `override` 才取代。寫在層 ③ 的 lens 想回流上游,跑
`/ensemble-contribute-lenses`。完整契約見
[`references/lens-layers.md`](plugins/parallel-ai-agents/references/lens-layers.md)。

## 為什麼

Expand All @@ -30,14 +57,23 @@ Claude Code marketplace,散發 **平行多 AI agent 審閱** plugin。
├── .claude-plugin/
│ └── marketplace.json # marketplace manifest
├── plugins/
│ └── parallel-ai-agents/ # 唯一的 plugin
│ ├── parallel-ai-agents/ # 主 plugin
│ │ ├── .claude-plugin/
│ │ │ └── plugin.json
│ │ ├── bin/
│ │ │ ├── codex-call # Swift script:直接 HTTP 呼叫 Codex
│ │ │ ├── pai-list-profiles # 查 PROFILES 真源
│ │ │ └── pai-contribute-lenses # 層 ③ 的回流流程
│ │ ├── skills/ # 六個 skill
│ │ ├── workflows/ # ensemble harness
│ │ ├── references/ # lens-layers 契約、built-in lens catalog
│ │ ├── CHANGELOG.md
│ │ └── CLAUDE.md # plugin internal guide
│ └── pai-lenses/ # 官方 lens pack(層 ②)
│ ├── .claude-plugin/
│ │ └── plugin.json
│ ├── bin/
│ │ └── codex-call # Swift script:直接 HTTP 呼叫 Codex
│ ├── skills/ # 三個 ensemble-review skill
│ ├── CHANGELOG.md
│ └── CLAUDE.md # plugin internal guide
│ ├── lenses/ # <profile>.csv
│ └── scripts/validate.py # CI 閘門
├── README.md # 本檔案:marketplace 說明
├── LICENSE # MIT
└── .gitignore
Expand Down
15 changes: 15 additions & 0 deletions plugins/pai-lenses/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"name": "pai-lenses",
"version": "0.3.0",
"description": "parallel-ai-agents 的 lens pack:以 CSV 提供可疊加的 reviewer lens(層 ②)。新增一條 lens = 改 CSV + bump 版本,不必動 plugin 程式碼。",
"author": {
"name": "Che Cheng"
},
"category": "development",
"keywords": [
"parallel-ai-agents",
"ensemble-review",
"lenses",
"code-review"
]
}
1 change: 1 addition & 0 deletions plugins/pai-lenses/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.DS_Store
21 changes: 21 additions & 0 deletions plugins/pai-lenses/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2026 Che Cheng

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
113 changes: 113 additions & 0 deletions plugins/pai-lenses/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# pai-lenses

[`parallel-ai-agents`](https://github.com/PsychQuant/parallel-ai-agents) 的 **lens pack** —— 用 CSV 提供可疊加的 reviewer lens。

裝了這個 plugin 之後,`ensemble-code-review` / `ensemble-academic-review` / `ensemble-lecture-review` /
`ensemble-compose` 的 lens 集合會自動疊上這裡的內容。

## 為什麼有這個 plugin

built-in lens 的真源是 `plugins/parallel-ai-agents/workflows/ensemble-workflow.js` 的 `PROFILES` 物件。
動它 = 改程式碼 → bump plugin 版本 → 同步 marketplace。結果是 lens 從 2026-06 起一條都沒新增過 ——
**不是沒人想改,是改一條的成本太高**。

把 lens 抽成獨立 plugin 之後:

| | 之前 | 現在 |
|---|---|---|
| 新增一條 lens | 改 JS + bump 主 plugin | 改 CSV + bump 本 plugin |
| 量測條件可追溯 | plugin 版本號會被無關改動污染 | lens pack 版本就是 lens 的版本座標 |

第二點對 `eval/fixtures/` 的偵測率數字尤其重要:lens 是量測儀器,前後用不同 lens 量到的數字不可比。
報表的 provenance 行會印出本 pack 的版本。

### 為什麼**不**是獨立 repo(#33 的更正)

本 pack 曾短暫是獨立的 `PsychQuant/pai-lenses` repo,理由是「降低外部貢獻的出口成本」。
那個理由**不成立**,而且反過來是障礙:

- 三層疊加的層 ③(`~/.claude/pai-lenses/`)要回流時,貢獻者得先判斷該進層 ① 還是層 ②,
而那兩層當時分屬**兩個 repo** —— 判定與開 PR 都跨 repo
- 兩層在同一棵樹上,`/ensemble-contribute-lenses` 才有辦法自動判定目標層並在**一個 PR** 裡完成

舊 repo 已封存(README 指向這裡)。層 ①②③ 的完整契約見
[`references/lens-layers.md`](../parallel-ai-agents/references/lens-layers.md)。

## 三層疊加

| 層 | 來源 | 給誰 |
|---|---|---|
| ① built-in | `parallel-ai-agents` 的 `PROFILES` | 所有人的 baseline |
| ② **lens pack(本 plugin)** | `lenses/<profile>.csv` | 裝了這個 plugin 的人 |
| ③ user | `~/.claude/pai-lenses/<profile>.csv` | 只有你自己 |

順序即優先序。撞名時**預設 first-wins**(先到的勝),只有標了 `override` 的才取代。

## CSV 格式

一個 profile 一個檔,profile 由**檔名**決定(`lenses/code.csv` → `code` profile)。

```csv
key,focus,needsSrt,override
perf,"檢查每個 hot path 的時間複雜度與不必要的重算",,
security,"(取代內建的 security lens)……",,true
```

| 欄 | 必填 | 說明 |
|---|---|---|
| `key` | ✅ | lens 識別名。與其他層同名時觸發撞名判定 |
| `focus` | ✅ | 該 reviewer 的檢查清單。**含逗號要 quote** —— 這是長 prose,不是短標籤 |
| `needsSrt` | — | truthy 時標記此 lens 需要逐字稿(`lecture` profile 用) |
| `override` | — | truthy 時**取代**同 key 的既有 lens |

truthy 判準:`1` / `true` / `yes`(不分大小寫)。空白或省略 = false。

### `override` 的語意

`override` 是「**我要取代那一條**」,不是「我比較重要」。

不標記 = 純新增;撞名時你的那條會被忽略(並在報表警告)。標記則會讓一條經過調校的 built-in lens
**消失**,所以請在 PR 描述裡寫清楚為什麼原本那條不夠用。

## 怎麼寫一條好 lens

看 `parallel-ai-agents` 的 [`references/builtin-lenses.csv`](https://github.com/PsychQuant/parallel-ai-agents/blob/main/plugins/parallel-ai-agents/references/builtin-lenses.csv)
—— 那是內建 lens 的唯讀 catalog,可以直接當範本。共同特徵:

- **一個 lens 只審一件事**。範圍越窄,reviewer 越不會滑回泛泛而談
- **focus 是逐點檢查清單**,不是一句話的期望。`(1)…(2)…(3)…` 的形式最有效
- **明講要用工具查證**(「用 Read/Grep 實際打開檔案核對」),否則模型傾向只讀眼前的內容
- **寫下這條 lens 的失敗模式**(「一段寫得很有說服力卻與程式碼不符的說明,比沒有註解更危險」)——
reviewer 需要知道它在防什麼

## 貢獻

**本機已經寫好 lens(層 ③)** → 跑 `/parallel-ai-agents:ensemble-contribute-lenses`。
它會掃 `~/.claude/pai-lenses/*.csv`、判定每條該進層 ① 還是層 ②、產出變更並開 PR。

**手動貢獻**:

1. Fork `PsychQuant/parallel-ai-agents` → 改 `plugins/pai-lenses/lenses/<profile>.csv`
2. **bump 兩處 version**:本 plugin 的 `.claude-plugin/plugin.json` **與** repo root
`.claude-plugin/marketplace.json` 的 `pai-lenses` entry。只改一處 → merge 後使用者收不到,
**且無錯誤訊息**(CI 的 `check_marketplace_sync` 會擋)
3. PR 描述說明:這條 lens 抓什麼、為什麼既有的抓不到、若標了 `override` 為何要取代
4. **檔名必須是既有 profile**(`bin/pai-list-profiles` 查得到的)。需要新 profile 就不是
改這裡 —— CSV 描述不了 profile 級的 `title`/`daFocus`/`codexDefault`,要改 `PROFILES`(層 ①)

CI(`pai-lenses-validate`)會檢查:semver `version`、marketplace 版本一致、檔名是既有 profile、
CSV 可解析且每檔至少一條 lens、以及 `key` 不是誤複製進來的註解列。

## 硬性前提:`plugin.json` 必須有 `version`

Claude Code 把 plugin 解到 `~/.claude/plugins/cache/<marketplace>/<plugin>/<version>/`。
`plugin.json` 缺 `version` 時,目錄名會退回 **git commit SHA**([官方文件](https://code.claude.com/docs/en/plugin-marketplaces)
的 fallback)或 `unknown`(實測在 `claude-plugins-official` 的幾個 plugin 上看過)。兩者都不是 semver,
`parallel-ai-agents` 的 semver glob 就定位不到,本 pack 等同沒裝 —— 但報表會出現 `unversioned` 警告,
不會靜默。CI 有守這一條。

順帶一提,這也是為什麼**每次改 lens 都要 bump 版本**:版本沒變,使用者端不會收到更新。

## License

MIT
Loading
Loading