@seanyao/roll 2.604.1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Changelog
2
2
 
3
+ ## v3.0.0
4
+
5
+ ### TypeScript 重写
6
+
7
+ - **引擎从 bash 换成 TypeScript** — roll 的核心重写为 pnpm monorepo(`packages/`:spec/core/infra/cli/web 分层)。这是一次引擎替换,不是功能改版
8
+ - **命令一个不变** — `roll init` / `loop` / `status` / `backlog` / `prices` / `slides` 等全部子命令的入参、输出、副作用、退出码保持原样;升级后照旧用
9
+ - **逐层对拍迁移** — 地基、CLI、领域服务、infra、loop 各层逐条移植,每条命令写 diff-test 断言「TS 输出 == 原 bash 输出」,逐字节对齐冻结的 v2 oracle
10
+ - **1031 项 TS 测试全绿** — 单测覆盖每个公共入口 + 跨层 diff-test;`npm i -g @seanyao/roll` 装的就是这套 TS-first CLI
11
+
12
+ ### 兼容与回滚
13
+
14
+ - **bash 实现留作回落 + oracle** — `bin/roll` 随包一起发布:TS 层尚未接管的命令自动透传 bash,行为零差异;同一份 bash 也是测试套件的标准答案
15
+ - **v2 归档在 `v2` 分支** — 锚点 tag `v2-freeze-2026-06-04`,需要时一键回滚
16
+
17
+ ## v2.604.2
18
+
19
+ ### 精简
20
+
21
+ - **自动简报功能下线(FIX-195)** — 不再后台定时生成每日简报;需要时手动运行 `roll brief` 即可,过往简报都保留
22
+
23
+ ### 稳定性
24
+
25
+ - **一条坏记录不再拖垮整个自动循环(FIX-193)** — 以前运行历史里出现一条损坏记录,会让 `roll loop` 整个起不来;现在自动跳过坏记录、照常运行
26
+
3
27
  ## v2.604.1
4
28
 
5
29
  ### PR Loop 重构
