@seanyao/roll 2026.510.5 → 2026.510.7

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/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.5"
7
+ VERSION="2026.510.7"
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"
@@ -1653,16 +1662,25 @@ _project_agent() {
1653
1662
  fi
1654
1663
  }
1655
1664
 
1665
+ _skill_content() {
1666
+ # Strip YAML frontmatter (---...---) — it's roll-internal metadata, not agent instructions
1667
+ awk 'NR==1 && /^---$/{skip=1;next} skip && /^---$/{skip=0;next} !skip{print}' "$1"
1668
+ }
1669
+
1656
1670
  _agent_run_skill() {
1657
1671
  local skill="$1"
1658
1672
  local agent; agent=$(_project_agent)
1659
1673
  local skill_file="${ROLL_HOME}/skills/${skill}/SKILL.md"
1660
1674
  [[ -f "$skill_file" ]] || { err "Skill not found: ${skill}"; return 1; }
1675
+ local content; content=$(_skill_content "$skill_file")
1661
1676
  case "$agent" in
1662
- claude) claude -p "$(cat "$skill_file")" ;;
1663
- kimi) kimi --quiet "$(cat "$skill_file")" ;;
1664
- deepseek) deepseek "$(cat "$skill_file")" ;;
1665
- *) warn "Unknown agent '${agent}', falling back to claude"; claude -p "$(cat "$skill_file")" ;;
1677
+ claude) claude -p "$content" ;;
1678
+ kimi) kimi --quiet -p "$content" ;;
1679
+ deepseek) deepseek "$content" ;;
1680
+ pi) pi -p "$content" ;;
1681
+ codex) codex exec "$content" ;;
1682
+ opencode) opencode run "$content" ;;
1683
+ *) err "Unknown agent '${agent}'. Run: roll agent use <claude|kimi|deepseek|pi|codex|opencode>"; return 1 ;;
1666
1684
  esac
1667
1685
  }
1668
1686
 
@@ -1671,10 +1689,10 @@ cmd_agent() {
1671
1689
  case "$subcmd" in
1672
1690
  use)
1673
1691
  local name="${1:-}"
1674
- [[ -z "$name" ]] && { err "Usage: roll agent use <claude|kimi|deepseek>"; exit 1; }
1692
+ [[ -z "$name" ]] && { err "Usage: roll agent use <claude|kimi|deepseek|pi|codex|opencode>"; exit 1; }
1675
1693
  command -v "$name" &>/dev/null || warn "${name} not found in PATH — setting anyway 未找到,仍写入配置"
1676
1694
  if [[ -f ".roll.yaml" ]] && grep -q "^agent:" .roll.yaml; then
1677
- sed -i "s/^agent:.*/agent: ${name}/" .roll.yaml
1695
+ local tmp; tmp=$(mktemp) && sed "s/^agent:.*/agent: ${name}/" .roll.yaml > "$tmp" && mv "$tmp" .roll.yaml
1678
1696
  else
1679
1697
  echo "agent: ${name}" >> .roll.yaml
1680
1698
  fi
@@ -1714,6 +1732,22 @@ _LOOP_TAG="# roll-loop"
1714
1732
  _LOOP_STATE="${HOME}/.shared/roll/loop/state.yaml"
1715
1733
  _LOOP_ALERT="${HOME}/.shared/roll/loop/ALERT.md"
1716
1734
 
1735
+ _agent_skill_cmd() {
1736
+ local skill_path="$1"
1737
+ local agent; agent=$(_project_agent)
1738
+ # Strip YAML frontmatter inline for cron commands
1739
+ local strip="awk 'NR==1 && /^---$/{skip=1;next} skip && /^---$/{skip=0;next} !skip{print}' '${skill_path}'"
1740
+ case "$agent" in
1741
+ claude) echo "claude -p \"\$(${strip})\"" ;;
1742
+ kimi) echo "kimi --quiet -p \"\$(${strip})\"" ;;
1743
+ deepseek) echo "deepseek \"\$(${strip})\"" ;;
1744
+ pi) echo "pi -p \"\$(${strip})\"" ;;
1745
+ codex) echo "codex exec \"\$(${strip})\"" ;;
1746
+ opencode) echo "opencode run \"\$(${strip})\"" ;;
1747
+ *) err "Unknown agent '${agent}'. Run: roll agent use <claude|kimi|deepseek|pi|codex|opencode>"; return 1 ;;
1748
+ esac
1749
+ }
1750
+
1717
1751
  cmd_loop() {
1718
1752
  local subcmd="${1:-status}"; shift || true
1719
1753
  case "$subcmd" in
@@ -1721,7 +1755,9 @@ cmd_loop() {
1721
1755
  off) _loop_off ;;
1722
1756
  now) _loop_now ;;
