@seanyao/roll 2026.510.4 → 2026.510.6

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/README.md CHANGED
@@ -146,7 +146,7 @@ Research → Design → Build → Check → Fix → (loop)
146
146
  | Skill | Phase | Description |
147
147
  |-------|-------|-------------|
148
148
  | `$roll-build` | DESIGN+BUILD | Universal entry: Story ID, fix ID, or free-text fly mode |
149
- | `$roll-design` | DESIGN | Explore approachesdesign solution → write Stories to BACKLOG |
149
+ | `$roll-design` | DESIGN | Multi-turn discuss[peer review] → DDD model → solution design [peer review] → write Stories to BACKLOG |
150
150
  | `$roll-fix` | FIX | Bug fix / hotfix from BACKLOG |
151
151
  | `$roll-jot` | Support | Fast capture a bug or idea into BACKLOG.md |
152
152
  | `$roll-spar` | BUILD | Adversarial TDD — Attacker writes tests, Defender writes code |
package/bin/roll CHANGED
@@ -4,7 +4,7 @@ set -euo pipefail
4
4
  # Roll — AI Agent Convention Manager
5
5
  # Single source of truth for how all AI coding agents behave.
6
6
 
7
- VERSION="2026.510.4"
7
+ VERSION="2026.510.6"
8
8
  ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
9
9
  ROLL_CONFIG="${ROLL_HOME}/config.yaml"
10
10
  ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
@@ -1332,7 +1332,16 @@ _peer_call() {
1332
1332
  output="$(kimi --quiet -p "$prompt" 2>"$stderr_log" || true)"
1333
1333
  ;;
1334
1334
  pi)
1335
- output="$(pi -p --mode text "$prompt" 2>"$stderr_log" || true)"
1335
+ output="$(pi -p "$prompt" 2>"$stderr_log" || true)"
1336
+ ;;
1337
+ deepseek)
1338
+ output="$(deepseek "$prompt" 2>"$stderr_log" || true)"
1339
+ ;;
1340
+ codex)
1341
+ output="$(codex exec --json --output-last-message "$prompt" 2>"$stderr_log" || true)"
1342
+ ;;
1343
+ opencode)
1344
+ output="$(opencode run "$prompt" 2>"$stderr_log" || true)"
1336
1345
  ;;
1337
1346
  *)
1338
1347
  err "Unsupported peer: $to 不支持的 peer: $to"
@@ -1639,6 +1648,268 @@ cmd_peer_help() {
1639
1648
  echo " help Show this help 显示帮助"
1640
1649
  }
1641
1650
 