package/README.md CHANGED
@@ -1,3 +1,9 @@
1
+ > **Roll v3 — the engine is now TypeScript (`packages/`, a pnpm monorepo). The commands are unchanged.**
2
+ > **Roll v3——引擎已是 TypeScript(`packages/`,pnpm monorepo);命令保持不变。**
3
+ > The bash implementation stays in-tree as `bin/roll`: the automatic fallback for any command the TS layer doesn't yet own, and the oracle the TS suite diff-tests against. `skills/` is a git submodule → [seanyao/roll-skills](https://github.com/seanyao/roll-skills). v2 is archived on the `v2` branch (anchor tag `v2-freeze-2026-06-04`).
4
+ > bash 实现作为 `bin/roll` 留在仓内:TS 层未接管命令的自动回落,也是 TS 套件对拍的 oracle。`skills/` 是 git submodule。v2 归档在 `v2` 分支(锚点 tag `v2-freeze-2026-06-04`)。
5
+ > Build & test: `pnpm install && pnpm -r test`.
6
+
1
7
  ```
2
8
  ██████╗ ██████╗ ██╗ ██╗
3
9
  ██╔══██╗██╔═══██╗██║ ██║
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="2.604.1"
7
+ VERSION="3.0.0"
8
8
  ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
9
9
  ROLL_CONFIG="${ROLL_HOME}/config.yaml"
10
10
  ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
@@ -1092,8 +1092,6 @@ editor: ${EDITOR:-vim}
1092
1092
  # loop_minute: 5 # omit to auto-derive from project hash
1093
1093
  loop_dream_hour: 3
1094
1094
  # loop_dream_minute: 10 # omit to auto-derive
1095
- loop_brief_hour: 9
1096
- # loop_brief_minute: 15 # omit to auto-derive
1097
1095
  primary_agent: claude
1098
1096
  YAML
1099
1097
  ok "$(msg shared.created_roll_config_yaml)"
@@ -1418,6 +1416,13 @@ cmd_setup() {
1418
1416
  esac
1419
1417
  done
1420
1418
 
1419
+ # P1-3.4 (v3 branch): skills/ is a git submodule — populate it for git clones
1420
+ # whose skills/ is still empty. No-op for npm/curl installs (no .gitmodules).
1421
+ if [[ -d "$ROLL_PKG_DIR/.git" ]] && [[ -f "$ROLL_PKG_DIR/.gitmodules" ]] \
1422
+ && [[ -z "$(ls -A "$ROLL_PKG_DIR/skills" 2>/dev/null)" ]]; then
1423
+ ( cd "$ROLL_PKG_DIR" && git submodule update --init --recursive --quiet ) || true
1424
+ fi
1425
+
1421
1426
  # Capture per-step outcomes for the v2 UI render at the end.
1422
1427
  local steps_buf=()
1423
1428
  _record() { steps_buf+=("$1|$2"); }
@@ -5752,8 +5757,6 @@ loop_schedule.period_minutes|project|nested:loop_schedule|1|1440|60
5752
5757
  loop_schedule.offset_minute|project|nested:loop_schedule|0|59|0
5753
5758
  loop_dream_hour|global|flat|0|23|3
5754
5759
  loop_dream_minute|global|flat|0|59|-
5755
- loop_brief_hour|global|flat|0|23|9
5756
- loop_brief_minute|global|flat|0|59|-
5757
5760
  EOF
5758
5761
  }
5759
5762
 
@@ -5892,10 +5895,10 @@ Usage: roll config <key> print current value + source
5892
5895
  roll config --list list all loop schedule keys
5893
5896
  roll config <key> <value> [--global|--project] set a value
5894
5897
  统一调度配置
5895
- Read / list / set the loop, dream and brief schedule keys without hand-editing
5898
+ Read / list / set the loop and dream schedule keys without hand-editing
5896
5899
  yaml. Default write scope is --project (.roll/local.yaml); --global writes
5897
5900
  ~/.roll/config.yaml.
5898
- 读 / 列 / 写 loop、dream、brief 调度 key,免去手工编辑 yaml。默认写 --project
5901
+ 读 / 列 / 写 loop、dream 调度 key,免去手工编辑 yaml。默认写 --project
5899
5902
  (.roll/local.yaml);--global 写 ~/.roll/config.yaml。
5900
5903
 
5901
5904
  Supported keys (range):
@@ -5905,14 +5908,11 @@ Supported keys (range):
5905
5908
  loop_schedule.offset_minute 0-59 minute offset within the period
5906
5909
  loop_dream_hour 0-23 dream daily fire hour
5907
5910
  loop_dream_minute 0-59 dream daily fire minute
5908
- loop_brief_hour 0-23 brief daily fire hour
5909
- loop_brief_minute 0-59 brief daily fire minute
5910
5911
 
5911
5912
  Compact facades (write multiple keys at once):
5912
5913
  roll config loop-window 9-18 loop_active_start + loop_active_end
5913
5914
  roll config loop-schedule 30/7 period_minutes + offset_minute
5914
5915
  roll config dream-time 03:20 loop_dream_hour + loop_dream_minute
5915
- roll config brief-time 09:15 loop_brief_hour + loop_brief_minute
5916
5916
 
5917
5917
  Examples:
5918
5918
  roll config loop_dream_hour
@@ -6012,11 +6012,12 @@ _config_loop_schedule() {
6012
6012
  return 0
6013
6013
  }
6014
6014
 
6015
- # US-LOOP-035: `roll config dream-time <HH:MM>` / `brief-time <HH:MM>` — compact
6016
- # facade writing loop_<svc>_hour + loop_<svc>_minute in one shot. With no value,
6017
- # prints the current effective time + source. HH ∈ [0,23], MM ∈ [0,59].
6015
+ # US-LOOP-035: `roll config dream-time <HH:MM>` compact facade writing
6016
+ # loop_<svc>_hour + loop_<svc>_minute in one shot. With no value, prints the
6017
+ # current effective time + source. HH ∈ [0,23], MM ∈ [0,59].
6018
6018
  # These keys are global-scoped, so writes land in ~/.roll/config.yaml.
6019
- # _config_daily_time <svc> <value> svc {dream, brief}
6019
+ # FIX-195: brief retired — svc is {dream} (the helper stays generic).
6020
+ # _config_daily_time <svc> <value>
6020
6021
  _config_daily_time() {
6021
6022
  local svc="$1" value="$2"
6022
6023
  local hour_key="loop_${svc}_hour" min_key="loop_${svc}_minute"
@@ -6115,8 +6116,9 @@ cmd_config() {
6115
6116
  [[ $_rc -eq 0 && -n "$value" ]] && _config_reload_schedule
6116
6117
  return $_rc
6117
6118
  ;;
6118
- dream-time|brief-time)
6119
- # dream/brief schedule keys are global-scoped (~/.roll/config.yaml).
6119
+ dream-time)
6120
+ # FIX-195: brief-time retired with the brief loop; dream-time is the only
6121
+ # daily schedule facade. The key is global-scoped (~/.roll/config.yaml).
6120
6122
  local fscope="$scope"; [[ -z "$fscope" ]] && fscope="global"
6121
6123
  ROLL_CFG_SCOPE="$fscope"
6122
6124
  local _rc
@@ -6171,7 +6173,7 @@ cmd_config() {
6171
6173
  fi
6172
6174
  _config_set "$key" "$value" "$file"
6173
6175
  ok "✓ set $key = $value in $file"
6174
- # US-LOOP-036: every recognized config key is a loop/dream/brief schedule key
6176
+ # US-LOOP-036: every recognized config key is a loop/dream schedule key
6175
6177
  # (display-only keys are out of scope for this command), so a successful write
6176
6178
  # always reloads the launchd plists.
6177
6179
  _config_reload_schedule
@@ -9658,9 +9660,10 @@ _install_launchd_plists() {
9658
9660
  local shared="${_SHARED_ROOT}"
9659
9661
 
9660
9662
  mkdir -p "$_LAUNCHD_DIR"
9661
- mkdir -p "${shared}/loop" "${shared}/dream" "${shared}/brief" "${shared}/pr" "${shared}/ci" "${shared}/alert"
9663
+ # FIX-194/FIX-195: brief/ci/alert loops retired — only loop/dream/pr remain.
9664
+ mkdir -p "${shared}/loop" "${shared}/dream" "${shared}/pr"
9662
9665
 
9663
- local active_start active_end dream_hour dream_minute brief_hour brief_minute loop_period loop_offset
9666
+ local active_start active_end dream_hour dream_minute loop_period loop_offset
9664
9667
  local _aw; _aw=$(_loop_read_active_window "$project_path")
9665
9668
  active_start="${_aw%% *}"; active_end="${_aw##* }"
9666
9669
  # US-LOOP-012: use _loop_schedule_spec instead of raw loop_minute
@@ -9669,8 +9672,6 @@ _install_launchd_plists() {
9669
9672
  loop_offset="${loop_spec##* }"
9670
9673
  dream_hour=$(_config_read_int "loop_dream_hour" "3")
9671
9674
  dream_minute=$(_config_read_int "loop_dream_minute" "$(_loop_derive_minute "$project_path" 2)")
9672
- brief_hour=$(_config_read_int "loop_brief_hour" "9")
9673
- brief_minute=$(_config_read_int "loop_brief_minute" "$(_loop_derive_minute "$project_path" 4)")
9674
9675
 
9675
9676
  # FIX-054: terminal preference removed — runner always uses Terminal.app.
9676
9677
 
@@ -9840,7 +9841,7 @@ cmd_loop() {
9840
9841
  *) cat <<'HELP'
9841
9842
  Usage: roll loop <on|off|now|test|status|monitor|runs|log|story|events|attach|mute|unmute|pause|resume|reset|gc|branches>
9842
9843
 
9843
- on Install launchd scheduler (loop + dream + brief + pr + ci + alert)
9844
+ on Install launchd scheduler (loop + dream + pr)
9844
9845
  off Remove launchd scheduler
9845
9846
  now Run one cycle immediately
9846
9847
  test Quick smoke test (tmux/popup/stream chain)
@@ -9885,7 +9886,7 @@ _loop_on() {
9885
9886
  local project_path; project_path=$(pwd -P)
9886
9887
  local agent; agent=$(_project_agent)
9887
9888
 
9888
- local active_start active_end loop_minute dream_hour dream_minute brief_hour brief_minute
9889
+ local active_start active_end loop_minute dream_hour dream_minute
9889
9890
  local _aw; _aw=$(_loop_read_active_window "$project_path")
9890
9891
  active_start="${_aw%% *}"; active_end="${_aw##* }"
9891
9892
  # US-LOOP-011: read schedule spec from project or global config
@@ -9900,8 +9901,6 @@ _loop_on() {
9900
9901
  loop_sched_zh=$(_loop_schedule_desc "$loop_period" "$loop_offset" zh)
9901
9902
  dream_hour=$(_config_read_int "loop_dream_hour" "3")
9902
9903
  dream_minute=$(_config_read_int "loop_dream_minute" "$(_loop_derive_minute "$project_path" 2)")
9903
- brief_hour=$(_config_read_int "loop_brief_hour" "9")
9904
- brief_minute=$(_config_read_int "loop_brief_minute" "$(_loop_derive_minute "$project_path" 4)")
9905
9904
 
9906
9905
  if [[ "$(uname)" == "Darwin" ]]; then
9907
9906
  _install_launchd_plists "$project_path" >/dev/null
@@ -9936,7 +9935,6 @@ _loop_on() {
9936
9935
  msg loop.roll_loop_s_active_02d_00 \
9937
9936
  "$loop_sched_en" "$active_start" "$active_end" "$loop_sched_zh" "$active_start" "$active_end"
9938
9937
  msg loop.roll_dream_daily_at_02d_02d "$dream_hour" "$dream_minute" "$dream_hour" "$dream_minute"
9939
- msg loop.roll_brief_daily_at_02d_02d "$brief_hour" "$brief_minute" "$brief_hour" "$brief_minute"
9940
9938
  echo " • Agent: ${agent} (change: roll agent use <name>)"
9941
9939
  return 0
9942
9940
  fi
@@ -9947,29 +9945,26 @@ _loop_on() {
9947
9945
  warn "$(msg loop.loop_already_enabled_for_this_project_2)"; return 0
9948
9946
  fi
9949
9947
 
9950
- mkdir -p "${_SHARED_ROOT}/loop" "${_SHARED_ROOT}/dream" "${_SHARED_ROOT}/brief"
9948
+ mkdir -p "${_SHARED_ROOT}/loop" "${_SHARED_ROOT}/dream"
9951
9949
 
9952
9950
  # FIX-052: per-project cron logs so concurrent projects don't interleave.
9953
9951
  local slug; slug=$(_project_slug "$project_path")
9954
- local loop_cmd dream_cmd brief_cmd
9952
+ local loop_cmd dream_cmd
9955
9953
  loop_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-loop/SKILL.md") >> ${_SHARED_ROOT}/loop/cron-${slug}.log 2>&1"
9956
- # IDEA-051: dream/brief cron logs are project-local, mirroring loop (FIX-139).
9957
- mkdir -p "${project_path}/.roll/dream" "${project_path}/.roll/brief"
9954
+ # IDEA-051: dream cron log is project-local, mirroring loop (FIX-139).
9955
+ mkdir -p "${project_path}/.roll/dream"
9958
9956
  dream_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-.dream/SKILL.md") >> ${project_path}/.roll/dream/cron.log 2>&1"
9959
- brief_cmd="cd \"${project_path}\" && $(_agent_skill_cmd "${sd}/roll-brief/SKILL.md") >> ${project_path}/.roll/brief/cron.log 2>&1"
9960
9957
 
9961
9958
  (
9962
9959
  crontab -l 2>/dev/null
9963
9960
  printf "%d * * * * %s %s:%s\n" "$loop_minute" "$loop_cmd" "$_LOOP_TAG" "$project_path"
9964
9961
  printf "%d %d * * * %s %s:%s\n" "$dream_minute" "$dream_hour" "$dream_cmd" "$_LOOP_TAG" "$project_path"
9965
- printf "%d %d * * * %s %s:%s\n" "$brief_minute" "$brief_hour" "$brief_cmd" "$_LOOP_TAG" "$project_path"
9966
9962
  ) | crontab -
9967
9963
 
9968
9964
  ok "$(msg loop.loop_enabled_2)"
9969
9965
  msg loop.roll_loop_s_active_02d_00_2 \
9970
9966
  "$loop_sched_en" "$active_start" "$active_end" "$loop_sched_zh" "$active_start" "$active_end"
9971
9967
  msg loop.roll_dream_daily_at_02d_02d_2 "$dream_hour" "$dream_minute" "$dream_hour" "$dream_minute"
9972
- msg loop.roll_brief_daily_at_02d_02d_2 "$brief_hour" "$brief_minute" "$brief_hour" "$brief_minute"
9973
9968
  echo " • Agent: ${agent} (change: roll agent use <name>)"
9974
9969
  }
9975
9970
 
@@ -13789,7 +13784,7 @@ _loop_monitor() {
13789
13784
  # Services status (three services on macOS, single on Linux)
13790
13785
  echo -e "$(msg loop.services ${BOLD} ${NC} ${CYAN} ${agent})"
13791
13786
  if [[ "$(uname)" == "Darwin" ]]; then
13792
- local active_start active_end dream_hour dream_minute brief_hour brief_minute
13787
+ local active_start active_end dream_hour dream_minute
13793
13788
  local _aw; _aw=$(_loop_read_active_window "$project_path")
13794
13789
  active_start="${_aw%% *}"; active_end="${_aw##* }"
13795
13790
  # US-LOOP-013: use schedule spec for display
@@ -13799,17 +13794,16 @@ _loop_monitor() {
13799
13794
  loop_offset="${loop_spec##* }"
13800
13795
  dream_hour=$(_config_read_int "loop_dream_hour" "3")
13801
13796
  dream_minute=$(_config_read_int "loop_dream_minute" "$(_loop_derive_minute "$project_path" 2)")
13802
- brief_hour=$(_config_read_int "loop_brief_hour" "9")
13803
- brief_minute=$(_config_read_int "loop_brief_minute" "$(_loop_derive_minute "$project_path" 4)")
13804
13797
 
13805
- local loop_sched dream_sched brief_sched
13798
+ local loop_sched dream_sched pr_sched
13806
13799
  loop_sched=$(_loop_schedule_desc "$loop_period" "$loop_offset" en)
13807
13800
  loop_sched="${loop_sched} active ${active_start}:00–${active_end}:00"
13808
13801
  dream_sched=$(printf "%02d:%02d" "$dream_hour" "$dream_minute")
13809
- brief_sched=$(printf "%02d:%02d" "$brief_hour" "$brief_minute")
13802
+ # FIX-195: pr is a 5-min PR Loop (StartInterval=300); brief was retired.
13803
+ pr_sched="every 5m"
13810
13804
 
13811
13805
  local svcs=("loop" "dream" "pr")
13812
- local scheds=("$loop_sched" "$dream_sched" "$brief_sched")
13806
+ local scheds=("$loop_sched" "$dream_sched" "$pr_sched")
13813
13807
  for i in "${!svcs[@]}"; do
13814
13808
  local svc="${svcs[$i]}" schedule="${scheds[$i]}"
13815
13809
  local state; state=$(_launchd_svc_state "$svc" "$project_path")
@@ -14899,7 +14893,7 @@ _legacy_home() {
14899
14893
  else
14900
14894
  crontab -l 2>/dev/null | grep -q "${_LOOP_TAG}:${project_path}" && loop_state="enabled"
14901
14895
  fi
14902
- local active_start active_end dream_hour dream_minute brief_hour brief_minute
14896
+ local active_start active_end dream_hour dream_minute
14903
14897
  local _aw; _aw=$(_loop_read_active_window "$project_path")
14904
14898
  active_start="${_aw%% *}"; active_end="${_aw##* }"
14905
14899
  # US-LOOP-013: use schedule spec for display
@@ -14909,8 +14903,6 @@ _legacy_home() {
14909
14903
  loop_offset="${loop_spec##* }"
14910
14904
  dream_hour=$(_config_read_int "loop_dream_hour" "3")
14911
14905
  dream_minute=$(_config_read_int "loop_dream_minute" "$(_loop_derive_minute "$project_path" 2)")
14912
- brief_hour=$(_config_read_int "loop_brief_hour" "9")
14913
- brief_minute=$(_config_read_int "loop_brief_minute" "$(_loop_derive_minute "$project_path" 4)")
14914
14906
 
14915
14907
  local loop_badge loop_sched
14916
14908
  loop_sched=$(_loop_schedule_desc "$loop_period" "$loop_offset" en)
@@ -15040,8 +15032,9 @@ _legacy_home() {
15040
15032
  # ── ⑥ Schedules & Last Brief ──────────────────────────────────────────────
15041
15033
  printf " ${BOLD}⏰ Schedules & Last Brief${NC}\n"
15042
15034
  local loop_sched_short; loop_sched_short=$(_loop_schedule_desc "$loop_period" "$loop_offset" en)
15043
- printf " %s · dream %02d:%02d · brief %02d:%02d\n" \
15044
- "$loop_sched_short" "$dream_hour" "$dream_minute" "$brief_hour" "$brief_minute"
15035
+ # FIX-195: brief loop retired schedule line shows loop + dream only.
15036
+ printf " %s · dream %02d:%02d\n" \
15037
+ "$loop_sched_short" "$dream_hour" "$dream_minute"
15045
15038
  local latest_brief; latest_brief=$(ls .roll/briefs/*.md 2>/dev/null | sort | tail -1 || true)
15046
15039
  if [[ -n "$latest_brief" ]]; then
15047
15040
  local mod_time now age summary