1723
1757
  status) _loop_status ;;
1724
- *) err "Usage: roll loop <on|off|now|status>"; exit 1 ;;
1758
+ resume) _loop_resume ;;
1759
+ reset) _loop_reset ;;
1760
+ *) err "Usage: roll loop <on|off|now|status|resume|reset>"; exit 1 ;;
1725
1761
  esac
1726
1762
  }
1727
1763
 
@@ -1737,9 +1773,9 @@ _loop_on() {
1737
1773
  mkdir -p ~/.shared/roll/{loop,dream,brief}
1738
1774
 
1739
1775
  local loop_cmd dream_cmd brief_cmd
1740
- loop_cmd="cd \"${project_path}\" && ${agent} -p \"\$(cat ${sd}/roll-loop/SKILL.md)\" >> ~/.shared/roll/loop/cron.log 2>&1"
1741
- dream_cmd="cd \"${project_path}\" && ${agent} -p \"\$(cat ${sd}/roll-.dream/SKILL.md)\" >> ~/.shared/roll/dream/cron.log 2>&1"
1742
- brief_cmd="cd \"${project_path}\" && ${agent} -p \"\$(cat ${sd}/roll-brief/SKILL.md)\" >> ~/.shared/roll/brief/cron.log 2>&1"
1776
+ loop_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-loop/SKILL.md") >> ~/.shared/roll/loop/cron.log 2>&1"
1777
+ dream_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-.dream/SKILL.md") >> ~/.shared/roll/dream/cron.log 2>&1"
1778
+ brief_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-brief/SKILL.md") >> ~/.shared/roll/brief/cron.log 2>&1"
1743
1779
 
1744
1780
  (
1745
1781
  crontab -l 2>/dev/null
@@ -1786,6 +1822,26 @@ _loop_status() {
1786
1822
  echo ""
1787
1823
  }
1788
1824
 
1825
+ _loop_resume() {
1826
+ if [[ ! -f "$_LOOP_STATE" ]]; then
1827
+ warn "No loop state found — nothing to resume 未找到 loop 状态,无需恢复"; return 0
1828
+ fi
1829
+ if grep -q "status: running" "$_LOOP_STATE" 2>/dev/null; then
1830
+ warn "Loop already running loop 正在运行中"; return 0
1831
+ fi
1832
+ info "Resuming loop from last state... 正在从上次状态恢复..."
1833
+ _agent_run_skill "roll-loop"
1834
+ }
1835
+
1836
+ _loop_reset() {
1837
+ if [[ -f "$_LOOP_STATE" ]]; then
1838
+ rm -f "$_LOOP_STATE"
1839
+ ok "Loop state cleared — will start fresh on next run loop 状态已清除,下次运行将重新开始"
1840
+ else
1841
+ info "No loop state to clear 无 loop 状态可清除"
1842
+ fi
1843
+ }
1844
+
1789
1845
  # ═══════════════════════════════════════════════════════════════════════════════
1790
1846
  # BRIEF — owner-facing project digest
1791
1847
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -1812,6 +1868,23 @@ cmd_brief() {
1812
1868
  [[ -f "$latest" ]] && cat "$latest"
1813
1869
  }
1814
1870
 
1871
+ cmd_release() {
1872
+ local script="scripts/release.sh"
1873
+ if [[ ! -f "$script" ]]; then
1874
+ err "scripts/release.sh not found — run this command from the roll project root."
1875
+ exit 1
1876
+ fi
1877
+ # Sync CHANGELOG.md before releasing, using the configured agent
1878
+ local last_tag; last_tag=$(git tag --sort=-version:refname | grep "^v" | head -1)
1879
+ if [[ -n "$last_tag" ]] && ! git diff "${last_tag}..HEAD" --name-only | grep -q "CHANGELOG.md"; then
1880
+ info "CHANGELOG.md not updated since ${last_tag}, syncing via $(_project_agent)..."
1881
+ _agent_run_skill "roll-.changelog"
1882
+ fi
1883
+ bash "$script" "$@"
1884
+ }
1885
+
1886
+ # ─────────────────────────────────────────────────────────────────────────────
1887
+
1815
1888
  _dashboard() {
1816
1889
  local project_path; project_path=$(pwd -P)
1817
1890
  local project_name; project_name=$(basename "$project_path")
@@ -1841,7 +1914,8 @@ _dashboard() {
1841
1914
  fi
1842
1915
 
1843
1916
  echo ""
1844
- echo " roll loop on|off|now|status · roll brief · roll agent use <name>"
1917
+ echo " loop <on|off|now|status|resume|reset> · brief · agent use <name> · release"
1918
+ echo " setup · update · init · status · peer · help"
1845
1919
  echo ""
1846
1920
  }
1847
1921
 
@@ -1867,9 +1941,10 @@ usage() {
1867
1941
  echo " init [Project] Create AGENTS.md + BACKLOG.md + docs/ 初始化项目工作流文件"
1868
1942
  echo " status [Diagnostic] Show current state 显示当前状态"
1869
1943
  echo " peer [Peer Review] Cross-agent negotiation 跨 Agent 协商对审"
1870
- echo " loop <on|off|now|status> [Autonomous] Manage scheduled BACKLOG executor 管理自主执行循环"
1944
+ echo " loop <on|off|now|status|resume|reset> [Autonomous] Manage scheduled BACKLOG executor 管理自主执行循环"
1871
1945
  echo " brief [Digest] Show latest owner brief (regenerate if stale) 展示最新简报"
1872
1946
  echo " agent [use <name>|list] [Config] Per-project agent selection 切换项目 agent"
1947
+ echo " release [Publish] Sync changelog + version bump + npm publish 同步日志并发版"
1873
1948
  echo ""
1874
1949
  echo "Examples / 示例:"
1875
1950
  echo " roll setup # New machine: first-time install 新机器首次安装"
@@ -1894,6 +1969,7 @@ main() {
1894
1969
  loop) cmd_loop "$@" ;;
1895
1970
  brief) cmd_brief "$@" ;;
1896
1971
  agent) cmd_agent "$@" ;;
1972
+ release) cmd_release "$@" ;;
1897
1973
  version|--version|-v) echo "roll v${VERSION}" ;;