1651
+ # ═══════════════════════════════════════════════════════════════════════════════
1652
+ # AGENT — per-project agent configuration
1653
+ # ═══════════════════════════════════════════════════════════════════════════════
1654
+
1655
+ _project_agent() {
1656
+ if [[ -f ".roll.yaml" ]] && grep -q "^agent:" .roll.yaml 2>/dev/null; then
1657
+ grep "^agent:" .roll.yaml | awk '{print $2}' | tr -d '"' | head -1
1658
+ elif [[ -f "$ROLL_CONFIG" ]] && grep -q "primary_agent:" "$ROLL_CONFIG" 2>/dev/null; then
1659
+ grep "primary_agent:" "$ROLL_CONFIG" | awk '{print $2}' | tr -d '"' | head -1
1660
+ else
1661
+ echo "claude"
1662
+ fi
1663
+ }
1664
+
1665
+ _agent_run_skill() {
1666
+ local skill="$1"
1667
+ local agent; agent=$(_project_agent)
1668
+ local skill_file="${ROLL_HOME}/skills/${skill}/SKILL.md"
1669
+ [[ -f "$skill_file" ]] || { err "Skill not found: ${skill}"; return 1; }
1670
+ case "$agent" in
1671
+ claude) claude -p "$(cat "$skill_file")" ;;
1672
+ kimi) kimi --quiet -p "$(cat "$skill_file")" ;;
1673
+ deepseek) deepseek "$(cat "$skill_file")" ;;
1674
+ pi) pi -p "$(cat "$skill_file")" ;;
1675
+ codex) codex exec --json --output-last-message "$(cat "$skill_file")" ;;
1676
+ opencode) opencode run "$(cat "$skill_file")" ;;
1677
+ *) err "Unknown agent '${agent}'. Run: roll agent use <claude|kimi|deepseek|pi|codex|opencode>"; return 1 ;;
1678
+ esac
1679
+ }
1680
+
1681
+ cmd_agent() {
1682
+ local subcmd="${1:-}"; shift || true
1683
+ case "$subcmd" in
1684
+ use)
1685
+ local name="${1:-}"
1686
+ [[ -z "$name" ]] && { err "Usage: roll agent use <claude|kimi|deepseek|pi|codex|opencode>"; exit 1; }
1687
+ command -v "$name" &>/dev/null || warn "${name} not found in PATH — setting anyway 未找到,仍写入配置"
1688
+ if [[ -f ".roll.yaml" ]] && grep -q "^agent:" .roll.yaml; then
1689
+ local tmp; tmp=$(mktemp) && sed "s/^agent:.*/agent: ${name}/" .roll.yaml > "$tmp" && mv "$tmp" .roll.yaml
1690
+ else
1691
+ echo "agent: ${name}" >> .roll.yaml
1692
+ fi
1693
+ ok "Agent set to ${name} for this project 当前项目 agent 已设为 ${name}"
1694
+ local project_path; project_path=$(pwd -P)
1695
+ crontab -l 2>/dev/null | grep -q "roll-loop:${project_path}" && \
1696
+ info "Takes effect on next scheduled run — or: roll loop now"
1697
+ ;;
1698
+ list)
1699
+ echo ""; echo " Available agents 可用 agent:"; echo ""
1700
+ local current; current=$(_project_agent)
1701
+ for a in claude kimi deepseek opencode codex pi; do
1702
+ if command -v "$a" &>/dev/null; then
1703
+ [[ "$a" == "$current" ]] && echo -e " ${GREEN}✓ ${a}${NC} (current)" \
1704
+ || echo -e " ${GREEN}✓ ${a}${NC}"
1705
+ else
1706
+ echo -e " ${YELLOW}✗ ${a}${NC} (not installed)"
1707
+ fi
1708
+ done; echo ""
1709
+ ;;
1710
+ "")
1711
+ local agent; agent=$(_project_agent)
1712
+ local src="global"; [[ -f ".roll.yaml" ]] && grep -q "^agent:" .roll.yaml 2>/dev/null && src="project (.roll.yaml)"
1713
+ echo -e "\n Agent ${CYAN}${agent}${NC} (${src})\n"
1714
+ echo " roll agent use <name> — switch agent for this project"
1715
+ echo " roll agent list — show installed agents"; echo ""
1716
+ ;;
1717
+ *) err "Unknown subcommand: $subcmd"; echo "Usage: roll agent [use <name>|list]"; exit 1 ;;
1718
+ esac
1719
+ }
1720
+
1721
+ # ═══════════════════════════════════════════════════════════════════════════════
1722
+ # LOOP — autonomous BACKLOG executor management
1723
+ # ═══════════════════════════════════════════════════════════════════════════════
1724
+
1725
+ _LOOP_TAG="# roll-loop"
1726
+ _LOOP_STATE="${HOME}/.shared/roll/loop/state.yaml"
1727
+ _LOOP_ALERT="${HOME}/.shared/roll/loop/ALERT.md"
1728
+
1729
+ _agent_skill_cmd() {
1730
+ local skill_path="$1"
1731
+ local agent; agent=$(_project_agent)
1732
+ case "$agent" in
1733
+ claude) echo "claude -p \"\$(cat '${skill_path}')\"" ;;
1734
+ kimi) echo "kimi --quiet -p \"\$(cat '${skill_path}')\"" ;;
1735
+ deepseek) echo "deepseek \"\$(cat '${skill_path}')\"" ;;
1736
+ pi) echo "pi -p \"\$(cat '${skill_path}')\"" ;;
1737
+ codex) echo "codex exec --json --output-last-message \"\$(cat '${skill_path}')\"" ;;
1738
+ opencode) echo "opencode run \"\$(cat '${skill_path}')\"" ;;
1739
+ *) err "Unknown agent '${agent}'. Run: roll agent use <claude|kimi|deepseek|pi|codex|opencode>"; return 1 ;;
1740
+ esac
1741
+ }
1742
+
1743
+ cmd_loop() {
1744
+ local subcmd="${1:-status}"; shift || true
1745
+ case "$subcmd" in
1746
+ on) _loop_on ;;
1747
+ off) _loop_off ;;
1748
+ now) _loop_now ;;
1749
+ status) _loop_status ;;
1750
+ resume) _loop_resume ;;
1751
+ reset) _loop_reset ;;
1752
+ *) err "Usage: roll loop <on|off|now|status|resume|reset>"; exit 1 ;;
1753
+ esac
1754
+ }
1755
+
1756
+ _loop_on() {
1757
+ local project_path; project_path=$(pwd -P)
1758
+ local agent; agent=$(_project_agent)
1759
+ local sd="${ROLL_HOME}/skills"
1760
+
1761
+ if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1762
+ warn "Loop already enabled for this project 当前项目 loop 已启用"; return 0
1763
+ fi
1764
+
1765
+ mkdir -p ~/.shared/roll/{loop,dream,brief}
1766
+
1767
+ local loop_cmd dream_cmd brief_cmd
1768
+ loop_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-loop/SKILL.md") >> ~/.shared/roll/loop/cron.log 2>&1"
1769
+ dream_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-.dream/SKILL.md") >> ~/.shared/roll/dream/cron.log 2>&1"
1770
+ brief_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-brief/SKILL.md") >> ~/.shared/roll/brief/cron.log 2>&1"
1771
+
1772
+ (
1773
+ crontab -l 2>/dev/null
1774
+ echo "0 * * * * ${loop_cmd} ${_LOOP_TAG}:${project_path}"
1775
+ echo "0 1 * * * ${dream_cmd} ${_LOOP_TAG}:${project_path}"
1776
+ echo "0 8 * * * ${brief_cmd} ${_LOOP_TAG}:${project_path}"
1777
+ ) | crontab -
1778
+
1779
+ ok "Loop enabled 已启用"
1780
+ echo " • roll-loop every hour :00 每小时整点"
1781
+ echo " • roll-.dream nightly at 01:00 每晚 01:00"
1782
+ echo " • roll-brief daily at 08:00 每天 08:00"
1783
+ echo " • Agent: ${agent} (change: roll agent use <name>)"
1784
+ }
1785
+
1786
+ _loop_off() {
1787
+ local project_path; project_path=$(pwd -P)
1788
+ if ! crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1789
+ warn "Loop not enabled for this project 当前项目 loop 未启用"; return 0
1790
+ fi
1791
+ crontab -l 2>/dev/null | grep -v "${_LOOP_TAG}:${project_path}" | crontab -
1792
+ ok "Loop disabled 已停用"
1793
+ }
1794
+
1795
+ _loop_now() {
1796
+ if [[ -f "$_LOOP_STATE" ]] && grep -q "status: running" "$_LOOP_STATE" 2>/dev/null; then
1797
+ warn "Loop already running loop 正在运行中"; return 0
1798
+ fi
1799
+ info "Triggering loop cycle... 正在触发一个周期..."
1800
+ _agent_run_skill "roll-loop"
1801
+ }
1802
+
1803
+ _loop_status() {
1804
+ local project_path; project_path=$(pwd -P)
1805
+ local agent; agent=$(_project_agent)
1806
+ echo ""
1807
+ if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1808
+ echo -e " Scheduler ${GREEN}● enabled${NC} Agent: ${CYAN}${agent}${NC}"
1809
+ else
1810
+ echo -e " Scheduler ${YELLOW}○ disabled${NC} run: roll loop on"
1811
+ fi
1812
+ [[ -f "$_LOOP_ALERT" ]] && { echo ""; echo -e " ${RED}⚠ ALERT:${NC}"; sed 's/^/ /' "$_LOOP_ALERT"; }
1813
+ [[ -f "$_LOOP_STATE" ]] && { echo ""; echo " State:"; sed 's/^/ /' "$_LOOP_STATE"; }
1814
+ echo ""
1815
+ }
1816
+
1817
+ _loop_resume() {
1818
+ if [[ ! -f "$_LOOP_STATE" ]]; then
1819
+ warn "No loop state found — nothing to resume 未找到 loop 状态,无需恢复"; return 0
1820
+ fi
1821
+ if grep -q "status: running" "$_LOOP_STATE" 2>/dev/null; then
1822
+ warn "Loop already running loop 正在运行中"; return 0
1823
+ fi
1824
+ info "Resuming loop from last state... 正在从上次状态恢复..."
1825
+ _agent_run_skill "roll-loop"
1826
+ }
1827
+
1828
+ _loop_reset() {
1829
+ if [[ -f "$_LOOP_STATE" ]]; then
1830
+ rm -f "$_LOOP_STATE"
1831
+ ok "Loop state cleared — will start fresh on next run loop 状态已清除,下次运行将重新开始"
1832
+ else
1833
+ info "No loop state to clear 无 loop 状态可清除"
1834
+ fi
1835
+ }
1836
+
1837
+ # ═══════════════════════════════════════════════════════════════════════════════
1838
+ # BRIEF — owner-facing project digest
1839
+ # ═══════════════════════════════════════════════════════════════════════════════
1840
+
1841
+ cmd_brief() {
1842
+ local briefs_dir="docs/briefs"
1843
+ local latest; latest=$(ls "${briefs_dir}"/*.md 2>/dev/null | sort | tail -1 || true)
1844
+
1845
+ if [[ -z "$latest" ]]; then
1846
+ info "No brief yet — generating... 暂无简报,正在生成..."
1847
+ _agent_run_skill "roll-brief"
1848
+ latest=$(ls "${briefs_dir}"/*.md 2>/dev/null | sort | tail -1 || true)
1849
+ else
1850
+ local mod_time now age
1851
+ mod_time=$(stat -c %Y "$latest" 2>/dev/null || stat -f %m "$latest" 2>/dev/null || echo 0)
1852
+ now=$(date +%s); age=$(( now - mod_time ))
1853
+ if (( age > 86400 )); then
1854
+ info "Brief is $(( age / 3600 ))h old — regenerating... 简报已 $(( age / 3600 )) 小时未更新,重新生成..."
1855
+ _agent_run_skill "roll-brief"
1856
+ latest=$(ls "${briefs_dir}"/*.md 2>/dev/null | sort | tail -1 || true)
1857
+ fi
1858
+ fi
1859
+
1860
+ [[ -f "$latest" ]] && cat "$latest"
1861
+ }
1862
+
1863
+ cmd_release() {
1864
+ local script="scripts/release.sh"
1865
+ if [[ ! -f "$script" ]]; then
1866
+ err "scripts/release.sh not found — run this command from the roll project root."
1867
+ exit 1
1868
+ fi
1869
+ # Sync CHANGELOG.md before releasing, using the configured agent
1870
+ local last_tag; last_tag=$(git tag --sort=-version:refname | grep "^v" | head -1)
1871
+ if [[ -n "$last_tag" ]] && ! git diff "${last_tag}..HEAD" --name-only | grep -q "CHANGELOG.md"; then
1872
+ info "CHANGELOG.md not updated since ${last_tag}, syncing via $(_project_agent)..."
1873
+ _agent_run_skill "roll-.changelog"
1874
+ fi
1875
+ bash "$script" "$@"
1876
+ }
1877
+
1878
+ # ─────────────────────────────────────────────────────────────────────────────
1879
+
1880
+ _dashboard() {
1881
+ local project_path; project_path=$(pwd -P)
1882
+ local project_name; project_name=$(basename "$project_path")
1883
+ local agent; agent=$(_project_agent)
1884
+
1885
+ echo -e "\n ${BOLD}${CYAN}${project_name}${NC} ${YELLOW}v${VERSION}${NC}\n"
1886
+
1887
+ if crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}"; then
1888
+ echo -e " Loop ${GREEN}● on${NC} Agent: ${CYAN}${agent}${NC}"
1889
+ else
1890
+ echo -e " Loop ${YELLOW}○ off${NC} run: roll loop on"
1891
+ fi
1892
+ [[ -f "$_LOOP_ALERT" ]] && echo -e " ${RED}⚠ ALERT — run: roll loop status${NC}"
1893
+
1894
+ local briefs_dir="docs/briefs"
1895
+ local latest; latest=$(ls "${briefs_dir}"/*.md 2>/dev/null | sort | tail -1 || true)
1896
+ if [[ -n "$latest" ]]; then
1897
+ local mod_time now age
1898
+ mod_time=$(stat -c %Y "$latest" 2>/dev/null || stat -f %m "$latest" 2>/dev/null || echo 0)
1899
+ now=$(date +%s); age=$(( (now - mod_time) / 3600 ))
1900
+ echo -e "\n Brief ${CYAN}$(basename "$latest")${NC} (${age}h ago)"
1901
+ grep -A1 "## Release Readiness" "$latest" 2>/dev/null | tail -1 | sed 's/^/ /'
1902
+ echo ""
1903
+ echo " run: roll brief for full brief"
1904
+ else
1905
+ echo -e "\n No brief yet — run: roll brief"
1906
+ fi
1907
+
1908
+ echo ""
1909
+ echo " roll loop on|off|now|status|resume|reset · roll brief · roll agent use <name>"
1910
+ echo ""
1911
+ }
1912
+
1642
1913
  # ═══════════════════════════════════════════════════════════════════════════════
1643
1914
  # MAIN
1644
1915
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -1661,11 +1932,17 @@ usage() {
1661
1932
  echo " init [Project] Create AGENTS.md + BACKLOG.md + docs/ 初始化项目工作流文件"
1662
1933
  echo " status [Diagnostic] Show current state 显示当前状态"
1663
1934
  echo " peer [Peer Review] Cross-agent negotiation 跨 Agent 协商对审"
1935
+ echo " loop <on|off|now|status|resume|reset> [Autonomous] Manage scheduled BACKLOG executor 管理自主执行循环"
1936
+ echo " brief [Digest] Show latest owner brief (regenerate if stale) 展示最新简报"
1937
+ echo " agent [use <name>|list] [Config] Per-project agent selection 切换项目 agent"
1664
1938
  echo ""
1665
1939
  echo "Examples / 示例:"
1666
1940
  echo " roll setup # New machine: first-time install 新机器首次安装"
1667
1941
  echo " roll update # Upgrade to latest version + re-sync 升级到最新版并重新同步"
1668
1942
  echo " roll init # New or re-merge project (run in project) 新建或重新合并(项目目录)"
1943
+ echo " roll loop on # Enable autonomous loop (cron) 启用自主执行循环"
1944
+ echo " roll brief # Show latest brief 查看最新简报"
1945
+ echo " roll agent use kimi # Switch this project to kimi 切换当前项目到 kimi"
1669
1946
 
1670
1947
  }
1671
1948
 
@@ -1679,9 +1956,13 @@ main() {
1679
1956
  init) cmd_init "$@" ;;
1680
1957
  status) cmd_status "$@" ;;
1681
1958
  peer) cmd_peer "$@" ;;
1959
+ loop) cmd_loop "$@" ;;
1960
+ brief) cmd_brief "$@" ;;
1961
+ agent) cmd_agent "$@" ;;
1962
+ release) cmd_release "$@" ;;
1682
1963
  version|--version|-v) echo "roll v${VERSION}" ;;
1683
1964
  help|--help|-h) usage ;;
1684
- "") usage; _show_changelog ;;
1965
+ "") [[ -f "BACKLOG.md" ]] && _dashboard || { usage; _show_changelog; } ;;
1685
1966
  *)
1686
1967
  err "Unknown command: $cmd 未知命令: $cmd"
1687
1968
  echo ""
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.510.4",
3
+ "version": "2026.510.6",
4
4
  "description": "Roll — Roll out features with AI agents",
5
5
  "scripts": {
6
6
  "test": "find tests/unit tests/integration -name '*.bats' | sort | xargs ./tests/helpers/bats-core/bin/bats"
@@ -0,0 +1,158 @@
1
+ ---
2
+ hidden: true
3
+ name: roll-.dream
4
+ license: MIT
5
+ allowed-tools: "Read, Glob, Grep, Bash(git:*), Write, Edit"
6
+ description: |
7
+ Nightly code and architecture health scan. Passively triggered by scheduler
8
+ (cron or GitHub Actions), not invoked by users directly. Detects dead code,
9
+ architectural drift from domain model, pruning candidates, and emerging patterns.
10
+ Outputs REFACTOR entries to BACKLOG.md and a daily log to docs/dream/.
11
+ Distinct from roll-sentinel: sentinel monitors runtime behavior; dream reviews
12
+ code structure and architectural health.
13
+ ---
14
+
15
+ # Roll Dream (Nightly Code Health Scan)
16
+
17
+ > Follows the Architecture Constraints, Development Discipline, and Engineering
18
+ > Common Sense defined in the project AGENTS.md.
19
+
20
+ **Passively triggered — do not invoke manually.** Runs nightly via scheduler.
21
+ Consolidates structural signals accumulated during the day, surfaces technical
22
+ debt, and writes REFACTOR entries to BACKLOG. The human reviews the dream log
23
+ in the morning brief.
24
+
25
+ ## Distinction from roll-sentinel
26
+
27
+ | | roll-sentinel | roll-.dream |
28
+ |--|--------------|------------|
29
+ | **Trigger** | Post-deploy, scheduled patrol | Nightly, fixed schedule |
30
+ | **Target** | Runtime behavior (production) | Code structure (codebase) |
31
+ | **Output** | FIX entries | REFACTOR entries + dream log |
32
+ | **Question** | "Is the product working?" | "Is the code healthy?" |
33
+
34
+ ## Scan Logic
35
+
36
+ Run all four scans every night. Each scan is independent.
37
+
38
+ ### Scan 1 — Dead Code
39
+
40
+ Find code that is defined but never referenced:
41
+
42
+ ```bash
43
+ # Unused exports (TypeScript/JS)
44
+ grep -r "^export " src/ --include="*.ts" -l | while read f; do
45
+ symbol=$(grep -o "export \(function\|const\|class\|type\|interface\) [A-Za-z]*" "$f" | awk '{print $NF}')
46
+ # check if symbol appears anywhere else in the codebase
47
+ done
48
+
49
+ # Unused files (no imports pointing to them)
50
+ # Git: files not touched in 90+ days and not imported anywhere
51
+ git log --since="90 days ago" --name-only --format="" | sort -u > /tmp/recently_touched
52
+ ```
53
+
54
+ Flag: files or exports with zero references outside their own file.
55
+
56
+ ### Scan 2 — Architectural Drift
57
+
58
+ Compare current code structure against the domain model in `docs/domain/`:
59
+
60
+ ```bash
61
+ # Read context-map.md and ubiquitous-language.md if they exist
62
+ # Check: do module/directory names match Bounded Context names?
63
+ # Check: do cross-module imports respect Context boundaries?
64
+ # Check: do any modules import directly across Context lines without ACL?
65
+ ```
66
+
67
+ Flag: modules that import directly from a different Bounded Context without
68
+ an Anti-Corruption Layer, or module names that have diverged from the
69
+ Ubiquitous Language.
70
+
71
+ ### Scan 3 — Pruning Candidates
72
+
73
+ Find over-engineering that can be simplified:
74
+
75
+ ```bash
76
+ # Abstractions with only one implementation
77
+ grep -r "interface \|abstract class " src/ --include="*.ts" -l
78
+
79
+ # Wrapper functions that do nothing but delegate
80
+ # Config flags that are never toggled (always true or always false)
81
+ # Error handling for paths that cannot occur
82
+ ```
83
+
84
+ Flag: interfaces with exactly one implementor, feature flags frozen to one
85
+ value, wrapper layers with no logic.
86
+
87
+ ### Scan 4 — Emerging Patterns
88
+
89
+ Find repeated structures that warrant extraction:
90
+
91
+ ```bash
92
+ # Duplicated code blocks (>10 lines, similar structure)
93
+ # Similar file structures across multiple modules
94
+ # Repeated try/catch patterns with identical handling
95
+ ```
96
+
97
+ Flag: any pattern appearing 3+ times that could be extracted into a shared
98
+ utility or convention.
99
+
100
+ ## Output
101
+
102
+ ### REFACTOR Entry (BACKLOG.md)
103
+
104
+ For each finding that warrants action, append one row to the `## ♻️ Refactor`
105
+ section of BACKLOG.md:
106
+
107
+ ```markdown
108
+ | REFACTOR-XXX | {one-line description} — flagged by dream {YYYY-MM-DD} | 📋 Todo |
109
+ ```
110
+
111
+ **Threshold**: only flag items where the fix would meaningfully reduce
112
+ complexity or prevent future bugs. Ignore cosmetic issues.
113
+
114
+ ### Dream Log (docs/dream/YYYY-MM-DD.md)
115
+
116
+ Always write a log, even when no REFACTOR entries are created:
117
+
118
+ ```markdown
119
+ # Dream Log {YYYY-MM-DD}
120
+
121
+ ## Summary
122
+ - Scans run: Dead Code / Architectural Drift / Pruning / Patterns
123
+ - Findings: {N} flagged, {M} REFACTOR entries created
124
+
125
+ ## Dead Code
126
+ {finding or "Nothing flagged."}
127
+
128
+ ## Architectural Drift
129
+ {finding or "No drift detected."}
130
+
131
+ ## Pruning Candidates
132
+ {finding or "Nothing flagged."}
133
+
134
+ ## Emerging Patterns
135
+ {finding or "No patterns detected."}
136
+
137
+ ## REFACTOR Entries Created
138
+ {list or "None."}
139
+ ```
140
+
141
+ ## Scheduler Configuration
142
+
143
+ roll-.dream runs **locally** — it reads the local codebase directly.
144
+
145
+ ### Local cron (default)
146
+
147
+ Installed automatically via `roll loop on` alongside roll-loop and roll-brief.
148
+ The cron entry is generated using the configured agent — no manual cron editing needed.
149
+
150
+ ## Failure Handling
151
+
152
+ If the scan fails partway through:
153
+
154
+ 1. Write partial results to `docs/dream/YYYY-MM-DD.md` with a `## Status: PARTIAL` header
155
+ 2. Do not write incomplete REFACTOR entries to BACKLOG
156
+ 3. Log the error to `~/.shared/roll/dream/error.log`
157
+
158
+ The scheduler (not this skill) is responsible for retry and human notification.
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: roll-brief
3
+ license: MIT
4
+ allowed-tools: "Read, Glob, Grep, Write, Bash(git:*)"
5
+ description: |
6
+ Owner-facing briefing generator. Summarizes what the agent has done since
7
+ the last brief: completed US/FIX/REFACTOR, in-progress items, BACKLOG queue
8
+ status, escalations requiring human attention, and a release-readiness verdict.
9
+ Three trigger modes: auto on Feature completion, daily morning schedule,
10
+ or manual $roll-brief invocation. Distinct from roll-.changelog (user-facing
11
+ release notes) — this is an internal digest for the product owner.
12
+ ---
13
+
14
+ # Roll Brief
15
+
16
+ > Follows the Architecture Constraints, Development Discipline, and Engineering
17
+ > Common Sense defined in the project AGENTS.md.
18
+
19
+ Owner-facing digest of autonomous agent activity. Gives the human everything
20
+ needed to decide whether to run `roll-release` — without having to read every
21
+ commit or diff.
22
+
23
+ ## Distinct from roll-.changelog
24
+
25
+ | | roll-brief | roll-.changelog |
26
+ |--|-----------|----------------|
27
+ | **Audience** | Product owner (internal) | End users (public) |
28
+ | **Content** | All activity including REFACTOR, escalations, health signals | Only user-visible feature changes |
29
+ | **Trigger** | Feature completion / daily / on-demand | Post-deploy |
30
+ | **Tone** | Operational, candid | Product-facing, polished |
31
+
32
+ ## Trigger Modes
33
+
34
+ ### 1. Feature Completion (auto)
35
+
36
+ Triggered by `roll-loop` when a set of related Stories under one Feature are
37
+ all marked ✅ Done. The loop passes the Feature name as context.
38
+
39
+ ### 2. Daily Morning (scheduled)
40
+
41
+ Runs at a fixed time each morning (configurable in `~/.roll/config.yaml`).
42
+ Covers all activity since the previous brief.
43
+
44
+ ```yaml
45
+ # ~/.roll/config.yaml
46
+ brief:
47
+ daily_time: "08:00" # local time
48
+ timezone: "Asia/Shanghai"
49
+ ```
50
+
51
+ ### 3. On-Demand
52
+
53
+ ```bash
54
+ $roll-brief # since last brief
55
+ $roll-brief --since 2026-05-09 # since specific date
56
+ $roll-brief --feature auth # scoped to one feature
57
+ ```
58
+
59
+ ## Workflow
60
+
61
+ ### Step 1 — Determine Scope
62
+
63
+ ```bash
64
+ # Find timestamp of last brief
65
+ ls docs/briefs/ | sort | tail -1
66
+
67
+ # Read BACKLOG.md — collect all status changes since last brief
68
+ # Read git log — commits since last brief timestamp
69
+ git log --since="{last_brief_timestamp}" --oneline
70
+ ```
71
+
72
+ ### Step 2 — Collect Activity
73
+
74
+ From BACKLOG.md and git log, classify all items since last brief:
75
+
76
+ - **Completed**: US-XXX ✅, FIX-XXX ✅, REFACTOR-XXX ✅
77
+ - **In progress**: items currently 🔨
78
+ - **Queue**: items still 📋 Todo (ordered by priority)
79
+ - **Dream findings**: any REFACTOR entries added by roll-.dream since last brief
80
+ - **Escalations**: any ALERT files in `~/.shared/roll/loop/` or `~/.shared/roll/dream/`
81
+
82
+ ### Step 3 — Assess Release Readiness
83
+
84
+ A simple heuristic — not a gate, just a signal for the human:
85
+
86
+ ```
87
+ ✅ Release candidate if:
88
+ - No 🔨 in-progress US items
89
+ - No open ESCALATE alerts
90
+ - CI is green (check latest workflow run)
91
+ - No critical REFACTOR entries flagged in last 48h
92
+
93
+ ⚠️ Hold if any of the above is false
94
+ ```
95
+
96
+ ### Step 4 — Write Brief
97
+
98
+ Output to `docs/briefs/YYYY-MM-DD-HH.md`:
99
+
100
+ ```markdown
101
+ # Brief {YYYY-MM-DD HH:mm}
102
+
103
+ ## What's Done
104
+ | ID | Description | Type |
105
+ |----|-------------|------|
106
+ | US-XXX | {title} | Story |
107
+ | FIX-XXX | {title} | Fix |
108
+ | REFACTOR-XXX | {title} | Refactor |
109
+
110
+ ## In Progress
111
+ | ID | Description |
112
+ |----|-------------|
113
+ | US-XXX | {title} — started {date} |
114
+
115
+ ## Queue ({N} items)
116
+ | ID | Description | Priority |
117
+ |----|-------------|----------|
118
+ | US-XXX | {title} | high |
119
+
120
+ ## Overnight Dream Findings
121
+ {summary from docs/dream/ since last brief, or "No new findings."}
122
+
123
+ ## Escalations Requiring Human Input
124
+ {any alerts, or "None — agent operating normally."}
125
+
126
+ ## Release Readiness
127
+ {✅ Release candidate / ⚠️ Hold — reason}
128
+
129
+ ---
130
+ *Next scheduled brief: {datetime}*
131
+ ```
132
+
133
+ ### Step 5 — Notify
134
+
135
+ After writing the file, print the brief path so it's visible in the terminal
136
+ or CI log:
137
+
138
+ ```
139
+ 📋 Brief written: docs/briefs/2026-05-10-08.md
140
+ Release readiness: ✅ Release candidate
141
+ ```
142
+
143
+ If there are escalations, print them prominently so they're impossible to miss.
144
+
145
+ ## Scheduler Configuration
146
+
147
+ roll-brief runs **locally**, triggered either by roll-loop (on Feature
148
+ completion) or by local cron (daily morning). The agent reads local
149
+ BACKLOG state and git history directly.
150
+
151
+ ### Local cron (daily morning)
152
+
153
+ Installed automatically via `roll loop on` alongside roll-loop and roll-.dream.
154
+ The cron entry is generated using the configured agent — no manual cron editing needed.
155
+
156
+ ### Triggered by roll-loop
157
+
158
+ When roll-loop detects a Feature is fully complete, it invokes roll-brief
159
+ automatically — no separate cron entry needed for that trigger.
@@ -263,6 +263,43 @@ MICRO-STEP {N}: {description of smallest testable change}
263
263
 
264
264
  Accumulate 3–5 micro-commits per Action. Each commit is a guaranteed working state.
265
265
 
266
+ #### Architectural Friction Signal (non-blocking)
267
+
268
+ While implementing, watch for these signals:
269
+
270
+ - This Action requires touching code in 3+ unrelated modules
271
+ - The existing module boundary has to be bent or bypassed to make this work
272
+ - A data structure or interface needs to change in a way that ripples across contexts
273
+ - The implementation feels "wrong" even when the test passes
274
+
275
+ When any signal appears, **do not stop — flag it**:
276
+
277
+ ```bash
278
+ # 1. Append to BACKLOG.md under ## ♻️ Refactor
279
+ # REFACTOR-XXX | <one-line description> | 📋 Todo
280
+
281
+ # 2. Append a brief entry to docs/features/refactor-log.md
282
+ ```
283
+
284
+ **REFACTOR entry format in BACKLOG.md:**
285
+
286
+ ```markdown
287
+ | REFACTOR-001 | Extract payment boundary — leaking into Order module during US-AUTH-003 | 📋 Todo |
288
+ ```
289
+
290
+ **refactor-log.md entry format:**
291
+
292
+ ```markdown
293
+ ## REFACTOR-001 Extract payment boundary
294
+
295
+ **Flagged**: {YYYY-MM-DD} during US-XXX
296
+ **Signal**: {which friction signal triggered this}
297
+ **Observation**: {1–3 sentences describing what felt wrong}
298
+ **Suggested scope**: {rough sense of what a fix would touch}
299
+ ```
300
+
301
+ Then continue implementing the current Story normally.
302
+
266
303
  ### Phase 5.5: E2E Deposit
267
304
 
268
305
  After TCR micro-steps pass, deposit an E2E test for this Story's core user flow.
@@ -480,6 +517,9 @@ git push
480
517
 
481
518
  ### Phase 12: Report & Celebrate
482
519
 
520
+ **Before reporting, run `$roll-.changelog`** to sync completed Story to CHANGELOG.md.
521
+ This is mandatory — release notes depend on it.
522
+
483
523
  ```
484
524
  ✅ Pushed to GitHub: origin/main
485
525
  🚀 Deployed: <url>
@@ -564,7 +604,7 @@ Before creating any file or directory:
564
604
  - [ ] **Verification Gate passed** (fresh evidence for tests, build, deploy, no regression)
565
605
  - [ ] **BACKLOG.md index status updated** (📋 → ✅, REQUIRED)
566
606
  - [ ] **`docs/features/<feature>.md` US section updated** (Completed date + [x] ACs, REQUIRED)
567
- - [ ] **CHANGELOG.md updated** via `$roll-.changelog`
607
+ - [ ] **CHANGELOG.md updated** via `$roll-.changelog` (REQUIRED)
568
608
  - [ ] Summary reported to user
569
609
 
570
610
  ---
@@ -144,6 +144,13 @@ User Input
144
144
  │ Approach confirmed
145
145
 
146
146
  ┌─────────────────────────────┐
147
+ │ [peer] Direction Review │ ← if complexity=large or cross-context; 10s opt-out
148
+ │ Skill("roll-peer", │
149
+ │ tag="architecture") │
150
+ └─────────────┬───────────────┘
151
+ │ AGREE / skipped
152
+
153
+ ┌─────────────────────────────┐
147
154
  │ 2. Analyze + DDD Depth │ ← Detect scope: Greenfield / Story / Fix
148
155
  │ - Requirement analysis │
149
156
  │ - Feasibility assessment │
@@ -208,6 +215,12 @@ User Input
208
215
 
209
216
 
210
217
  ┌─────────────────────────────────────────┐
218
+ │ [peer] Plan Review │ ← if complexity=large; 10s opt-out
219
+ │ Skill("roll-peer", tag="architecture")│
220
+ └──────────────────┬──────────────────────┘
221
+ │ AGREE / skipped
222
+
223
+ ┌─────────────────────────────────────────┐
211
224
  │ 4. Split into Stories │
212
225
  │ - INVEST principles │
213
226
  │ - Bounded Context → US domain prefix │
@@ -435,6 +448,60 @@ Domain: Order Context > Order Aggregate > OrderItem Entity
435
448
  - More than 2 viable technical paths exist
436
449
  - Requirement involves an unfamiliar tech stack or new domain
437
450
 
451
+ ### How to Conduct the Discussion
452
+
453
+ Discuss is **multi-turn by default**. The goal is to reach clarity together, not to produce a complete comparison matrix in one shot.
454
+
455
+ **Step 1 — Understand before proposing**
456
+
457
+ Before listing options, make sure the core problem is clear. If context is thin, ask 1–2 focused questions first:
458
+
459
+ ```
460
+ Before I lay out the options — can you tell me [specific constraint / scale / existing system boundary]?
461
+ ```
462
+
463
+ Only skip this if the context is already rich enough to reason from.
464
+
465
+ **Step 2 — Offer an opinionated starting point, not a menu**
466
+
467
+ Don't dump 4 options at once. Lead with a concrete recommendation and the key tradeoff:
468
+
469
+ ```
470
+ My read: go with X. The main tradeoff is [Y vs Z]. Want me to walk through why, or should I compare against [alternative] first?
471
+ ```
472
+
473
+ Then wait. Let the user redirect.
474
+
475
+ **Step 3 — Follow the thread**
476
+
477
+ If the user wants to dig into a specific option or challenge an assumption, stay on that thread. Don't pivot back to the full comparison until the current thread is resolved.
478
+
479
+ **Step 4 — Surface hidden assumptions explicitly**
480
+
481
+ When a direction starts to crystallize, name the assumptions holding it up:
482
+
483
+ ```
484
+ This only holds if [assumption]. Is that true for your situation?
485
+ ```
486
+
487
+ **Step 5 — Name convergence before triggering the gate**
488
+
489
+ When the discussion reaches a clear conclusion, summarize it explicitly before asking to proceed:
490
+
491
+ ```
492
+ Looks like we've landed on: [decision]. The key reasons: [1–2 points].
493
+ ```
494
+
495
+ Then trigger the gate.
496
+
497
+ **Gate rule** — after convergence is named, always end with this explicit prompt and **wait for user reply before proceeding**:
498
+
499
+ ```
500
+ ➡️ Continue to solution design, or keep exploring?
501
+ ```
502
+
503
+ Do **not** infer "approach confirmed" from the user's reaction to the comparison. Only proceed to Step 2 (Analyze) when the user explicitly says to continue (e.g., "yes", "proceed", "go ahead", "design it").
504
+
438
505
  **Can stop at any time** — if after discussion the user says "let's not do it" or "let me think about it", there's no need to continue to the planning phase.
439
506
 
440
507
  ---
@@ -559,6 +626,24 @@ $roll-design --fix "fix login API 404" → Create FIX-AUTH-001
559
626
  $roll-fix FIX-AUTH-001 → Quick fix
560
627
  ```
561
628
 
629
+ ### With roll-peer
630
+
631
+ Two checkpoints, both with 10s opt-out:
632
+
633
+ ```
634
+ 1. After Discuss — Direction Review
635
+ Approach confirmed → [peer, tag=architecture] → challenge the direction before DDD
636
+ Trigger: complexity=large OR requirement touches multiple Bounded Contexts
637
+
638
+ 2. After Solution Design — Plan Review
639
+ Plan written → [peer, tag=architecture] → full plan review before story split
640
+ Trigger: complexity=large (greenfield always qualifies)
641
+ ```
642
+
643
+ On AGREE or user skip → continue to the next step normally.
644
+ On REFINE/OBJECT → incorporate feedback, regenerate the relevant output, re-trigger peer.
645
+ On ESCALATE → present both proposals to user for final call.
646
+
562
647
  ---
563
648
 
564
649
  ## Project Context Rule
@@ -320,6 +320,8 @@ Change the Status of the corresponding row from `📋 Todo` to `✅ Done`.
320
320
 
321
321
  ### 12. Update Changelog
322
322
 
323
+ **Mandatory** — release notes depend on this step. Do not skip.
324
+
323
325
  ```bash
324
326
  $roll-.changelog
325
327
  ```
@@ -0,0 +1,204 @@
1
+ ---
2
+ name: roll-loop
3
+ license: MIT
4
+ allowed-tools: "Read, Glob, Grep, Write, Edit, Bash(git:*), Bash(cat:*), Skill"
5
+ description: |
6
+ Autonomous BACKLOG executor. Runs on a schedule (hourly via cron or GitHub
7
+ Actions), scans BACKLOG.md for 📋 Todo items, and routes each to the
8
+ appropriate skill: US-XXX → $roll-build, FIX-XXX → $roll-fix,
9
+ REFACTOR-XXX → $roll-build. Handles agent fallback on token/network failure.
10
+ Never executes roll-release autonomously — release is always a human decision.
11
+ Triggers roll-brief when a Feature completes.
12
+ ---
13
+
14
+ # Roll Loop (Autonomous BACKLOG Executor)
15
+
16
+ > Follows the Architecture Constraints, Development Discipline, and Engineering
17
+ > Common Sense defined in the project AGENTS.md.
18
+
19
+ Runs on a schedule. Picks up pending BACKLOG items and executes them without
20
+ human intervention. The human stays informed via `roll-brief` and retains
21
+ sole authority over `roll-release`.
22
+
23
+ ## Execution Boundary
24
+
25
+ **What roll-loop executes autonomously:**
26
+ - US-XXX (User Stories) → `$roll-build`
27
+ - FIX-XXX (Bug fixes) → `$roll-fix`
28
+ - REFACTOR-XXX (Refactors) → `$roll-build`
29
+
30
+ **What roll-loop never executes:**
31
+ - `roll-release` — production deployment is always a human decision
32
+ - Any Story marked 🚫 Hold or flagged for human review
33
+ - Destructive operations outside normal skill scope
34
+
35
+ ## Configuration
36
+
37
+ ```yaml
38
+ # ~/.roll/config.yaml
39
+ loop:
40
+ primary_agent: claude # claude | deepseek | kimi
41
+ fallback_agent: deepseek # used when primary fails
42
+ max_items_per_run: 3 # limit parallel risk; adjust as needed
43
+ brief_on_feature_complete: true
44
+ retry_backoff: [2, 4, 8, 16] # seconds, exponential
45
+ ```
46
+
47
+ ## Workflow
48
+
49
+ ### Step 1 — Read State
50
+
51
+ ```bash
52
+ STATE_FILE=~/.shared/roll/loop/state.yaml
53
+
54
+ # If a previous run was interrupted, resume from state
55
+ if [ -f "$STATE_FILE" ] && grep -q "status: interrupted" "$STATE_FILE"; then
56
+ # Resume the interrupted item first
57
+ fi
58
+ ```
59
+
60
+ ### Step 2 — Scan BACKLOG
61
+
62
+ Read `BACKLOG.md`. Collect all rows where Status = `📋 Todo`, in order:
63
+
64
+ Priority: FIX-XXX first (bugs block progress), then US-XXX, then REFACTOR-XXX.
65
+
66
+ Cap at `max_items_per_run` to limit blast radius per cycle.
67
+
68
+ ### Step 3 — Route and Execute
69
+
70
+ For each item:
71
+
72
+ ```
73
+ Item type → Skill invoked
74
+ ─────────────────────────────────
75
+ US-XXX → Skill("roll-build", "US-XXX")
76
+ FIX-XXX → Skill("roll-fix", "FIX-XXX")
77
+ REFACTOR-XXX → Skill("roll-build", "REFACTOR-XXX")
78
+ ```
79
+
80
+ Before invoking, write current item to state file:
81
+
82
+ ```yaml
83
+ # ~/.shared/roll/loop/state.yaml
84
+ status: running
85
+ current_item: US-AUTO-004
86
+ started_at: "2026-05-10T02:00:00+08:00"
87
+ agent: claude
88
+ run_id: loop-20260510-0200
89
+ ```
90
+
91
+ ### Step 4 — Post-Item Cleanup
92
+
93
+ After each item completes:
94
+
95
+ 1. Update state file: `status: idle`
96
+ 2. Check if a Feature is now fully complete (all its Stories ✅)
97
+ 3. If yes and `brief_on_feature_complete: true` → invoke `Skill("roll-brief")`
98
+
99
+ ### Step 5 — Write Run Summary
100
+
101
+ After all items in this cycle:
102
+
103
+ ```yaml
104
+ # ~/.shared/roll/loop/state.yaml
105
+ status: idle
106
+ last_run: "2026-05-10T02:15:00+08:00"
107
+ last_run_items: [US-AUTH-003, FIX-007]
108
+ last_run_outcome: success
109
+ ```
110
+
111
+ ## Failure Handling
112
+
113
+ ### Network Error (transient)
114
+
115
+ ```
116
+ Attempt 1 fails
117
+ → wait 2s → Attempt 2
118
+ → wait 4s → Attempt 3
119
+ → wait 8s → Attempt 4
120
+ → wait 16s → Attempt 5
121
+ → still failing → escalate to token/agent failure path
122
+ ```
123
+
124
+ ### Token Exhausted / Agent Unavailable
125
+
126
+ ```
127
+ Primary agent fails (non-network error)
128
+ → switch to fallback_agent from config
129
+ → retry current item with fallback
130
+ → if fallback also fails → PAUSE
131
+ ```
132
+
133
+ ### Pause + Alert
134
+
135
+ When both agents fail:
136
+
137
+ 1. Write state:
138
+ ```yaml
139
+ status: paused
140
+ paused_at: "2026-05-10T02:07:00+08:00"
141
+ paused_on: US-AUTH-003
142
+ reason: "both primary (claude) and fallback (deepseek) unavailable"
143
+ ```
144
+
145
+ 2. Write alert:
146
+ ```markdown
147
+ # ALERT — roll-loop paused
148
+
149
+ **Time**: 2026-05-10 02:07
150
+ **Paused on**: US-AUTH-003
151
+ **Reason**: claude: token exhausted; deepseek: network error after 5 retries
152
+
153
+ **Action required** (choose one):
154
+ - Top up credits and run: `roll loop resume`
155
+ - Switch agent: edit `~/.roll/config.yaml` → `loop.primary_agent`
156
+ - Take over manually: `$roll-build US-AUTH-003`
157
+ ```
158
+
159
+ 3. Write alert file to `~/.shared/roll/loop/ALERT.md`
160
+
161
+ ## Resuming After Pause
162
+
163
+ ```bash
164
+ roll loop resume # picks up from state.yaml current_item
165
+ roll loop status # show current state without running
166
+ roll loop reset # clear state and start fresh next scheduled run
167
+ ```
168
+
169
+ ## Scheduler Configuration
170
+
171
+ roll-loop runs **locally** — it needs access to the local codebase, local
172
+ test runner, and local agent CLI. GitHub Actions runs on remote servers and
173
+ cannot fulfill these requirements.
174
+
175
+ ### Local cron (default)
176
+
177
+ Install once with `roll loop on` — it reads the configured agent from
178
+ `.roll.yaml` or `~/.roll/config.yaml` and writes the correct cron entry
179
+ automatically. No agent-specific command needed.
180
+
181
+ ```bash
182
+ roll loop on # install cron for loop + dream + brief
183
+ roll loop off # remove cron entries
184
+ roll loop status # show current state
185
+ ```
186
+
187
+ ### Manual run (for testing)
188
+
189
+ ```bash
190
+ roll loop now # execute one cycle immediately
191
+ ```
192
+
193
+ ## Integration Map
194
+
195
+ ```
196
+ roll-loop
197
+ ├── reads BACKLOG.md
198
+ ├── invokes $roll-build (US-XXX, REFACTOR-XXX)
199
+ ├── invokes $roll-fix (FIX-XXX)
200
+ ├── invokes $roll-brief (on Feature completion)
201
+ ├── reads ~/.roll/config.yaml (agent routing)
202
+ ├── writes ~/.shared/roll/loop/state.yaml
203
+ └── writes ~/.shared/roll/loop/ALERT.md (on failure)
204
+ ```
@@ -170,12 +170,13 @@ If serve mode is available, prefer HTTP transport over direct CLI invocation.
170
170
 
171
171
  | Peer | Non-interactive command | Reliability | Notes |
172
172
  |------|------------------------|-------------|-------|
173
- | `claude` | `claude -p "<prompt>"` | ✅ High | Native, stable |
174
- | `deepseek` | `deepseek "<prompt>"` | ✅ Good | No TTY dependency |
173
+ | `claude` | `claude -p "<prompt>"` | ✅ Verified | Native, stable |
174
+ | `deepseek` | `deepseek "<prompt>"` | ✅ Verified | No TTY dependency |
175
175
  | `deepseek` (serve) | `curl localhost:<port>/v1/...` | ✅ High | Start with `deepseek serve --http`; preferred over direct CLI |
176
- | `kimi` | `kimi --quiet "<prompt>"` | ⚠️ Unverified | Verify non-interactive support before use |
177
- | `codex` | `codex exec --json --output-last-message "<prompt>"` | ⚠️ Unstable | Known CI failures due to Ink/TTY (issues [#1080](https://github.com/openai/codex/issues/1080), [#1340](https://github.com/openai/codex/issues/1340)); use only as fallback |
178
- | `pi` | `pi -p "<prompt>"` | ⚠️ Unverified | Verify non-interactive support before use |
176
+ | `kimi` | `kimi --quiet -p "<prompt>"` | Verified | `--quiet` is alias for `--print --output-format text --final-message-only`; prompt via `-p` |
177
+ | `pi` | `pi -p "<prompt>"` | Verified | Clean non-interactive output |
178
+ | `opencode` | `opencode run "<prompt>"` | Verified | Works non-interactively |
179
+ | `codex` | `codex exec "<prompt>"` | ⚠️ Auth required | Token must be valid; re-login with `codex login` if expired |
179
180
 
180
181
  **CLI vs. API Key**: `claude`, `deepseek`, `kimi`, `codex` CLIs authenticate via existing subscription accounts — no separate API key required. This is the primary advantage of CLI transport over the MCP/HTTP approach.
181
182