@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 +1 -1
- package/bin/roll +284 -3
- package/package.json +1 -1
- package/skills/roll-.dream/SKILL.md +158 -0
- package/skills/roll-brief/SKILL.md +159 -0
- package/skills/roll-build/SKILL.md +41 -1
- package/skills/roll-design/SKILL.md +85 -0
- package/skills/roll-fix/SKILL.md +2 -0
- package/skills/roll-loop/SKILL.md +204 -0
- package/skills/roll-peer/SKILL.md +6 -5
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 |
|
|
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.
|
|
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
|
|
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.
|
|
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
|
package/skills/roll-fix/SKILL.md
CHANGED
|
@@ -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>"` | ✅
|
|
174
|
-
| `deepseek` | `deepseek "<prompt>"` | ✅
|
|
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>"` |
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
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
|
|