1898
1974
  help|--help|-h) usage ;;
1899
1975
  "") [[ -f "BACKLOG.md" ]] && _dashboard || { usage; _show_changelog; } ;;
@@ -1950,6 +2026,12 @@ _notify_update() {
1950
2026
  [[ -f "$cache" ]] || return
1951
2027
  local latest; latest=$(awk '{print $2}' "$cache" 2>/dev/null || true)
1952
2028
  [[ -z "$latest" || "$latest" == "$VERSION" ]] && return
2029
+ local newer; newer=$(printf '%s\n%s\n' "$VERSION" "$latest" | sort -V | tail -1)
2030
+ if [[ "$newer" == "$VERSION" ]]; then
2031
+ # Running version is newer than cached — stale cache, clear it
2032
+ rm -f "$cache"
2033
+ return
2034
+ fi
1953
2035
  echo ""
1954
2036
  warn "v${latest} available — run 'roll update' to upgrade 有新版本 v${latest} — 运行 'roll update' 升级"
1955
2037
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seanyao/roll",
3
- "version": "2026.510.5",
3
+ "version": "2026.510.7",
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"
@@ -144,18 +144,8 @@ roll-.dream runs **locally** — it reads the local codebase directly.
144
144
 
145
145
  ### Local cron (default)
146
146
 
147
- ```bash
148
- # Run at 01:00 every night (adjust to your timezone)
149
- 0 1 * * * cd /path/to/project && claude -p "$(cat ~/.roll/skills/roll-.dream/SKILL.md)" >> ~/.shared/roll/dream/cron.log 2>&1
150
- ```
151
-
152
- Installed automatically via `roll loop install` alongside roll-loop.
153
- The agent command is read from `~/.roll/config.yaml → loop.primary_agent`.
154
-
155
- ### Agent with native scheduling support
156
-
157
- If your agent (Claude Code, opencode, etc.) supports scheduled prompts
158
- natively, prefer that over cron for cleaner lifecycle management.
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.
159
149
 
160
150
  ## Failure Handling
161
151
 
@@ -150,13 +150,8 @@ BACKLOG state and git history directly.
150
150
 
151
151
  ### Local cron (daily morning)
152
152
 
153
- ```bash
154
- # 08:00 every morning (adjust to your timezone)
155
- 0 8 * * * cd /path/to/project && claude -p "$(cat ~/.roll/skills/roll-brief/SKILL.md)"
156
- ```
157
-
158
- Installed automatically via `roll loop install` alongside roll-loop and
159
- roll-.dream. The agent command respects `~/.roll/config.yaml → loop.primary_agent`.
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.
160
155
 
161
156
  ### Triggered by roll-loop
162
157
 
@@ -517,6 +517,9 @@ git push
517
517
 
518
518
  ### Phase 12: Report & Celebrate
519
519
 
520
+ **Before reporting, run `$roll-.changelog`** to sync completed Story to CHANGELOG.md.
521
+ This is mandatory — release notes depend on it.
522
+
520
523
  ```
521
524
  ✅ Pushed to GitHub: origin/main
522
525
  🚀 Deployed: <url>
@@ -601,7 +604,7 @@ Before creating any file or directory:
601
604
  - [ ] **Verification Gate passed** (fresh evidence for tests, build, deploy, no regression)
602
605
  - [ ] **BACKLOG.md index status updated** (📋 → ✅, REQUIRED)
603
606
  - [ ] **`docs/features/<feature>.md` US section updated** (Completed date + [x] ACs, REQUIRED)
604
- - [ ] **CHANGELOG.md updated** via `$roll-.changelog`
607
+ - [ ] **CHANGELOG.md updated** via `$roll-.changelog` (REQUIRED)
605
608
  - [ ] Summary reported to user
606
609
 
607
610
  ---
@@ -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
  ```
@@ -174,47 +174,20 @@ cannot fulfill these requirements.
174
174
 
175
175
  ### Local cron (default)
176
176
 
177
- Install once with `roll loop install`:
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.
178
180
 
179
181
  ```bash
180
- # What roll loop install writes to crontab:
181
- 0 * * * * cd /path/to/project && claude -p "$(cat ~/.roll/skills/roll-loop/SKILL.md)" >> ~/.shared/roll/loop/cron.log 2>&1
182
+ roll loop on # install cron for loop + dream + brief
183
+ roll loop off # remove cron entries
184
+ roll loop status # show current state
182
185
  ```
183
186
 
184
- The agent invoked (`claude`, `kimi`, `deepseek`, etc.) is read from
185
- `~/.roll/config.yaml → loop.primary_agent`. The command is agent-agnostic:
186
-
187
- ```bash
188
- # claude
189
- claude -p "$(cat ~/.roll/skills/roll-loop/SKILL.md)"
190
-
191
- # kimi
192
- kimi --quiet "$(cat ~/.roll/skills/roll-loop/SKILL.md)"
193
-
194
- # deepseek
195
- deepseek "$(cat ~/.roll/skills/roll-loop/SKILL.md)"
196
- ```
197
-
198
- ### Agent with native scheduling support
199
-
200
- Some agents support scheduled prompts natively (e.g., Claude Code hooks,
201
- opencode scheduled tasks). If your agent supports it, prefer that over cron
202
- — the agent handles its own lifecycle and the setup is cleaner.
203
-
204
- Consult your agent's documentation for scheduling configuration.
205
-
206
187
  ### Manual run (for testing)
207
188
 
208
189
  ```bash
209
- roll loop run # execute one cycle immediately
210
- roll loop status # show current state without running
211
- ```
212
-
213
- ### Local cron
214
-
215
- ```bash
216
- # Every hour
217
- 0 * * * * cd /path/to/project && claude -p "$(cat ~/.roll/skills/roll-loop/SKILL.md)" >> ~/.shared/roll/loop/cron.log 2>&1
190
+ roll loop now # execute one cycle immediately
218
191
  ```
219
192
 
220
193
  ## Integration Map
@@ -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