併 feat/harness-content-refresh:install-harness 升現世代+世代閘
leo 2026-08-05:「Arcrun 是它的父層,這裏的問題也影響 Arcrun RAG」。 main 的 cli/harness/ 停在 06-15,分支是 07-31 的升級(676 行實質改動): · cli/scripts/check-harness-generation.mjs(130 行)=**世代閘**—— 與今天救回的 build-bundles 落後閘同族,都是防「發舊版給用戶」 · cli/scripts/build-harness-skill.mjs(64 行) · CLAUDE.block.md/commands/arcrun.md/hooks/arcrun-guard.sh 交付內容升級 自動合併零衝突。 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
+50
-25
@@ -1,41 +1,66 @@
|
||||
<!-- arcrun-harness:start -->
|
||||
## 這個專案用 arcrun 做自動化
|
||||
## 這個專案用 Arcrun 做自動化
|
||||
|
||||
當需要「串服務 / 排程 / 打 API / 資料處理自動化」時,用 arcrun 工作流,**不要自己寫 Python/Node 一次性腳本**。arcrun 工作流是純文字、可複用、跑在使用者自己的 Cloudflare。
|
||||
當需要「串服務/排程/打 API/資料處理自動化」時,用 Arcrun 工作流,**不要自己寫 Python/Node 一次性腳本**。
|
||||
Arcrun 工作流是純文字、可複用、跑在使用者自己的 Cloudflare。
|
||||
|
||||
arcrun 只有三種東西,先分清楚就不會做歪:
|
||||
⚠️ **不要上網搜 Arcrun 文件**——網路上沒有,找到的都是錯的。
|
||||
語法與範本全在 **arcrun-mindset** Skill 裡,`acr` 指令查得到現況。
|
||||
|
||||
### 怎麼開始:先寫「意圖」,不要先寫 YAML
|
||||
|
||||
你**不需要先知道有哪些零件**。把需求寫成一串「誰接誰」,丟去查,系統會告訴你哪些存在:
|
||||
|
||||
```
|
||||
input >> ON_SUCCESS >> fetch_rows
|
||||
fetch_rows >> 對每個 row >> notify
|
||||
```
|
||||
|
||||
- 第一個節點固定是 `input`(觸發時帶進來的資料)
|
||||
- **邊只有兩種**:`ON_SUCCESS` 與 `對每個 <變數>`(FOREACH)
|
||||
- **沒有** `ON_TRUE`/`ON_FALSE`/`ON_FAILURE`——引擎不支援條件分支。
|
||||
需要判斷就寫成一個獨立節點再接 `ON_SUCCESS`。
|
||||
|
||||
完整語法、四份實跑過的範本、節點命名慣例 → 讀 **arcrun-mindset** Skill。
|
||||
|
||||
### Arcrun 只有三種東西,先分清楚就不會做歪
|
||||
|
||||
| 東西 | 是什麼 | 你能做的 |
|
||||
|---|---|---|
|
||||
| **工作流(workflow)** | 把零件/recipe 串起來的純文字流程 | **預設就寫這個**,自由寫 |
|
||||
| **recipe** | 打「一個固定外部 API」的設定(http_request + endpoint/header/body 模板) | 自由寫、**而且該投稿**(見下) |
|
||||
| **零件(component)** | WASM 程式(流程控制 / 資料處理 / http_request / auth),固定一小套 | **你不自製**,由維護者管,走 GitHub PR |
|
||||
| **工作流(workflow)** | 把零件/recipe 串起來的純文字流程 | **預設就寫這個**,自由寫 |
|
||||
| **recipe** | 打「一個固定外部 API」的設定(endpoint/header/body 模板) | 自由寫、**而且該投稿** |
|
||||
| **零件(component)** | WASM 程式(流程控制/資料處理/`http_request`/auth),固定一小套 | **你不自製**,走 PR |
|
||||
|
||||
> **一句話判準**:打一個固定外部 endpoint → 寫 **recipe**;流程控制 / 資料處理 / 通用 HTTP → 用既有**零件**;其他 → 寫**工作流**串起來。
|
||||
> **一句話判準**:打一個固定外部 endpoint → 寫 **recipe**;流程控制/資料處理/通用 HTTP → 用既有**零件**;其他 → 寫**工作流**串起來。
|
||||
>
|
||||
> ⚠️ 常見錯誤:把 `telegram_send`/`gmail_send`/`kbdb_get` 當**零件**寫。它們是 **recipe**。
|
||||
|
||||
### 三個最常踩的坑(讀懂這三條,就不會像前人誤解四輪)
|
||||
### 查詢回 `not_found` 時:兩條路,都不准改寫成 code
|
||||
|
||||
**① recipe 是公共資產,發現缺的就自己做一個投稿,不必問人。**
|
||||
`acr recipe push` = 把 recipe **投稿到公共庫**,不是存私人腳本。公開/私有只是 recipe 的一個屬性(同一身份可有公私版本並存),不是兩條不同的路。
|
||||
→ 所以「想打某個 API 但沒有現成 recipe」時,**正解是自己寫一個 recipe 並 push 上去**(會 inject credential、push 時幫你檢查打不打得通)。這是被鼓勵的,別誤以為要自用、不上傳。
|
||||
查詢會誠實回報(兩庫都查過才點名):
|
||||
|
||||
**② 缺一個能力 → 去補 API(cypher endpoint),不准用 recipe / 多步工作流拼裝出來。**
|
||||
判準口訣:**「這段邏輯換一個介面(CLI→MCP)要不要重寫?要重寫 → 它是『能力』,該長在 API。」**
|
||||
- ❌ 缺 `upsert` → 在工作流裡拼「先查、沒有再建」、或寫個 recipe 假裝補上。
|
||||
- ✅ 缺 `upsert` → 去 API 加一個 `upsert` endpoint,CLI/MCP/recipe 都呼叫它。
|
||||
recipe 只負責「打一個固定外部 API」這件單純事;它不是用來補 arcrun 自己缺的能力的。缺能力就回報 / 補在 API,不要繞。
|
||||
| status | 意思 | 你該做什麼 |
|
||||
|---|---|---|
|
||||
| `found` / `resolved` | 有現成的可用 | **只填 payload** |
|
||||
| `not_found` | 零件庫與 recipe 庫都沒有 | 照回應的 `suggestion` 走(見下兩條路),並看 `similar_components`/`similar_recipes` 有沒有能直接用的 |
|
||||
| `unknown` | 查不到 registry(未部署/網路失敗) | **不代表不存在**,別據此改寫成 code |
|
||||
|
||||
**③ 已經有自製零件(例如 mira 的那幾個)→ 讓它退場,別再加新的。**
|
||||
你不該自製零件;既有的自製零件要往這三條退場:
|
||||
- `claude_api` 之類「工作流回頭叫 LLM」→ **刪掉**,需要 AI 判斷時是**你(操盤的 CC)自己做**,再叫工作流做確定性的下一步。arcrun 是 AI 用的工具,不是工具回頭用 AI。
|
||||
- `kbdb_*` 之類資料存取 → 改走已備好的 **`acr kbdb` 薄殼 / `kbdb_*` MCP 工具**(template + record 模型),不要當零件。
|
||||
- 純粹打某個固定外部 API 的假零件 → **改寫成 recipe** 投稿(見①)。
|
||||
- **缺外部 API** → **自己寫一個 recipe** 並 `acr recipe push`(幾行 YAML,不用部署 Worker、不用寫程式)。
|
||||
recipe 是公共資產,發現缺的就補一個投稿,不必問人。
|
||||
- **缺計算能力**(加解密/壓縮這類純運算) → 投稿**零件 PR**(要人類確認,罕見)。
|
||||
|
||||
🔴 **查不到就改寫成 `code` 節點 =「腹語術」**(表面用 Arcrun、實際全寫 JS)。
|
||||
`code` 只用於**局部整形**(例:剝掉 LLM 回應的雜訊、切段落),不用來取代零件與流程控制。
|
||||
> 實錄:每一個寫進 `code` 的 `if` 都是沒被測過的新 bug;零件的價值是「被測過 1000 次」,寫進 code 就歸零。
|
||||
|
||||
### 其餘鐵律
|
||||
|
||||
- **先查能力再動手**:`acr parts`(看可用零件)、`acr auth-recipe list`(看支援的認證服務)、`acr kbdb`(資料存取)。
|
||||
- **暴露資料要人類同意**:部署對外 webhook / push recipe 會讓東西可被外部呼叫 → 停下來讓使用者明示同意,不替他決定公開。
|
||||
- **誠實**:沒打通就誠實說(缺 credential 標「未驗收:缺 X」),不假裝成功;完成以 HTTP 2xx / trace 為證,不口頭宣布。
|
||||
- **先查能力再動手**:`acr search <關鍵字>`(一次掃零件/recipe/auth-recipe/workflow)、
|
||||
`acr parts`(零件)、`acr recipe list`(recipe)、`acr auth-recipe list`(支援的認證)。
|
||||
- **需要 AI 判斷時你自己做**,不要讓工作流回頭呼叫 LLM。Arcrun 是 AI 用的工具,不是工具回頭用 AI。
|
||||
- **金鑰只拿名字**:定義裡只寫 `{{credential.<名字>}}`,真身絕不寫進 workflow/recipe 檔案。
|
||||
- **暴露資料要人類同意**:`acr push`/`acr recipe push` 會讓東西可被外部呼叫 → 停下來讓使用者明示同意,不替他決定公開。
|
||||
- **誠實**:沒打通就誠實說(缺 credential 標「未驗收:缺 X」),不假裝成功;完成以 HTTP 2xx/trace 為證,不口頭宣布。
|
||||
|
||||
開始前讀 **arcrun-mindset** Skill(世界觀)。使用者技術細節交給你,CLI 操作你來做。
|
||||
開始前讀 **arcrun-mindset** Skill(意圖語法+範本+世界觀)。使用者技術細節交給你,CLI 操作你來做。
|
||||
<!-- arcrun-harness:end -->
|
||||
|
||||
@@ -1,26 +1,60 @@
|
||||
# 用 arcrun 完成這個自動化需求
|
||||
# 用 Arcrun 完成這個自動化需求
|
||||
|
||||
使用者想做一個自動化。你的任務:用 arcrun 做出來,全程不要讓使用者自己寫程式。
|
||||
使用者想做一個自動化。你的任務:用 Arcrun 做出來,全程不要讓使用者自己寫程式。
|
||||
|
||||
⚠️ **不要上網搜 Arcrun 文件**(網路上沒有)。先讀 **arcrun-mindset** Skill,再用 `acr` 指令查現況。
|
||||
|
||||
## 鐵則
|
||||
- **用 arcrun 工作流 / recipe,絕不自己寫 Python/Node 腳本。** 使用者選 arcrun 就是不想要一次性腳本。
|
||||
- 打外部 API → 寫 recipe(`acr recipe push`),不自刻 HTTP client。
|
||||
- 不自製零件(WASM)—— 零件由 arcrun 維護。你能用的是現有零件 + recipe + 工作流。
|
||||
- 需要 AI 判斷時你自己做,不要讓工作流回頭呼叫 LLM。
|
||||
- **用 Arcrun 工作流/recipe,絕不自己寫 Python/Node 腳本。** 使用者選 Arcrun 就是不想要一次性腳本。
|
||||
- **打外部 API → 寫 recipe**(`acr recipe push`),不自刻 HTTP client。缺 recipe 就自己補一個,不必問人。
|
||||
- **不自製零件(WASM)**——零件由 Arcrun 維護。你能用的是現有零件 + recipe + 工作流。
|
||||
- **需要 AI 判斷時你自己做**,不要讓工作流回頭呼叫 LLM。
|
||||
- 🔴 **查不到零件就改寫成 `code` 節點 = 腹語術**,禁止。缺 API 寫 recipe、缺能力投稿零件。
|
||||
|
||||
## 步驟
|
||||
1. 先讀 **arcrun-mindset** Skill(世界觀 + 資源去哪取)。
|
||||
2. 跑 `acr parts` 看零件、`acr auth-recipe list` 看支援的認證。**先查再動手。**
|
||||
3. 把使用者需求拆成工作流(哪些零件、什麼順序、什麼條件),寫成 `.yaml`。
|
||||
4. 需要 credential(API key / token)→ 用 `acr auth-recipe scaffold <service>` 看要哪些,
|
||||
明確告訴使用者去哪取得、怎麼 `acr creds push`。
|
||||
5. `acr validate` 通過後 `acr push` 部署,告訴使用者 webhook URL / 怎麼 `acr run`。
|
||||
6. 完成給客觀證據(HTTP 2xx / trace),不要只說「做好了」。
|
||||
|
||||
## 遇到要暴露資料(對外 webhook)
|
||||
### 1. 先寫「意圖」,不要先寫 YAML
|
||||
把使用者的需求寫成一串「誰接誰」(**不必是真實零件名**,用你想得到的名字即可):
|
||||
|
||||
```
|
||||
input >> ON_SUCCESS >> fetch_rows
|
||||
fetch_rows >> 對每個 row >> notify
|
||||
```
|
||||
|
||||
- 第一個節點固定是 `input`
|
||||
- 邊只有 `ON_SUCCESS` 與 `對每個 <變數>`(**沒有** `ON_TRUE`/`ON_FALSE`/`ON_FAILURE`)
|
||||
- 需要判斷 → 寫成獨立節點(例 `check_amount`)再接 `ON_SUCCESS`
|
||||
|
||||
語法細節、四份實跑過的範本、節點命名慣例 → **arcrun-mindset** Skill。
|
||||
|
||||
### 2. 丟去查,讓系統告訴你有什麼
|
||||
`acr search <關鍵字>` 一次掃零件/recipe/auth-recipe/workflow;
|
||||
或把意圖串丟 `/cypher/search`,逐節點拿 `found` / `resolved` / `not_found` / `unknown`。
|
||||
|
||||
- `found`/`resolved` → **只填 payload**
|
||||
- `not_found` → 照回應的 `suggestion` 走(缺 API 寫 recipe、缺計算能力投稿零件),
|
||||
並看 `similar_components`/`similar_recipes` 有沒有現成能用的
|
||||
- `unknown` → **不代表不存在**,別據此改寫成 code
|
||||
|
||||
### 3. 把意圖變成 workflow YAML
|
||||
節點填上查到的真實零件/recipe + payload。
|
||||
需要 credential 時:`acr auth-recipe scaffold <service>` 看要哪些,明確告訴使用者去哪取得、怎麼 `acr creds push`。
|
||||
🔑 定義裡只寫 `{{credential.<名字>}}`,**真身絕不寫進檔案**。
|
||||
|
||||
### 4. 驗證 → 部署 → 給證據
|
||||
```bash
|
||||
acr validate <workflow>.yaml # 先驗
|
||||
acr push <workflow>.yaml # 部署(暴露動作,見下)
|
||||
acr run <workflow> # 觸發一次
|
||||
acr logs <workflow> # 看執行紀錄
|
||||
```
|
||||
完成要給客觀證據(HTTP 2xx/trace),不要只說「做好了」。
|
||||
|
||||
## 遇到要暴露資料(對外 webhook/recipe 投稿)
|
||||
停下來,明確告訴使用者「這會讓 X 可被外部呼叫」,要他同意。不要替他決定公開。
|
||||
非互動環境下把完整指令印給使用者自己貼上跑。
|
||||
|
||||
## 還沒設定好 arcrun?
|
||||
## 還沒設定好 Arcrun?
|
||||
若 `acr` 指令不存在或還沒 `acr init`:先帶使用者完成前置設定
|
||||
(裝 CLI → 拿 Cloudflare 帳號的兩串憑證 → `acr init --self-hosted`)。
|
||||
拿 Cloudflare 憑證時用白話照抄式引導,不要對使用者講 KV / Worker / R2 等術語。
|
||||
|
||||
@@ -66,7 +66,7 @@ if echo "$CMD" | grep -qE "acr (push|recipe push)\b"; then
|
||||
if echo "$EXEC_PART" | grep -qE "(^|[;&|][[:space:]]*)acr[[:space:]]+(push|recipe[[:space:]]+push)\b"; then
|
||||
if [ ! -t 0 ] && [ "${ARCRUN_HUMAN_CONFIRMED:-}" != "1" ]; then
|
||||
block "在非互動環境自動執行暴露動作(acr push / recipe push 會讓東西可被外部呼叫)" \
|
||||
"交人類在終端機執行(真 TTY 會自動放行)。可把指令完整複製給使用者貼上自己跑:\`acr push <你的 workflow.yaml>\`。或使用者先在對話明示同意後親自於終端機執行。不要替使用者決定公開。"
|
||||
"交人類在終端機執行(真 TTY 會自動放行)。可把指令完整複製給使用者貼上自己跑:\`acr push <你的 workflow.yaml>\`。或使用者先在對話明示同意後親自於終端機執行。不要替使用者決定公開。(部署前的正路見 arcrun-mindset Skill:先 \`acr validate\`)"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
@@ -76,15 +76,29 @@ fi
|
||||
if echo "$CMD" | grep -qE "(^|[;&| ])(python3?|node)[ ]+[^ ]+\.(py|js|mjs|ts)\b"; then
|
||||
# 排除明顯的測試 / 既有工具呼叫(pytest / npm test / jest 等)降低誤判
|
||||
if ! echo "$CMD" | grep -qE "(pytest|jest|vitest|npm (run )?test|mocha|\btest_)"; then
|
||||
remind "偵測到用 python/node 跑腳本。這專案用 arcrun,串服務/自動化不要自刻一次性腳本。" \
|
||||
"先跑 \`acr parts\` 看有哪些零件,把需求寫成 workflow.yaml 用 \`acr run\`。若這確實不是自動化(例如跑測試/別的工具),忽略本提醒。"
|
||||
remind "偵測到用 python/node 跑腳本。這專案用 Arcrun,串服務/自動化不要自刻一次性腳本。" \
|
||||
"讀 arcrun-mindset Skill,先把需求寫成「意圖」串(\`input >> ON_SUCCESS >> <下一步>\`,邊只有 ON_SUCCESS 與「對每個 X」),再用 \`acr search <關鍵字>\` 查哪些零件/recipe 存在,最後才寫 workflow.yaml → \`acr validate\` → \`acr run\`。若這確實不是自動化(例如跑測試/別的工具),忽略本提醒。"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── 提醒(不硬擋):自寫打固定 API 的 script,而非 recipe ──────────────
|
||||
if echo "$CMD" | grep -qE "(curl|fetch|requests\.(get|post)|axios).*https?://"; then
|
||||
remind "偵測到自己打外部 API。arcrun 裡「打固定 endpoint」應寫成 recipe,不自刻 HTTP 呼叫。" \
|
||||
"用 \`acr recipe push\` 把這個 API 包成 recipe,workflow 裡用 component 引用它。見 arcrun-mindset Skill。"
|
||||
remind "偵測到自己打外部 API。Arcrun 裡「打固定 endpoint」應寫成 recipe,不自刻 HTTP 呼叫。" \
|
||||
"先 \`acr recipe search <服務名>\` 看有沒有現成的;沒有就自己寫幾行 YAML(canonical_id/endpoint/method/auth_service)用 \`acr recipe push\` 投稿,workflow 裡用 \`http_request\` + 該 recipe 引用它。缺 recipe 就自己補,不必問人。寫法見 arcrun-mindset Skill。"
|
||||
fi
|
||||
|
||||
# ── 提醒(不硬擋):把 code 節點當成缺零件的替代品(「腹語術」)──────────────
|
||||
# 查詢回 not_found 就改寫成 code = 表面用 Arcrun、實際全寫 JS。這是現世代最常見的走歪。
|
||||
if [ "$TOOL" = "Write" ] || [ "$TOOL" = "Edit" ] || [ "$TOOL" = "MultiEdit" ]; then
|
||||
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
|
||||
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // .tool_input.new_string // ""')
|
||||
if echo "$FILE" | grep -qE '\.(ya?ml)$' && echo "$CONTENT" | grep -qE 'component:[[:space:]]*["'"'"']?code\b'; then
|
||||
# 只在 code 內容看起來在做流程控制/取代零件時提醒(含 if/for/fetch),單純整形不吵
|
||||
if echo "$CONTENT" | grep -qE '\b(if[[:space:]]*\(|for[[:space:]]*\(|fetch\(|await[[:space:]]+fetch)'; then
|
||||
remind "workflow 裡的 \`code\` 節點含流程控制/HTTP 呼叫——這可能是「腹語術」(表面用 Arcrun、實際全寫 JS)。" \
|
||||
"\`code\` 只用於局部整形(例:剝掉 LLM 回應的雜訊、切段落)。缺外部 API → 寫 recipe(\`acr recipe push\`);缺計算能力 → 投稿零件 PR;要判斷 → 寫成獨立節點接 \`ON_SUCCESS\`(引擎沒有條件邊)。每個寫進 code 的 if 都是沒被測過的新 bug。見 arcrun-mindset Skill。"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exit 0
|
||||
|
||||
@@ -10,6 +10,16 @@
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"matcher": "Write|Edit|MultiEdit",
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": ".claude/hooks/arcrun-guard.sh",
|
||||
"timeout": 5
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,78 +1,245 @@
|
||||
---
|
||||
name: arcrun-mindset
|
||||
description: >-
|
||||
arcrun 的世界觀 — 用 arcrun 開發自動化時的預設心態 + 資源去哪取。當你(AI 操盤手)要在
|
||||
arcrun 上做任何事(串服務、處理資料、認證、把東西開放給人用)前讀這個。它讓你做出「方向對」
|
||||
的選擇、知道資源在哪,避免技術上能跑但架構上錯、或自己重刻 arcrun 已有的東西。
|
||||
在 Arcrun 上做任何事時使用(用戶說「幫我用 Arcrun 做 X」「用 arcrun 寫一個工作流」「把 X 自動化」)。
|
||||
Arcrun 是跑在 Cloudflare 上的工作流引擎——你用 `>>` 寫「意圖」,系統告訴你有哪些現成零件與 recipe,
|
||||
你只填 payload,不必自己寫程式。**不要上網搜 Arcrun 文件**(網路上沒有),也不要自己猜 YAML 格式:
|
||||
先讀本 skill,再用 `acr` 指令(或 MCP 工具)查現成零件。
|
||||
涵蓋:意圖工作流語法、四份實跑過的範本、零件 vs recipe 的分別、缺件的兩條路、已知的坑。
|
||||
---
|
||||
|
||||
# arcrun mindset(給 AI 操盤手)
|
||||
# Arcrun:怎麼寫意圖工作流
|
||||
|
||||
你在 arcrun 上幫使用者開發自動化。arcrun 很簡單,簡單到你常會把它想複雜、或退回自己熟悉的
|
||||
Python/Node 自刻。這份幫你在岔路上選對方向,並告訴你資源在哪。
|
||||
> **你已經配備 Arcrun**(此專案裝了 `acr` CLI,可能另有 `arcrun_*` MCP 工具)。
|
||||
> **別上網找文件**——網路上沒有 Arcrun 的文件,找到的都是錯的。答案都在本 skill 與 `acr` 指令裡。
|
||||
|
||||
## 先做這三件(照順序)
|
||||
|
||||
1. `acr whoami` — 確認連到哪個帳號(**勿自行 curl 猜帳號 URL**)
|
||||
2. 讀本 skill 下面的語法與範本 → 寫出 `>>` 意圖
|
||||
3. `acr parts`/`acr recipe list`(或 `acr search <關鍵字>` 一次掃全部)— 確認零件與 recipe 真的存在
|
||||
|
||||
**卡住時**:`acr search <關鍵字>` 跨類搜尋;有 MCP 就 `arcrun_get_skill('INDEX')` 拿全館導航。
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句話世界觀
|
||||
|
||||
**arcrun 裡幾乎所有東西都是工作流(workflow)。** 工作流 = 一張紙,寫「用哪些零件、什麼順序、什麼條件」。
|
||||
你大部分時間在寫紙、改紙,不是在造新零件、也不是自己寫腳本。
|
||||
**Arcrun 裡幾乎所有東西都是工作流(workflow)。** 工作流 = 一張紙,寫「用哪些零件、什麼順序、什麼條件」。
|
||||
你大部分時間在**寫紙、改紙**,不是在造新零件、也不是自己寫腳本。
|
||||
|
||||
**Arcrun 只有三種東西,先分清楚就不會做歪:**
|
||||
|
||||
| 東西 | 是什麼 | 你能做的 |
|
||||
|---|---|---|
|
||||
| **工作流(workflow)** | 把零件/recipe 串起來的純文字流程 | **預設就寫這個**,自由寫 |
|
||||
| **recipe** | 打「一個固定外部 API」的設定(endpoint/header/body 模板) | 自由寫、**而且該投稿**(缺就自己補) |
|
||||
| **零件(component)** | WASM 程式(流程控制/資料處理/`http_request`/auth),固定一小套 | **你不自製**,走 PR 由維護者管 |
|
||||
|
||||
> **一句話判準**:打一個固定外部 endpoint → 寫 **recipe**;流程控制/資料處理/通用 HTTP → 用既有**零件**;其他 → 寫**工作流**串起來。
|
||||
|
||||
---
|
||||
|
||||
## 1. 工作流是 default,不要退回自己寫 Python
|
||||
<!-- 以下正文由 registry/skills/write_intent_workflow.md 於建置期複製而來(單一真相源)。
|
||||
不要直接編輯本段——改 registry 那份,然後跑 `npm run build:harness`。 -->
|
||||
|
||||
使用者選 arcrun,就是不要「每次重刻、跑完即丟」的腳本。所以你的預設順序:
|
||||
## 1. 意圖工作流的語法
|
||||
|
||||
1. **先想能不能用工作流做**(串現有零件 / recipe + 流程控制)。99% 可以。
|
||||
2. 要打的服務有 HTTP API、但沒有對應 recipe → **寫一個 recipe**(http_request + 固定設定 YAML,不用部署、不用審核)。
|
||||
3. **只有**封閉純邏輯(流程控制 / 資料處理)、現有零件不夠、且值得全 arcrun 重用 → 才考慮零件(而零件走 PR,不是你現在做)。
|
||||
一串「誰接誰」,每行一個關係:
|
||||
|
||||
> 典型走歪:「我先用 Python 測一下」。停。使用者要的是 arcrun 工作流。先 `acr parts` 看有什麼,用工作流串。
|
||||
```
|
||||
<節點A> >> <邊> >> <節點B>
|
||||
```
|
||||
|
||||
## 2. 資源去哪取(不要自己重造 arcrun 已有的)
|
||||
- **節點**=一個步驟。用你想得到的名字(中文可以),**不必是真實零件名**
|
||||
- **邊**=什麼情況下往下走
|
||||
|
||||
## 2. 邊只有兩種(真範本裡出現過的)
|
||||
|
||||
| 邊 | 意思 | 真例 |
|
||||
|---|---|---|
|
||||
| `ON_SUCCESS` | 上一步成功就往下 | `input >> ON_SUCCESS >> prep` |
|
||||
| `對每個 <變數>` | 上一步產出清單,逐項處理(FOREACH)| `parse_card >> 對每個 block >> post_block` |
|
||||
|
||||
⚠️ **不要寫 `ON_FAILURE`/`ON_TRUE`/`ON_FALSE`**——引擎目前**沒有條件分支**
|
||||
(實測 `grep ON_TRUE|ON_FALSE` 於 cypher-executor = 0;見 Gitea Arcrun#5)。
|
||||
需要判斷時:**寫成一個獨立節點**(例 `check_amount`)再接 `ON_SUCCESS`,
|
||||
讓查詢告訴你有沒有零件可用。
|
||||
|
||||
## 3. 第一個節點固定是 `input`
|
||||
|
||||
所有真範本都以 `input` 起頭——那是「觸發時帶進來的資料」。
|
||||
|
||||
---
|
||||
|
||||
## 4. 真範本(照抄結構、改內容)
|
||||
|
||||
> 以下四份**全部是實際部署且 `verdict=success` 的 workflow**,不是簡化示範。
|
||||
> 用 `acr logs <name>`(有 MCP 則 `arcrun_get_workflow(<name>)`) 可以拿完整定義。
|
||||
|
||||
### A. 最短:取資料 → 處理 (`graph_neighbors`)
|
||||
```
|
||||
input >> ON_SUCCESS >> fetch_triplets
|
||||
fetch_triplets >> ON_SUCCESS >> bfs_neighbors
|
||||
```
|
||||
|
||||
### B. 長鏈:多次查詢 → 組裝 → 問 AI → 收尾 (`rag_chat`)
|
||||
```
|
||||
input >> ON_SUCCESS >> prep
|
||||
prep >> ON_SUCCESS >> kw_search
|
||||
kw_search >> ON_SUCCESS >> sem_search
|
||||
sem_search >> ON_SUCCESS >> fetch_triplets
|
||||
fetch_triplets >> ON_SUCCESS >> fetch_blocks_a
|
||||
fetch_blocks_a >> ON_SUCCESS >> assemble
|
||||
assemble >> ON_SUCCESS >> ask_llm
|
||||
ask_llm >> ON_SUCCESS >> finalize
|
||||
```
|
||||
`prep` 前處理/`assemble` 組 prompt/`finalize` 收拾回應——三個常見的整形節點。
|
||||
|
||||
### C. 一節點分岔兩條 FOREACH (`rag_ingest_card`)
|
||||
```
|
||||
input >> ON_SUCCESS >> parse_card
|
||||
parse_card >> 對每個 block >> post_block
|
||||
parse_card >> 對每個 rel >> post_triplet
|
||||
```
|
||||
同一節點可有多條出邊,各自處理不同清單。
|
||||
|
||||
### D. 混合:直線 + 兩段 FOREACH (`rag_takedown_direct`)
|
||||
```
|
||||
input >> ON_SUCCESS >> prep
|
||||
prep >> ON_SUCCESS >> list_dead_blocks
|
||||
list_dead_blocks >> ON_SUCCESS >> build_deprecations
|
||||
build_deprecations >> 對每個 dead_entry >> deprecate_entry
|
||||
build_deprecations >> ON_SUCCESS >> list_triplets
|
||||
list_triplets >> ON_SUCCESS >> pick_dead_triplets
|
||||
pick_dead_triplets >> 對每個 dead_record >> deprecate_triplet
|
||||
```
|
||||
`build_deprecations` 同時有 FOREACH 出邊與 `ON_SUCCESS` 出邊——
|
||||
前者處理清單、後者繼續主線。
|
||||
|
||||
---
|
||||
|
||||
## 5. 節點怎麼命名(照真範本的模式,查詢較容易媒合)
|
||||
|
||||
| 意圖 | 模式 | 真例 |
|
||||
|---|---|---|
|
||||
| 前處理/正規化 | `prep` | `rag_chat.prep` |
|
||||
| 取一批資料 | `fetch_*`/`list_*` | `fetch_triplets`/`list_dead_blocks` |
|
||||
| 搜尋 | `*_search` | `kw_search`/`sem_search` |
|
||||
| 解析/切塊 | `parse_*` | `parse_card` |
|
||||
| 寫入 | `post_*` | `post_block`/`post_triplet` |
|
||||
| 組裝 | `assemble`/`build_*` | `assemble`/`build_deprecations` |
|
||||
| 問 AI | `ask_llm` | `rag_chat.ask_llm` |
|
||||
| 收尾整形 | `finalize` | `rag_chat.finalize` |
|
||||
|
||||
---
|
||||
|
||||
## 6. 寫完一定要查(**不要直接部署**)
|
||||
|
||||
```bash
|
||||
curl -s -X POST https://arcrun-cypher-executor.<subdomain>.workers.dev/cypher/search \
|
||||
-H 'content-type: application/json' -H 'X-Arcrun-API-Key: <namespace>' \
|
||||
-d '{"triplets":["input >> ON_SUCCESS >> fetch_data","fetch_data >> ON_SUCCESS >> notify"]}'
|
||||
```
|
||||
|
||||
回應的每個節點會有:
|
||||
|
||||
| status | 意思 | 你該做什麼 |
|
||||
|---|---|---|
|
||||
| `found` | 有這個節點。`source: component` 附 `input_schema`(怎麼填 payload)與 `success_rate`;`source: recipe` 附 description/endpoint | **只填 payload** |
|
||||
| `not_found` | **兩庫(零件 registry+recipe 庫)都查過,確定沒有** | 照 `suggestion` 欄走:缺 API → 寫 recipe(skill `write_recipe`);缺計算能力 → 投稿零件 PR(skill `add_new_wasm_component`)。`similar_components`/`similar_recipes` 是相近候選——先看有沒有現成的能直接用 |
|
||||
| `unknown` | 查不到 registry | **不代表不存在**,別據此改寫成 code |
|
||||
|
||||
> 註(2026-07-31):`/cypher/search` 曾對任何節點名都回假 `found`,已修為真查兩庫。
|
||||
> 舊實例(未更新部署)仍可能假 found——status 可信度以該實例部署版本為準。
|
||||
|
||||
---
|
||||
|
||||
## 7. 常犯的錯
|
||||
|
||||
1. **用不存在的邊**(`ON_FAILURE`/`ON_TRUE`)→ 只有 `ON_SUCCESS` 與 `對每個 X`
|
||||
2. **第一個節點不是 `input`**
|
||||
3. **把 recipe 當零件寫**——`telegram_send`/`gmail`/`kbdb_get` 是 **recipe** 不是零件
|
||||
→ 寫成 `http_request` + 該 recipe
|
||||
4. 🔴 **查詢回 `not_found` 就改寫成 `code` 節點**
|
||||
→ 那叫「腹語術」(表面用 Arcrun、實際全寫 JS)。正解:缺 API 寫 recipe、缺能力投稿零件。
|
||||
`code` 只用在**局部整形**(例:剝掉 LLM 回應的雜訊),不用來取代零件與流程控制。
|
||||
|
||||
---
|
||||
|
||||
## 8. 相關
|
||||
|
||||
- 完整版指引與十題考卷(含 haiku 實測 10/10):
|
||||
頂層 repo `system-dev/docs/3-specs/arcrun-usable/`
|
||||
- 下一步該讀哪支 skill(需 MCP):`arcrun_list_skills()`
|
||||
- 定期掃資料 → `build_watcher_workflow`
|
||||
- RAG 檢索問答 → `rag_with_arcrun`
|
||||
- workflow 卡住不動 → `debug_paused_workflow`
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 9. 資源去哪取(不要自己重造 Arcrun 已有的)
|
||||
|
||||
| 你想知道 | 跑這個 |
|
||||
|---|---|
|
||||
| 有哪些零件可用 | `acr parts` |
|
||||
| 某零件的設定範本 | `acr parts scaffold <name>` |
|
||||
| 有哪些 recipe | `acr recipe list`/`acr recipe search <關鍵字>` |
|
||||
| 支援哪些服務的認證 | `acr auth-recipe list` |
|
||||
| 某服務認證要哪些 credential + 範例 | `acr auth-recipe scaffold <service>` |
|
||||
| 已上傳的 recipe | `acr recipe list` |
|
||||
| 某服務認證要哪些 credential + 範例 | `acr auth-recipe scaffold <service>` |
|
||||
| **一次掃全部**(零件/recipe/auth-recipe/workflow) | `acr search <關鍵字>` |
|
||||
| 已部署的 workflow | `acr list` |
|
||||
| 某次執行為什麼失敗 | `acr logs <workflow>` |
|
||||
| 工作流語法、指令 | `acr --help` |
|
||||
|
||||
**先查再動手**——arcrun 多半已經有你要的零件 / recipe / 認證,不要自刻。
|
||||
**先查再動手**——Arcrun 多半已經有你要的零件/recipe/認證,不要自刻。
|
||||
|
||||
## 3. arcrun 是你(AI)用的工具,不是工具回頭呼叫 AI
|
||||
## 10. 做出來以後:驗證 → 部署
|
||||
|
||||
需要智慧判斷 / 自然語言轉換時,**你自己做**,再呼叫工作流執行確定性的下一步。
|
||||
**不要在工作流中間放零件回頭呼叫 LLM**。arcrun 的大腦就是操盤的你。
|
||||
```bash
|
||||
acr validate <workflow>.yaml # 先驗,別直接部署
|
||||
acr push <workflow>.yaml # 部署(暴露動作,見 §12)
|
||||
acr run <workflow> # 觸發一次,看實際結果
|
||||
acr logs <workflow> # 看執行紀錄/失敗原因
|
||||
```
|
||||
|
||||
## 4. arcrun 不替你做授權判斷
|
||||
需要 credential(API key/token)時:`acr auth-recipe scaffold <service>` 看要哪些,
|
||||
明確告訴使用者去哪取得、怎麼 `acr creds push`。
|
||||
🔑 **金鑰只拿名字**:workflow/recipe 裡只寫 `{{credential.<名字>}}`,
|
||||
**真身絕不寫進定義檔**(執行前才由系統回填)。
|
||||
|
||||
API 打不打得通由發 key 的服務決定。401/403 是對方服務在行使授權,**不是 arcrun 的 bug、不是你做錯**。
|
||||
不要在 arcrun 裡建「允許/禁止某 endpoint」的二次授權清單。
|
||||
## 11. Arcrun 是你(AI)用的工具,不是工具回頭呼叫 AI
|
||||
|
||||
## 5. 把東西開放給別人用 = 要使用者明示同意
|
||||
需要智慧判斷/自然語言轉換時,**你自己做**,再呼叫工作流執行確定性的下一步。
|
||||
**不要在工作流中間放零件回頭呼叫 LLM**——Arcrun 的大腦就是操盤的你。
|
||||
(唯一例外:`ask_llm` 這種「內容生成本身就是流程的一步」,見範本 B。)
|
||||
|
||||
部署對外 webhook、push recipe 會讓資料/能力**可被外部呼叫**(暴露面):
|
||||
## 12. 把東西開放給別人用 = 要使用者明示同意
|
||||
|
||||
`acr push`(部署 workflow)與 `acr recipe push`(投稿 recipe)會讓資料/能力**可被外部呼叫**:
|
||||
- 停下來,明確告訴使用者「這會讓 X 可被外部呼叫」,要他同意。**不替他決定公開。**
|
||||
- 非互動環境(你直跑)遇到 → 停,要人類確認,絕不自己塞 confirm 假裝同意。
|
||||
- arcrun 可提供保護(要求呼叫者帶 key / 限流)——提醒使用者。
|
||||
- 非互動環境(你直跑)遇到 → 停,把完整指令印給使用者自己貼上跑,絕不自己塞 confirm 假裝同意。
|
||||
- Arcrun 可提供保護(要求呼叫者帶 key/限流)——提醒使用者。
|
||||
|
||||
## 6. 誠實(最重要)
|
||||
## 13. Arcrun 不替你做授權判斷
|
||||
|
||||
API 打不打得通由發 key 的服務決定。401/403 是對方服務在行使授權,**不是 Arcrun 的 bug、不是你做錯**。
|
||||
不要在 Arcrun 裡建「允許/禁止某 endpoint」的二次授權清單。
|
||||
|
||||
## 14. 誠實(最重要)
|
||||
|
||||
- **不假綠**:沒打通就誠實說。缺 credential 打不到 2xx → 標「未驗收:缺 X」,不 mock 充綠燈。
|
||||
- **不假裝防偽 / 不代替人類確認**有風險的動作(暴露資料)。
|
||||
- **完成 = 客觀證據**(HTTP 2xx + trace),不是口頭「做好了」。
|
||||
- **不假裝防偽/不代替人類確認**有風險的動作(暴露資料)。
|
||||
- **完成 = 客觀證據**(HTTP 2xx + trace),不是口頭「做好了」。
|
||||
|
||||
---
|
||||
|
||||
## 怎麼用這份 mindset
|
||||
## 動手前的自檢清單
|
||||
|
||||
每次準備動手,先過一遍:
|
||||
1. 這能用工作流 / recipe 做嗎?(多半能 → 別自己寫 Python、別造零件)
|
||||
2. 我查過 `acr parts` / `acr auth-recipe` 了嗎?(arcrun 可能已有)
|
||||
3. 我是不是讓工作流回頭呼叫 AI?(是 → 改成我自己做)
|
||||
4. 這動作會把資料開放給別人嗎?(會 → 要使用者明示同意)
|
||||
5. 我有沒有假裝(假綠 / 假防偽 / 代替人類確認)?(有 → 停,誠實標明)
|
||||
1. 我把意圖寫成 `>>` 串了嗎?(還是直接跳去寫 YAML/寫程式)
|
||||
2. 我查過 `acr search` / `acr parts` / `acr recipe list` 了嗎?
|
||||
3. 查詢回 `not_found` 時,我走的是 recipe/零件 PR 兩條路,**還是偷偷改寫成 `code`**?(後者=腹語術)
|
||||
4. 我是不是讓工作流回頭呼叫 AI 做判斷?(是 → 改成我自己做)
|
||||
5. 這動作會把資料開放給別人嗎?(會 → 要使用者明示同意)
|
||||
6. 我有沒有假裝(假綠/假防偽/代替人類確認)?(有 → 停,誠實標明)
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: arcrun-mindset
|
||||
description: >-
|
||||
在 Arcrun 上做任何事時使用(用戶說「幫我用 Arcrun 做 X」「用 arcrun 寫一個工作流」「把 X 自動化」)。
|
||||
Arcrun 是跑在 Cloudflare 上的工作流引擎——你用 `>>` 寫「意圖」,系統告訴你有哪些現成零件與 recipe,
|
||||
你只填 payload,不必自己寫程式。**不要上網搜 Arcrun 文件**(網路上沒有),也不要自己猜 YAML 格式:
|
||||
先讀本 skill,再用 `acr` 指令(或 MCP 工具)查現成零件。
|
||||
涵蓋:意圖工作流語法、四份實跑過的範本、零件 vs recipe 的分別、缺件的兩條路、已知的坑。
|
||||
---
|
||||
|
||||
# Arcrun:怎麼寫意圖工作流
|
||||
|
||||
> **你已經配備 Arcrun**(此專案裝了 `acr` CLI,可能另有 `arcrun_*` MCP 工具)。
|
||||
> **別上網找文件**——網路上沒有 Arcrun 的文件,找到的都是錯的。答案都在本 skill 與 `acr` 指令裡。
|
||||
|
||||
## 先做這三件(照順序)
|
||||
|
||||
1. `acr whoami` — 確認連到哪個帳號(**勿自行 curl 猜帳號 URL**)
|
||||
2. 讀本 skill 下面的語法與範本 → 寫出 `>>` 意圖
|
||||
3. `acr parts`/`acr recipe list`(或 `acr search <關鍵字>` 一次掃全部)— 確認零件與 recipe 真的存在
|
||||
|
||||
**卡住時**:`acr search <關鍵字>` 跨類搜尋;有 MCP 就 `arcrun_get_skill('INDEX')` 拿全館導航。
|
||||
|
||||
---
|
||||
|
||||
## 0. 一句話世界觀
|
||||
|
||||
**Arcrun 裡幾乎所有東西都是工作流(workflow)。** 工作流 = 一張紙,寫「用哪些零件、什麼順序、什麼條件」。
|
||||
你大部分時間在**寫紙、改紙**,不是在造新零件、也不是自己寫腳本。
|
||||
|
||||
**Arcrun 只有三種東西,先分清楚就不會做歪:**
|
||||
|
||||
| 東西 | 是什麼 | 你能做的 |
|
||||
|---|---|---|
|
||||
| **工作流(workflow)** | 把零件/recipe 串起來的純文字流程 | **預設就寫這個**,自由寫 |
|
||||
| **recipe** | 打「一個固定外部 API」的設定(endpoint/header/body 模板) | 自由寫、**而且該投稿**(缺就自己補) |
|
||||
| **零件(component)** | WASM 程式(流程控制/資料處理/`http_request`/auth),固定一小套 | **你不自製**,走 PR 由維護者管 |
|
||||
|
||||
> **一句話判準**:打一個固定外部 endpoint → 寫 **recipe**;流程控制/資料處理/通用 HTTP → 用既有**零件**;其他 → 寫**工作流**串起來。
|
||||
|
||||
---
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
---
|
||||
|
||||
## 9. 資源去哪取(不要自己重造 Arcrun 已有的)
|
||||
|
||||
| 你想知道 | 跑這個 |
|
||||
|---|---|
|
||||
| 有哪些零件可用 | `acr parts` |
|
||||
| 某零件的設定範本 | `acr parts scaffold <name>` |
|
||||
| 有哪些 recipe | `acr recipe list`/`acr recipe search <關鍵字>` |
|
||||
| 支援哪些服務的認證 | `acr auth-recipe list` |
|
||||
| 某服務認證要哪些 credential + 範例 | `acr auth-recipe scaffold <service>` |
|
||||
| **一次掃全部**(零件/recipe/auth-recipe/workflow) | `acr search <關鍵字>` |
|
||||
| 已部署的 workflow | `acr list` |
|
||||
| 某次執行為什麼失敗 | `acr logs <workflow>` |
|
||||
| 工作流語法、指令 | `acr --help` |
|
||||
|
||||
**先查再動手**——Arcrun 多半已經有你要的零件/recipe/認證,不要自刻。
|
||||
|
||||
## 10. 做出來以後:驗證 → 部署
|
||||
|
||||
```bash
|
||||
acr validate <workflow>.yaml # 先驗,別直接部署
|
||||
acr push <workflow>.yaml # 部署(暴露動作,見 §12)
|
||||
acr run <workflow> # 觸發一次,看實際結果
|
||||
acr logs <workflow> # 看執行紀錄/失敗原因
|
||||
```
|
||||
|
||||
需要 credential(API key/token)時:`acr auth-recipe scaffold <service>` 看要哪些,
|
||||
明確告訴使用者去哪取得、怎麼 `acr creds push`。
|
||||
🔑 **金鑰只拿名字**:workflow/recipe 裡只寫 `{{credential.<名字>}}`,
|
||||
**真身絕不寫進定義檔**(執行前才由系統回填)。
|
||||
|
||||
## 11. Arcrun 是你(AI)用的工具,不是工具回頭呼叫 AI
|
||||
|
||||
需要智慧判斷/自然語言轉換時,**你自己做**,再呼叫工作流執行確定性的下一步。
|
||||
**不要在工作流中間放零件回頭呼叫 LLM**——Arcrun 的大腦就是操盤的你。
|
||||
(唯一例外:`ask_llm` 這種「內容生成本身就是流程的一步」,見範本 B。)
|
||||
|
||||
## 12. 把東西開放給別人用 = 要使用者明示同意
|
||||
|
||||
`acr push`(部署 workflow)與 `acr recipe push`(投稿 recipe)會讓資料/能力**可被外部呼叫**:
|
||||
- 停下來,明確告訴使用者「這會讓 X 可被外部呼叫」,要他同意。**不替他決定公開。**
|
||||
- 非互動環境(你直跑)遇到 → 停,把完整指令印給使用者自己貼上跑,絕不自己塞 confirm 假裝同意。
|
||||
- Arcrun 可提供保護(要求呼叫者帶 key/限流)——提醒使用者。
|
||||
|
||||
## 13. Arcrun 不替你做授權判斷
|
||||
|
||||
API 打不打得通由發 key 的服務決定。401/403 是對方服務在行使授權,**不是 Arcrun 的 bug、不是你做錯**。
|
||||
不要在 Arcrun 裡建「允許/禁止某 endpoint」的二次授權清單。
|
||||
|
||||
## 14. 誠實(最重要)
|
||||
|
||||
- **不假綠**:沒打通就誠實說。缺 credential 打不到 2xx → 標「未驗收:缺 X」,不 mock 充綠燈。
|
||||
- **不假裝防偽/不代替人類確認**有風險的動作(暴露資料)。
|
||||
- **完成 = 客觀證據**(HTTP 2xx + trace),不是口頭「做好了」。
|
||||
|
||||
---
|
||||
|
||||
## 動手前的自檢清單
|
||||
|
||||
1. 我把意圖寫成 `>>` 串了嗎?(還是直接跳去寫 YAML/寫程式)
|
||||
2. 我查過 `acr search` / `acr parts` / `acr recipe list` 了嗎?
|
||||
3. 查詢回 `not_found` 時,我走的是 recipe/零件 PR 兩條路,**還是偷偷改寫成 `code`**?(後者=腹語術)
|
||||
4. 我是不是讓工作流回頭呼叫 AI 做判斷?(是 → 改成我自己做)
|
||||
5. 這動作會把資料開放給別人嗎?(會 → 要使用者明示同意)
|
||||
6. 我有沒有假裝(假綠/假防偽/代替人類確認)?(有 → 停,誠實標明)
|
||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "arcrun",
|
||||
"version": "1.3.13",
|
||||
"version": "1.3.14",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "arcrun",
|
||||
"version": "1.3.13",
|
||||
"version": "1.3.14",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"chalk": "^5.3.0",
|
||||
|
||||
+3
-1
@@ -8,7 +8,9 @@
|
||||
"main": "./dist/index.js",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build": "npm run build:harness && npm run check:harness && tsc",
|
||||
"build:harness": "node scripts/build-harness-skill.mjs",
|
||||
"check:harness": "node scripts/check-harness-generation.mjs",
|
||||
"dev": "tsc --watch",
|
||||
"test": "node --test \"tests/**/*.test.ts\"",
|
||||
"prepublishOnly": "npm run build && chmod +x dist/index.js"
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* build-harness-skill.mjs — 由 registry/skills/ 組出 harness 的 arcrun-mindset SKILL.md
|
||||
*
|
||||
* 【為什麼是「建置期複製」而不是人工維護兩份】
|
||||
* `registry/skills/write_intent_workflow.md` 是意圖語法的**單一真相源**——它同時是
|
||||
* MCP `arcrun_get_skill()` 回給雲端 AI 的內容。harness 的 skill 若人工再抄一份,
|
||||
* 兩份必然漂移(2026-07-31 實錄:harness 那份停在上一代,grep「意圖」「>>」= 0 命中,
|
||||
* 只講世界觀,害新裝的用戶 AI 學不到 `>>`)。
|
||||
*
|
||||
* 作法:harness skill = 三段拼接
|
||||
* SKILL.md.head ← harness 專屬(frontmatter/CLI 入口/三種東西的分型)
|
||||
* registry 的 write_intent_workflow.md 正文 ← 單一真相源,只此一份被維護
|
||||
* SKILL.md.tail ← harness 專屬(acr 指令表/暴露同意/誠實鐵律)
|
||||
*
|
||||
* 為什麼不用 symlink / npm 打包直接引用:npm `files` 只收 `harness/`,
|
||||
* registry/ 不進套件;symlink 在 npm pack 與 Windows 上不可靠。建置期複製最單純。
|
||||
*
|
||||
* 產物 `SKILL.md` **有 commit 進 repo**(npm 套件裝的是它,不會跑 build),
|
||||
* 由 check-harness-generation.mjs 驗證它與 registry 沒有漂移。
|
||||
*/
|
||||
import { readFileSync, writeFileSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url)); // cli/scripts
|
||||
const repoRoot = join(here, '..', '..'); // repo 根
|
||||
const skillDir = join(here, '..', 'harness', 'skills', 'arcrun-mindset');
|
||||
const registrySkill = join(repoRoot, 'registry', 'skills', 'write_intent_workflow.md');
|
||||
|
||||
const head = readFileSync(join(skillDir, 'SKILL.md.head'), 'utf8').trimEnd();
|
||||
const tail = readFileSync(join(skillDir, 'SKILL.md.tail'), 'utf8').trimEnd();
|
||||
const body = readFileSync(registrySkill, 'utf8');
|
||||
|
||||
// 取 registry skill 的正文:去掉它自己的 H1 標題與「何時用這個 skill」那段
|
||||
// (harness 的 head 已用 CLI 語境寫過入口),從第一個 `## 1.` 章節起收。
|
||||
const idx = body.indexOf('## 1. 意圖工作流的語法');
|
||||
if (idx < 0) {
|
||||
console.error('❌ registry/skills/write_intent_workflow.md 找不到「## 1. 意圖工作流的語法」章節;');
|
||||
console.error(' registry skill 結構變了 → 請同步更新 cli/scripts/build-harness-skill.mjs 的取段規則。');
|
||||
process.exit(1);
|
||||
}
|
||||
const middle = body
|
||||
.slice(idx)
|
||||
// registry 版把 MCP 工具當預設介面;harness 裝在有 acr CLI 的專案 → 補上 CLI 等價指令
|
||||
.replace(/`arcrun_get_workflow\(<name>\)`/g, '`acr logs <name>`(有 MCP 則 `arcrun_get_workflow(<name>)`)')
|
||||
.replace(/`arcrun_list_components` \/ `arcrun_search_components`/g, '`acr parts` / `acr search`')
|
||||
.replace(/下一步該讀哪支 skill:`arcrun_list_skills\(\)`/g, '下一步該讀哪支 skill(需 MCP):`arcrun_list_skills()`')
|
||||
.trimEnd();
|
||||
|
||||
const out = [
|
||||
head,
|
||||
'',
|
||||
'<!-- 以下正文由 registry/skills/write_intent_workflow.md 於建置期複製而來(單一真相源)。',
|
||||
' 不要直接編輯本段——改 registry 那份,然後跑 `npm run build:harness`。 -->',
|
||||
'',
|
||||
middle,
|
||||
'',
|
||||
tail,
|
||||
'',
|
||||
].join('\n');
|
||||
|
||||
writeFileSync(join(skillDir, 'SKILL.md'), out, 'utf8');
|
||||
console.log(`✓ harness skill 已由 registry 重建:${out.length} bytes`);
|
||||
@@ -0,0 +1,130 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* check-harness-generation.mjs — 世代閘:harness 內容脫節就讓 build/publish 失敗
|
||||
*
|
||||
* 【為什麼要這道閘】
|
||||
* 2026-07-31 實錄:`acr install-harness` 的管道一直是好的,但它鋪出去的**內容停在上一代**——
|
||||
* harness skill grep「意圖」「>>」= 0 命中,只講世界觀。管道綠燈、交付物過時,
|
||||
* 沒有任何機械檢查會抱怨 ⇒ 世代脫節可以無聲存在好幾個月。
|
||||
*
|
||||
* 這道閘檢查四件交付物的「現世代指紋」。缺指紋 = exit 1,擋掉 build 與 npm publish。
|
||||
* 指紋要挑「上一代絕不會有、現世代一定有」的字串,不是隨便的關鍵字。
|
||||
*/
|
||||
import { readFileSync, existsSync, statSync } from 'node:fs';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { execFileSync } from 'node:child_process';
|
||||
|
||||
const here = dirname(fileURLToPath(import.meta.url));
|
||||
const harness = join(here, '..', 'harness');
|
||||
const repoRoot = join(here, '..', '..');
|
||||
|
||||
/** @type {{file: string, must: [string, string][], mustNot?: [string,string][]}[]} */
|
||||
const CHECKS = [
|
||||
{
|
||||
file: 'skills/arcrun-mindset/SKILL.md',
|
||||
must: [
|
||||
['>>', '意圖語法(`A >> 邊 >> B`)——步驟 1 的核心教材'],
|
||||
['ON_SUCCESS', '合法邊之一'],
|
||||
['對每個', 'FOREACH 邊(十題裡有四題要用)'],
|
||||
['input', '第一個節點固定是 input'],
|
||||
['not_found', '現世代查詢狀態(舊版寫 missing/假 found)'],
|
||||
['腹語術', '缺件不准改寫成 code 的紅線'],
|
||||
['recipe', '零件 vs recipe 分型'],
|
||||
],
|
||||
mustNot: [
|
||||
// ON_TRUE 只准出現在「教它不存在」的脈絡(否定詞/實測證據)。
|
||||
// 若哪天它出現在範本裡(正面示範),就是教材寫錯,該擋。
|
||||
['ON_TRUE', '引擎沒有條件邊,教材不該把它當可用的邊', /不要寫|不存在|沒有條件|❌|非法|grep|= 0/],
|
||||
],
|
||||
},
|
||||
{
|
||||
file: 'CLAUDE.block.md',
|
||||
must: [
|
||||
['>>', '意圖語法要在 CLAUDE.md 就先亮相'],
|
||||
['not_found', '缺件兩條路的觸發點'],
|
||||
],
|
||||
},
|
||||
{
|
||||
file: 'commands/arcrun.md',
|
||||
must: [
|
||||
['>>', '/arcrun 的第一步就該是寫意圖'],
|
||||
['acr search', '現世代的跨類搜尋指令'],
|
||||
],
|
||||
},
|
||||
{
|
||||
file: 'hooks/arcrun-guard.sh',
|
||||
must: [
|
||||
['arcrun-mindset', 'hook 被擋下時要把 AI 導向 skill,而不是叫它去翻 repo 文件'],
|
||||
['>>', 'hook 的正路提示要提到意圖語法'],
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
let fail = 0;
|
||||
const say = (s) => console.log(s);
|
||||
|
||||
say('\n 世代閘:檢查 harness 交付物是否為現世代內容\n');
|
||||
|
||||
for (const c of CHECKS) {
|
||||
const p = join(harness, c.file);
|
||||
if (!existsSync(p)) {
|
||||
say(` ❌ ${c.file} — 檔案不存在`);
|
||||
fail++;
|
||||
continue;
|
||||
}
|
||||
const text = readFileSync(p, 'utf8');
|
||||
const missing = c.must.filter(([needle]) => !text.includes(needle));
|
||||
const badNot = (c.mustNot ?? []).filter(([needle, , allowIfNear]) => {
|
||||
if (!text.includes(needle)) return false;
|
||||
if (!allowIfNear) return true;
|
||||
// 允許「在教『不要用』的脈絡裡」出現:看該字串所在行是否有豁免詞
|
||||
return !text
|
||||
.split('\n')
|
||||
.filter((l) => l.includes(needle))
|
||||
.every((l) => allowIfNear.test(l));
|
||||
});
|
||||
|
||||
if (missing.length === 0 && badNot.length === 0) {
|
||||
say(` ✓ ${c.file}`);
|
||||
} else {
|
||||
fail++;
|
||||
say(` ❌ ${c.file}`);
|
||||
for (const [needle, why] of missing) say(` 缺指紋「${needle}」— ${why}`);
|
||||
for (const [needle, why] of badNot) say(` 不該出現「${needle}」— ${why}`);
|
||||
}
|
||||
}
|
||||
|
||||
// harness skill 必須是由 registry 重建的最新版(防「改了 registry 忘了重跑 build」)
|
||||
const skillPath = join(harness, 'skills', 'arcrun-mindset', 'SKILL.md');
|
||||
const registrySkill = join(repoRoot, 'registry', 'skills', 'write_intent_workflow.md');
|
||||
if (existsSync(skillPath) && existsSync(registrySkill)) {
|
||||
try {
|
||||
execFileSync(process.execPath, [join(here, 'build-harness-skill.mjs')], { stdio: 'pipe' });
|
||||
const rebuilt = readFileSync(skillPath, 'utf8');
|
||||
const before = statSync(skillPath); // 重建後內容即為期望值
|
||||
void before;
|
||||
// 重建是冪等的:若重建後與 git 中的版本不同,git diff 會在 CI 顯示;
|
||||
// 這裡直接比對「重建結果是否含 registry 當前的關鍵段落」
|
||||
const reg = readFileSync(registrySkill, 'utf8');
|
||||
const marker = reg.includes('## 7. 常犯的錯') ? '## 7. 常犯的錯' : null;
|
||||
if (marker && !rebuilt.includes(marker)) {
|
||||
say(` ❌ harness skill 與 registry 漂移:registry 有「${marker}」但重建產物沒有`);
|
||||
fail++;
|
||||
} else {
|
||||
say(' ✓ harness skill 與 registry/skills/write_intent_workflow.md 同步');
|
||||
}
|
||||
} catch (e) {
|
||||
say(` ❌ 無法由 registry 重建 harness skill:${e.message}`);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
say('');
|
||||
if (fail) {
|
||||
say(` 🔴 世代閘擋下(${fail} 項)。harness 交付的內容落後於現世代。`);
|
||||
say(' 修法:改 registry/skills/write_intent_workflow.md(單一真相源)或對應的');
|
||||
say(' cli/harness/ 檔案,然後跑 `npm run build:harness` 重建,再跑本檢查。\n');
|
||||
process.exit(1);
|
||||
}
|
||||
say(' ✅ 世代閘通過:四件交付物都帶現世代指紋\n');
|
||||
@@ -110,11 +110,18 @@ function mergeSettings(cwd: string, src: string): void {
|
||||
writeFileSync(path, JSON.stringify(settings, null, 2) + '\n', 'utf8');
|
||||
}
|
||||
|
||||
/** 遞迴複製目錄樹(覆蓋同名檔)。 */
|
||||
/** 建置期產物的來源片段(`SKILL.md.head` / `.tail`),只給 build-harness-skill.mjs 用,
|
||||
* 不該被鋪進使用者專案(使用者拿到的是拼接好的 `SKILL.md`)。 */
|
||||
function isBuildSource(name: string): boolean {
|
||||
return name.endsWith('.head') || name.endsWith('.tail');
|
||||
}
|
||||
|
||||
/** 遞迴複製目錄樹(覆蓋同名檔;跳過建置期來源片段)。 */
|
||||
function copyTree(srcDir: string, dstDir: string): void {
|
||||
if (!existsSync(srcDir)) return;
|
||||
mkdirSync(dstDir, { recursive: true });
|
||||
for (const name of readdirSync(srcDir, { withFileTypes: true })) {
|
||||
if (isBuildSource(name.name)) continue;
|
||||
const s = join(srcDir, name.name);
|
||||
const d = join(dstDir, name.name);
|
||||
if (name.isDirectory()) copyTree(s, d);
|
||||
|
||||
Reference in New Issue
Block a user