homunculus-code 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONTRIBUTING.md +56 -0
- package/LICENSE +21 -0
- package/README.md +443 -0
- package/bin/init.js +317 -0
- package/commands/eval-skill.md +48 -0
- package/commands/evolve.md +67 -0
- package/commands/improve-skill.md +50 -0
- package/core/evaluate-session.js +173 -0
- package/core/observe.sh +51 -0
- package/core/prune-instincts.js +159 -0
- package/docs/nightly-agent.md +130 -0
- package/examples/reference/README.md +47 -0
- package/examples/reference/architecture.yaml +886 -0
- package/examples/reference/evolved-agents/assistant-explorer.md +86 -0
- package/examples/reference/evolved-agents/shell-debugger.md +108 -0
- package/examples/reference/evolved-agents/tdd-runner.md +112 -0
- package/examples/reference/evolved-evals/api-system-diagnosis.eval.yaml +125 -0
- package/examples/reference/evolved-evals/assistant-system-management.eval.yaml +123 -0
- package/examples/reference/evolved-evals/claude-code-reference.eval.yaml +394 -0
- package/examples/reference/evolved-evals/development-verification-patterns.eval.yaml +117 -0
- package/examples/reference/evolved-evals/multi-agent-design-patterns.eval.yaml +151 -0
- package/examples/reference/evolved-evals/shell-automation-patterns.eval.yaml +209 -0
- package/examples/reference/evolved-evals/tdd-workflow.eval.yaml +191 -0
- package/examples/reference/evolved-evals/workflows.eval.yaml +148 -0
- package/examples/reference/evolved-skills/api-system-diagnosis.md +234 -0
- package/examples/reference/evolved-skills/assistant-system-management.md +199 -0
- package/examples/reference/evolved-skills/development-verification-patterns.md +243 -0
- package/examples/reference/evolved-skills/multi-agent-design-patterns.md +259 -0
- package/examples/reference/evolved-skills/shell-automation-patterns.md +347 -0
- package/examples/reference/evolved-skills/tdd-workflow.md +272 -0
- package/examples/reference/evolved-skills/workflows.md +237 -0
- package/package.json +25 -0
- package/templates/CLAUDE.md.template +36 -0
- package/templates/architecture.template.yaml +41 -0
- package/templates/rules/evolution-system.md +29 -0
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Skill: Multi-Agent Design Patterns
|
|
2
|
+
|
|
3
|
+
**Version:** 1.2
|
|
4
|
+
**Evolved from:** 6 instincts (parallel-agent-task-distribution-2026, parallel-agents-git-coordination-2026, direnv-worktrees-parallel-agents-2026, mechanical-agent-responsibility-isolation-2026, agentic-engineering-patterns-2026, autonomous-agent-reliability-2026)
|
|
5
|
+
**Confidence threshold:** 0.75–0.92
|
|
6
|
+
**Created:** 2026-03-19
|
|
7
|
+
**Use when:** 設計多 agent 架構:平行任務分配、subagent dispatch 條件、worktree 隔離、orchestration 反模式識別
|
|
8
|
+
|
|
9
|
+
## 目的
|
|
10
|
+
|
|
11
|
+
設計、協調多 agent 系統的通用指引。適用於:平行 agent 任務分配、長時間自主 agent 可靠性、agent 職責隔離、多 agent 環境配置。
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Pattern 1:去中心化任務分配(Parallel Agents)
|
|
16
|
+
|
|
17
|
+
**問題**:多個 agent 同時執行時,如何避免重複認領同一任務?
|
|
18
|
+
|
|
19
|
+
### 方案:File Lock + Git Push
|
|
20
|
+
```
|
|
21
|
+
任務宣告流程:
|
|
22
|
+
1. agent 寫檔案到 current_tasks/<task-id>.lock(含 agent_id + timestamp)
|
|
23
|
+
2. git add + commit + push
|
|
24
|
+
3. 若 push 失敗(競爭衝突)→ git pull → 檢查 current_tasks/ → 換選下一個任務
|
|
25
|
+
4. 任務完成 → 刪除 lock 檔 + commit
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 有機專業化(Emergent Specialization)
|
|
29
|
+
- **不要預先指定分工**,讓 agent 依 repo 狀態自然分化
|
|
30
|
+
- 16 agent 建 C compiler 案例:自然出現「實作者、去重者、效能優化者、文件維護者」角色
|
|
31
|
+
- 強制指定分工反而降低彈性
|
|
32
|
+
|
|
33
|
+
### Oracle-driven 解除卡頓
|
|
34
|
+
當所有 agent 被同一個 bug 卡住時:
|
|
35
|
+
1. 引入外部 oracle(如 GCC 編譯結果、CI 測試輸出)
|
|
36
|
+
2. 把 oracle 輸出拆分為 per-agent 的獨立子任務
|
|
37
|
+
3. 恢復平行性
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Pattern 2:Git-Based 任務協調細節
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# Agent 認領任務
|
|
45
|
+
TASK_ID="fix-memory-leak-str.c"
|
|
46
|
+
AGENT_ID="agent-$(uuidgen | head -c 8)"
|
|
47
|
+
|
|
48
|
+
# 宣告認領
|
|
49
|
+
echo "{\"agent\":\"$AGENT_ID\",\"started\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\"}" \
|
|
50
|
+
> current_tasks/${TASK_ID}.lock
|
|
51
|
+
git add current_tasks/ && git commit -m "claim: $TASK_ID by $AGENT_ID" && git push
|
|
52
|
+
|
|
53
|
+
# 若 push 失敗(conflict)
|
|
54
|
+
if git pull --rebase; then
|
|
55
|
+
# 檢查是否被搶先
|
|
56
|
+
if [ -f "current_tasks/${TASK_ID}.lock" ]; then
|
|
57
|
+
# 已被其他 agent 認領,選下一個任務
|
|
58
|
+
select_next_task
|
|
59
|
+
fi
|
|
60
|
+
fi
|
|
61
|
+
|
|
62
|
+
# 任務完成
|
|
63
|
+
rm current_tasks/${TASK_ID}.lock
|
|
64
|
+
git commit -am "done: $TASK_ID" && git push
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**適用場景**:多個 Claude Code agent 在不同 worktree 並行工作、夜間批量任務分配。
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Pattern 3:平行 Agent 環境配置(direnv + Worktrees)
|
|
72
|
+
|
|
73
|
+
**問題**:`git worktree add` 新 worktree 後,`.env` / API keys / local config 遺失。
|
|
74
|
+
|
|
75
|
+
### 解法:direnv `.envrc` 共享引用
|
|
76
|
+
|
|
77
|
+
在 repo 根目錄建立 `.envrc`(加入 `.gitignore`):
|
|
78
|
+
```bash
|
|
79
|
+
# .envrc(不 commit)
|
|
80
|
+
export ANTHROPIC_API_KEY="sk-ant-..."
|
|
81
|
+
export DATABASE_URL="postgresql://localhost:5432/mydb"
|
|
82
|
+
|
|
83
|
+
# 讓所有 worktree 共用同一個 .venv
|
|
84
|
+
export VIRTUAL_ENV="$(git rev-parse --show-toplevel)/.venv"
|
|
85
|
+
export PATH="$VIRTUAL_ENV/bin:$PATH"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
每個 `git worktree add` 後,`.envrc` 自動生效(direnv hook):
|
|
89
|
+
- worktree 繼承主 repo 的環境變數
|
|
90
|
+
- 無需手動複製 `.env` 檔案
|
|
91
|
+
|
|
92
|
+
**搭配 CC v2.1.76 的 `worktree.sparsePaths`**(monorepo 優化):
|
|
93
|
+
```json
|
|
94
|
+
// .claude/settings.json
|
|
95
|
+
{
|
|
96
|
+
"worktree": {
|
|
97
|
+
"sparsePaths": ["src/", "tests/", "package.json"]
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Pattern 4:Agent 職責隔離原則
|
|
105
|
+
|
|
106
|
+
### 機械式 Agent vs 決策 Agent
|
|
107
|
+
|
|
108
|
+
| 類型 | 職責 | 典型任務 |
|
|
109
|
+
|------|------|---------|
|
|
110
|
+
| **機械式 Agent** | 確定性轉換 | JSON 格式轉換、schema 驗證、markdown 提取 |
|
|
111
|
+
| **決策 Agent** | 判斷與過濾 | 優先級排序、合理性評估、去重 |
|
|
112
|
+
|
|
113
|
+
**關鍵原則**:機械式 agent 不應包含任何決策邏輯。
|
|
114
|
+
|
|
115
|
+
```
|
|
116
|
+
❌ 錯誤設計:summarizer agent 同時負責「提取 + 去重 + 優先排序」
|
|
117
|
+
✅ 正確設計:
|
|
118
|
+
- summarizer(機械式):提取所有 JSON → 輸出完整列表
|
|
119
|
+
- reviewer(決策):由人工或決策 agent 過濾
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**為什麼重要**:混入決策邏輯的機械式 agent 難以 debug(「它為什麼漏掉這條」),且無法在不同 context 下重用。
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## Pattern 5:自主 Agent 可靠性
|
|
127
|
+
|
|
128
|
+
### 模型選擇(長時間 Loop)
|
|
129
|
+
- **Opus 4.6**:✅ 穩定遵守「LOOP FOREVER」指令(Karpathy 118 次實驗驗證)
|
|
130
|
+
- **較弱模型**:❌ 長時間後可能無法維持指令遵循
|
|
131
|
+
|
|
132
|
+
→ 長時間夜間 agent 應使用 Opus/Sonnet,不用 Haiku
|
|
133
|
+
|
|
134
|
+
### Context Compression 邊界
|
|
135
|
+
在自然任務邊界點做 compact,不要等到 token 耗盡:
|
|
136
|
+
```
|
|
137
|
+
一個里程碑完成 → compact → 繼續下一個
|
|
138
|
+
(不要連續跑直到 context 爆掉)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 外部記憶持久化(SHARED_TASK_NOTES 模式)
|
|
142
|
+
長時間 agent 必須維護外部狀態檔案(不只依賴對話記憶):
|
|
143
|
+
|
|
144
|
+
```markdown
|
|
145
|
+
# TASK_PROGRESS.md(agent 每輪更新)
|
|
146
|
+
|
|
147
|
+
## 已完成
|
|
148
|
+
- [x] 分析 9 個 API instincts → 提煉為 skill
|
|
149
|
+
- [x] 執行 eval:3/3 PASS
|
|
150
|
+
|
|
151
|
+
## 當前狀態
|
|
152
|
+
研究 multi-agent patterns 中...
|
|
153
|
+
|
|
154
|
+
## 下一步
|
|
155
|
+
1. 寫 skill 檔案
|
|
156
|
+
2. 建立 eval spec
|
|
157
|
+
3. 更新 MEMORY.md
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**量化依據**:IBM 研究(2026):agent 記憶萃取讓任務完成率 69.6% → 73.2%(+3.6%)
|
|
161
|
+
|
|
162
|
+
### Tool 描述品質 = 可靠性槓桿
|
|
163
|
+
- Tool 文件越精確,agent 成功率越高(Trace-Free+ 100+ tools 研究)
|
|
164
|
+
- 定期審查 instinct 的「觸發條件」,確保描述精準
|
|
165
|
+
- 考慮讓 agent 自動重寫/改進 tool 文件
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Pattern 6:Agentic Engineering 最佳實踐
|
|
170
|
+
|
|
171
|
+
### Simon Willison 三原則(2026 現場驗證)
|
|
172
|
+
|
|
173
|
+
1. **先讓 agent 探索工具**:
|
|
174
|
+
```
|
|
175
|
+
Use `uvx my-tool --help` to learn about these tools first.
|
|
176
|
+
```
|
|
177
|
+
- 不要假設 agent 知道私有/新工具的用法
|
|
178
|
+
|
|
179
|
+
2. **TDD + Agent = 最佳組合**(Pragmatic Summit 2026):
|
|
180
|
+
- 「Tests are no longer even remotely optional」
|
|
181
|
+
- 先要求 agent 寫 failing tests → 再實作 → red-green
|
|
182
|
+
- Tests 讓 agent 保持正確方向,成本接近零
|
|
183
|
+
|
|
184
|
+
3. **複合工程迴圈(Compound Engineering Loop)**:
|
|
185
|
+
```
|
|
186
|
+
任務完成 → retrospective → 文件「什麼有效」
|
|
187
|
+
↓
|
|
188
|
+
注入未來 agent 指令(instincts/skills)
|
|
189
|
+
```
|
|
190
|
+
- 每次互動都累積在 knowledge base 中
|
|
191
|
+
- J-Assistant 的 nightly instinct evolve 是這個原則的系統化實作
|
|
192
|
+
|
|
193
|
+
### Agent-friendly 輸出格式
|
|
194
|
+
多 agent 協作時,log 格式很重要:
|
|
195
|
+
```bash
|
|
196
|
+
# ✅ Agent 可自動解析
|
|
197
|
+
ERROR: file.c:42: undefined variable 'foo'
|
|
198
|
+
TASK_COMPLETE: fix-memory-leak-str.c
|
|
199
|
+
|
|
200
|
+
# ❌ Agent 難以處理
|
|
201
|
+
Something went wrong somewhere in the file maybe around line 40ish
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## Pattern 7:Writer/Reviewer 雙 Session
|
|
207
|
+
|
|
208
|
+
降低自我確認偏誤(confirmation bias)的核心模式。
|
|
209
|
+
|
|
210
|
+
### 方案:Session A 實作 → Session B 全新 context 做 review
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
Session A(Writer):
|
|
214
|
+
claude -p "Implement feature X" --allowedTools "Edit,Write,Bash"
|
|
215
|
+
→ 產出 code changes
|
|
216
|
+
|
|
217
|
+
Session B(Reviewer):
|
|
218
|
+
claude -p "Review the changes in @src/feature.ts" --allowedTools "Read,Grep,Glob"
|
|
219
|
+
→ 全新 context,不帶 Session A 的決策偏見
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 為什麼有效
|
|
223
|
+
- Session A 做完決策後,會傾向「確認自己的選擇是對的」
|
|
224
|
+
- Session B 從零開始看 code,更容易發現 A 忽略的問題
|
|
225
|
+
- Claude Code 官方最佳實踐(Section 16: Advanced Workflows)
|
|
226
|
+
|
|
227
|
+
### 使用時機
|
|
228
|
+
- 重要功能的 code review(不是 typo fix)
|
|
229
|
+
- 涉及安全、認證、資料庫 schema 等高風險改動
|
|
230
|
+
- forge-dev review 階段可考慮用獨立 session 驗證
|
|
231
|
+
|
|
232
|
+
### 注意事項
|
|
233
|
+
- Reviewer session 應**只有唯讀工具**(Read/Grep/Glob),不改 code
|
|
234
|
+
- 也可以反過來:A 寫測試 → B 寫實作(TDD 變體)
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## 設計反模式
|
|
239
|
+
|
|
240
|
+
- ❌ **過度中心化**:用一個 orchestrator 管理所有 agent 任務(bottleneck + single point of failure)
|
|
241
|
+
- ❌ **硬編碼分工**:預先指定每個 agent 的角色(限制有機專業化)
|
|
242
|
+
- ❌ **機械式 agent 加入決策邏輯**(難以 debug 和重用)
|
|
243
|
+
- ❌ **不維護外部記憶**:只依賴對話歷史(compact 後遺失上下文)
|
|
244
|
+
- ❌ **跳過 tool 探索**:假設 agent 知道所有工具的用法
|
|
245
|
+
- ❌ **過度分派 subagent**:root agent 有足夠 token 時仍強制 dispatch subagent。
|
|
246
|
+
Subagent 的核心價值是「保護 root context window」,不是分工。
|
|
247
|
+
判斷準則:root agent 仍有 >40% context 空間 → 直接處理;
|
|
248
|
+
否 → 考慮 dispatch(新 context window + 隔離 token 預算)。
|
|
249
|
+
(Simon Willison,2026-03-17:「The main value of subagents is preserving the root context, not specialization」)
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 觸發場景
|
|
254
|
+
|
|
255
|
+
- 設計多個 Claude agent 並行工作的架構
|
|
256
|
+
- 夜間 batch 任務需要平行化
|
|
257
|
+
- 長時間自主 agent 出現可靠性問題
|
|
258
|
+
- 機械式 agent(summarizer/extractor)需要與決策 agent 分離
|
|
259
|
+
- 新專案引入 agent 輔助開發,選擇合適的 TDD 和 review 流程
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
# Skill: Shell Automation Patterns
|
|
2
|
+
|
|
3
|
+
**Version:** 1.6
|
|
4
|
+
**Icon:** lightning
|
|
5
|
+
**Abbr:** Shell
|
|
6
|
+
**Evolved from:**
|
|
7
|
+
- `discord-as-primary-output.md` (0.95)
|
|
8
|
+
- `claude-print-in-shell-scripts.md` (0.85)
|
|
9
|
+
- `jq-state-management.md` (0.85)
|
|
10
|
+
- `gog-for-google-workspace.md` (0.90)
|
|
11
|
+
- `playwright-usage-check.md` (0.88)
|
|
12
|
+
- `modular-autodiscovery-pattern.md` (0.80)
|
|
13
|
+
- `jq-jsonl-stream-parsing.md` (0.90)
|
|
14
|
+
- `jq-null-safety-diagnostic-cycle.md` (0.85)
|
|
15
|
+
- `jq-reduce-scope-trap.md` (0.85)
|
|
16
|
+
**Evolved:** 2026-03-14 night agent (v1.4)
|
|
17
|
+
**Use when:** 撰寫 shell 自動化腳本:launchd plist、jq 狀態管理、Claude CLI headless、Discord webhook、JSONL 串流解析、macOS zsh 模式
|
|
18
|
+
|
|
19
|
+
## 用途
|
|
20
|
+
|
|
21
|
+
這個 skill 涵蓋 Javan 在 shell script 自動化中反覆使用的核心模式。
|
|
22
|
+
適用於設計或擴充 cron job、heartbeat checks、daily-news 等系統。
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## 模式 1:Discord Webhook 輸出
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
WEBHOOK_URL="$DISCORD_WEBHOOK" # 從環境變數取得
|
|
30
|
+
|
|
31
|
+
send_discord() {
|
|
32
|
+
local msg="$1"
|
|
33
|
+
# 分塊處理(2000 字元限制)
|
|
34
|
+
while [[ ${#msg} -gt 1900 ]]; do
|
|
35
|
+
chunk="${msg:0:1900}"
|
|
36
|
+
msg="${msg:1900}"
|
|
37
|
+
curl -s -X POST "$WEBHOOK_URL" \
|
|
38
|
+
-H "Content-Type: application/json" \
|
|
39
|
+
-d "{\"content\": $(echo "$chunk" | jq -Rs .)}"
|
|
40
|
+
done
|
|
41
|
+
curl -s -X POST "$WEBHOOK_URL" \
|
|
42
|
+
-H "Content-Type: application/json" \
|
|
43
|
+
-d "{\"content\": $(echo "$msg" | jq -Rs .)}"
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
**規則:**
|
|
48
|
+
- 夜間(01:00-07:00)不推送 Discord
|
|
49
|
+
- 錯誤 log 寫入檔案,不推 Discord
|
|
50
|
+
- 2000 字元需分塊
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 模式 2:Claude --print AI 處理
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
process_with_claude() {
|
|
58
|
+
local input_file="$1"
|
|
59
|
+
local prompt_file="$2"
|
|
60
|
+
local log_file="$3"
|
|
61
|
+
|
|
62
|
+
RESULT=$(unset CLAUDECODE; claude --print \
|
|
63
|
+
--system-prompt "$(cat "$prompt_file")" \
|
|
64
|
+
--model claude-sonnet-4-6 \
|
|
65
|
+
< "$input_file" \
|
|
66
|
+
2>>"$log_file")
|
|
67
|
+
|
|
68
|
+
echo "$RESULT"
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
**規則:**
|
|
73
|
+
- 必須 `unset CLAUDECODE`
|
|
74
|
+
- stdin 傳入(`< file`)
|
|
75
|
+
- 模型固定 `claude-sonnet-4-6`
|
|
76
|
+
- 檢查 exit code:`|| { log "claude --print failed"; return 1; }`
|
|
77
|
+
- 長時間呼叫加 timeout:`timeout 300 claude --print ...`
|
|
78
|
+
- 輸出可能為空,使用前先檢查 `[[ -n "$RESULT" ]]`
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
## 模式 3:jq JSON 狀態管理
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
STATE_FILE="path/to/state.json"
|
|
86
|
+
|
|
87
|
+
# 讀取
|
|
88
|
+
VALUE=$(jq -r '.key // empty' "$STATE_FILE")
|
|
89
|
+
|
|
90
|
+
# 原子更新(含 trap EXIT 清理)
|
|
91
|
+
TMPFILE=$(mktemp)
|
|
92
|
+
trap "rm -f $TMPFILE" EXIT # 必須在 mktemp 後立即設!確保中斷時 tmpfile 被清理
|
|
93
|
+
jq --arg val "$VALUE" '.key = $val' "$STATE_FILE" > "$TMPFILE" \
|
|
94
|
+
&& mv "$TMPFILE" "$STATE_FILE"
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**規則:**
|
|
98
|
+
- 永遠用 tmpfile 做原子更新
|
|
99
|
+
- **mktemp 後立即加 `trap "rm -f $TMPFILE" EXIT`**(不然 script 中斷會留下 /tmp/ 殘留)
|
|
100
|
+
- trap 放在 mktemp 之後(mktemp 之前 $TMPFILE 未定義,trap 觸發會報錯)
|
|
101
|
+
- `EXIT` 比 `ERR` 更完整:ERR 不捕捉 kill 信號,EXIT 涵蓋所有結束情況
|
|
102
|
+
- 讀取時用 `// empty` 設預設值
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 模式 4:gog CLI 操作 Gmail
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
# 搜尋
|
|
110
|
+
gog gmail search "is:unread" --account starpincer@gmail.com
|
|
111
|
+
|
|
112
|
+
# 讀取
|
|
113
|
+
gog gmail message get "$MSG_ID" --account starpincer@gmail.com
|
|
114
|
+
|
|
115
|
+
# 標記已讀
|
|
116
|
+
gog gmail message modify "$MSG_ID" \
|
|
117
|
+
--remove UNREAD \
|
|
118
|
+
--account starpincer@gmail.com \
|
|
119
|
+
--no-input
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
**規則:**
|
|
123
|
+
- Gmail 帳號固定 `starpincer@gmail.com`
|
|
124
|
+
- cron job 中不用 MCP,用 gog
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 模式 5:模組化自動發現
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
MODULES_DIR="$SCRIPT_DIR/checks"
|
|
132
|
+
|
|
133
|
+
for module in "$MODULES_DIR"/*.sh; do
|
|
134
|
+
[[ -f "$module" ]] || continue
|
|
135
|
+
# 透過環境變數傳遞 context
|
|
136
|
+
export CONTEXT_VAR="$value"
|
|
137
|
+
result=$(zsh "$module" 2>/dev/null) || continue
|
|
138
|
+
# 處理 JSON 輸出
|
|
139
|
+
echo "$result" | jq -r '.key // empty'
|
|
140
|
+
done
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
**規則:**
|
|
144
|
+
- 每個模組透過環境變數接收輸入
|
|
145
|
+
- 透過 stdout 輸出 JSON
|
|
146
|
+
- 模組失敗不中斷整體流程(`|| continue`)
|
|
147
|
+
- 錯誤輸出導向 log:`2>>"$LOG_FILE"`
|
|
148
|
+
- 失敗時記錄哪個模組失敗:`log "WARN: $module_name failed"`
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## 模式 6:Playwright 使用量監控
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# 取得使用率
|
|
156
|
+
USAGE_RESULT=$(cd ~/assistant/heartbeat && node check-usage.js 2>/dev/null)
|
|
157
|
+
USAGE_PCT=$(echo "$USAGE_RESULT" | jq -r '.percentage // empty')
|
|
158
|
+
USAGE_DAY=$(echo "$USAGE_RESULT" | jq -r '.days_into_week // empty')
|
|
159
|
+
|
|
160
|
+
# 計算預算(線性增長,週限額 80%)
|
|
161
|
+
USAGE_BUDGET=$(awk "BEGIN {printf \"%.1f\", ($USAGE_DAY / 7) * 80}")
|
|
162
|
+
|
|
163
|
+
# 判斷是否超預算
|
|
164
|
+
if (( $(echo "$USAGE_PCT > $USAGE_BUDGET" | bc -l) )); then
|
|
165
|
+
echo "Over budget"
|
|
166
|
+
fi
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## 模式 7:JSONL 事件流解析
|
|
174
|
+
|
|
175
|
+
Claude SDK session log 是 JSONL 格式,用 `select()` 過濾事件:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
SESSION_LOG="path/to/session.jsonl"
|
|
179
|
+
|
|
180
|
+
# 從 session 結果中提取統計
|
|
181
|
+
CLAUDE_COST=$(jq -r 'select(.type=="result") | .total_cost_usd // 0' "$SESSION_LOG" 2>/dev/null | tail -1)
|
|
182
|
+
CLAUDE_TURNS=$(jq -r 'select(.type=="result") | .num_turns // "?"' "$SESSION_LOG" 2>/dev/null | tail -1)
|
|
183
|
+
CLAUDE_STOP_REASON=$(jq -r 'select(.type=="result") | .subtype // .stop_reason // "unknown"' "$SESSION_LOG" 2>/dev/null | tail -1)
|
|
184
|
+
|
|
185
|
+
# 監控 session 進度
|
|
186
|
+
LAST_TOOL=$(jq -r 'select(.type=="tool_use") | .tool_name // empty' "$SESSION_LOG" 2>/dev/null | tail -1)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
**規則:**
|
|
190
|
+
- `2>/dev/null` 避免解析錯誤的行產生 stderr
|
|
191
|
+
- `tail -1` 取最後一個 result 事件
|
|
192
|
+
- `// fallback` 處理 null/missing 欄位
|
|
193
|
+
|
|
194
|
+
**常見 JSONL 事件類型:**
|
|
195
|
+
- `tool_use` — Claude 呼叫工具
|
|
196
|
+
- `tool_result` — 工具回傳結果
|
|
197
|
+
- `result` — session 結束摘要(cost、turns、stop_reason)
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 模式 8:多步驟 Claude 腳本(--resume + --json-schema)
|
|
204
|
+
|
|
205
|
+
多個 claude 呼叫共用 session context,或要求結構化輸出:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
# 步驟 1:分析 + 取得 session_id
|
|
209
|
+
SESSION=$(unset CLAUDECODE; claude --print \
|
|
210
|
+
--output-format json \
|
|
211
|
+
--model claude-haiku \
|
|
212
|
+
--no-session-persistence \
|
|
213
|
+
"分析以下代碼並找出 TODO 列表:$(cat src/main.go)" \
|
|
214
|
+
| jq -r '.session_id')
|
|
215
|
+
|
|
216
|
+
# 步驟 2:接續 session context 做後續處理
|
|
217
|
+
RESULT=$(unset CLAUDECODE; claude --print \
|
|
218
|
+
--output-format json \
|
|
219
|
+
--resume "$SESSION" \
|
|
220
|
+
"根據剛才的 TODO 列表,產出優先級排序的 JSON" \
|
|
221
|
+
| jq -r '.result')
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
**結構化輸出(--json-schema):**
|
|
225
|
+
```bash
|
|
226
|
+
STRUCTURED=$(unset CLAUDECODE; claude --print \
|
|
227
|
+
--output-format json \
|
|
228
|
+
--json-schema '{"type":"object","properties":{"items":{"type":"array","items":{"type":"string"}}}}' \
|
|
229
|
+
"列出前 5 個最重要的安全漏洞" \
|
|
230
|
+
| jq '.structured_output.items[]')
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**工具限制(fan-out 安全模式):**
|
|
234
|
+
```bash
|
|
235
|
+
for file in $(cat files.txt); do
|
|
236
|
+
unset CLAUDECODE
|
|
237
|
+
claude --print \
|
|
238
|
+
--allowedTools "Edit,Bash(git add *),Bash(git commit *)" \
|
|
239
|
+
"遷移 $file 到新架構" & # 平行執行
|
|
240
|
+
done
|
|
241
|
+
wait
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
**規則:**
|
|
245
|
+
- `--no-session-persistence` 用於 CI/CD(不存 session 記錄)
|
|
246
|
+
- `--resume` 需要 session_id,從 `jq -r '.session_id'` 取得
|
|
247
|
+
- `--allowedTools` 比 `--disallowedTools` 更嚴格(白名單優先)
|
|
248
|
+
- 平行 fan-out 時每個 claude 呼叫要 `unset CLAUDECODE`
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 決策樹:選擇輸出方式
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
需要輸出給用戶?
|
|
256
|
+
├── 白天(07:00-01:00)→ Discord webhook
|
|
257
|
+
├── 夜間(01:00-07:00)→ 寫入 night-report.md,等早上
|
|
258
|
+
└── 錯誤訊息 → 寫入 log 檔
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## 決策樹:選擇工具
|
|
262
|
+
|
|
263
|
+
```
|
|
264
|
+
需要 AI 處理?
|
|
265
|
+
├── 在 session 內 → 直接使用 Claude Code
|
|
266
|
+
└── 在 cron/shell script 中 → claude --print (unset CLAUDECODE)
|
|
267
|
+
|
|
268
|
+
需要操作 Gmail?
|
|
269
|
+
├── 在 session 內(互動) → Gmail MCP
|
|
270
|
+
└── 在 cron/shell script 中 → gog CLI
|
|
271
|
+
|
|
272
|
+
需要處理 JSON?
|
|
273
|
+
├── 簡單讀寫 → jq
|
|
274
|
+
└── 複雜轉換 → Python
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
---
|
|
278
|
+
|
|
279
|
+
## Pattern 9:jq 錯誤防護與除錯迴圈
|
|
280
|
+
|
|
281
|
+
**來源:** `jq-null-safety-diagnostic-cycle.md` + `jq-reduce-scope-trap.md`
|
|
282
|
+
|
|
283
|
+
### Null Safety(最常見錯誤)
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
# ❌ .history 為 null 時崩潰
|
|
287
|
+
jq '.history[] | .value' state.json
|
|
288
|
+
|
|
289
|
+
# ✅ 加 ? 運算符,null 時跳過
|
|
290
|
+
jq '.history[]? | .value' state.json
|
|
291
|
+
|
|
292
|
+
# ✅ 讀取帶預設值(避免 null 傳播)
|
|
293
|
+
VALUE=$(jq -r '.key // "default"' state.json)
|
|
294
|
+
COUNT=$(jq '.items // [] | length' state.json)
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### reduce 作用域陷阱
|
|
298
|
+
|
|
299
|
+
```bash
|
|
300
|
+
# ❌ reduce 內部 . 是 accumulator,不是原始 input
|
|
301
|
+
reduce .goals[] as $goal ({}; .result = (.history[] | ...))
|
|
302
|
+
|
|
303
|
+
# ✅ 先捕捉原始 input 為 $root
|
|
304
|
+
. as $root |
|
|
305
|
+
reduce .goals[] as $goal ({}; .result = ($root.history[] | ...))
|
|
306
|
+
|
|
307
|
+
# ✅ 或改用 map + group_by 迴避 reduce
|
|
308
|
+
jq '[.goals[] | {id: .id, value: .value}] | group_by(.id)' state.json
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### 除錯迴圈
|
|
312
|
+
|
|
313
|
+
jq 報錯時的標準步驟:
|
|
314
|
+
1. 從錯誤訊息(`Cannot iterate over null`、`compile error`)定位代碼行
|
|
315
|
+
2. 提取 jq 表達式,**單獨在 CLI 測試**:`echo '{"key":null}' | jq '.key[]?'`
|
|
316
|
+
3. 識別問題類型:null context / macOS jq 版本限制 / reduce scope
|
|
317
|
+
4. 修復 + 驗證整個 script 輸出結構
|
|
318
|
+
|
|
319
|
+
### Pipe + Alternative 優先順序陷阱(`//` 陷阱)
|
|
320
|
+
|
|
321
|
+
jq 的 `|` 和 `//` 優先順序容易誤判,導致靜默錯誤:
|
|
322
|
+
|
|
323
|
+
```bash
|
|
324
|
+
# ❌ 看起來是 "context 或 task",實際解析卻不同:
|
|
325
|
+
jq '.actions | map(...)[$i].context // .actions | map(...)[$i].task' f.json
|
|
326
|
+
# 真實解析:.actions | (.context // .actions) | .task
|
|
327
|
+
# 當 .context 非 null 字串時:後段 | map(...) 試圖 iterate 字串 → "Cannot iterate over string" 錯誤
|
|
328
|
+
# 當 .context 為 null 時:返回整個 .actions 陣列,.task 取不到 → 空字串
|
|
329
|
+
|
|
330
|
+
# ✅ 括號明確指定 alternative 範圍:
|
|
331
|
+
jq '.actions | map(...)[$i] | (.context // .task)' f.json
|
|
332
|
+
|
|
333
|
+
# ✅ 規則:// 兩側都要用括號保護,特別是左側含 | 的表達式
|
|
334
|
+
VALUE=$(jq -r '.items[$i] | (.name // .id // empty)' state.json)
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
**實例(heartbeat.sh Bug C,2026-03-15):** `system-maintenance.sh` 寫入 context 為 `"Trimmed observations.jsonl from 1146 to 500 lines"` 的 TODO action,heartbeat.sh 解析時觸發 `Cannot iterate over string ("Trimmed ob...")` jq error,在 launchd.err.log 持續出現。
|
|
338
|
+
|
|
339
|
+
### macOS jq 版本注意
|
|
340
|
+
|
|
341
|
+
```bash
|
|
342
|
+
# macOS jq 1.7.1 不支援 try...catch,改用:
|
|
343
|
+
jq '.field // empty' file.json 2>/dev/null || echo "fallback"
|
|
344
|
+
|
|
345
|
+
# 安全讀取 JSONL(忽略解析失敗的行)
|
|
346
|
+
jq -r 'select(.type=="result") | .value' log.jsonl 2>/dev/null | tail -1
|
|
347
|
+
```
|