@seanyao/roll 2026.518.1 → 2026.518.3
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 +13 -0
- package/bin/roll +4 -4
- package/lib/roll-loop-status.py +5 -2
- package/lib/roll_render.py +8 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v2026.518.3
|
|
4
|
+
|
|
5
|
+
### Fixed
|
|
6
|
+
|
|
7
|
+
- **autonomous loop 跑测试时狂开 Ghostty 窗口** — `_write_loop_runner_script` 生成的 outer runner 在 popup 分支只检查 `ROLL_LOOP_NO_POPUP` / mute 文件 / Darwin;当 loop 的执行 agent 跑 bats 跑到 `_loop_test` 这类需要执行生成脚本的测试时,每个 test case 都会真的弹一个 Ghostty 窗口,一轮 cycle 累出 80+ 个孤儿窗口。popup 守卫追加 `BATS_TEST_NUMBER` 判定,任何 bats 上下文里自动跳过 popup `[loop]`
|
|
8
|
+
|
|
9
|
+
## v2026.518.2
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- **`roll loop status` dashboard 崩溃 / 列对齐** — `_dash_release_ready` 用 `grep -c` 在零匹配时返回 "0" + 非零退出码再被 `|| echo 0` 追加一个 "0",拼成 "0\n0" 让 `[[ -gt ]]` 报语法错;改用 `grep | wc -l | tr -d ' '` 单值返回。Today 表头去掉 "(in progress)" 后缀(曾溢出到 Yesterday 列),数值列宽 6→8 让指标行对齐表头 `[loop]`
|
|
14
|
+
- **v2026.518.1 在刚发版的仓库里 dashboard 不可用** — 上面那个崩溃在 HEAD == latest tag、或 tag 之后只有 docs/chore commit 的仓库中必触发;本版作为追加修复,升到 v2026.518.2 即恢复 `[release]`
|
|
15
|
+
|
|
3
16
|
## v2026.518.1
|
|
4
17
|
|
|
5
18
|
### Improved
|
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.518.
|
|
7
|
+
VERSION="2026.518.3"
|
|
8
8
|
ROLL_HOME="${ROLL_HOME:-${HOME}/.roll}"
|
|
9
9
|
ROLL_CONFIG="${ROLL_HOME}/config.yaml"
|
|
10
10
|
ROLL_GLOBAL="${ROLL_HOME}/conventions/global"
|
|
@@ -2590,7 +2590,7 @@ if command -v tmux >/dev/null 2>&1; then
|
|
|
2590
2590
|
# Auto-attach popup: when not muted, spawn a Terminal window attached to the
|
|
2591
2591
|
# tmux session so the user can watch the loop work in real time. Best-effort
|
|
2592
2592
|
# focus retention: capture the current frontmost app and re-activate after.
|
|
2593
|
-
if [ -z "\${ROLL_LOOP_NO_POPUP:-}" ] && [ ! -f "\$HOME/.shared/roll/loop/mute-${slug}" ] && [ "\$(uname)" = "Darwin" ]; then
|
|
2593
|
+
if [ -z "\${ROLL_LOOP_NO_POPUP:-}" ] && [ -z "\${BATS_TEST_NUMBER:-}" ] && [ ! -f "\$HOME/.shared/roll/loop/mute-${slug}" ] && [ "\$(uname)" = "Darwin" ]; then
|
|
2594
2594
|
# Runtime terminal detection: try preferred first, fallback through installed apps.
|
|
2595
2595
|
# open -na returns non-zero when app not found, so || chain works as fallback.
|
|
2596
2596
|
_launched=0
|
|
@@ -5127,8 +5127,8 @@ _dash_release_ready() {
|
|
|
5127
5127
|
latest_tag=$(git describe --tags --abbrev=0 2>/dev/null) || return 1
|
|
5128
5128
|
local commits_with_code
|
|
5129
5129
|
commits_with_code=$(git log "${latest_tag}..HEAD" --pretty=format:%s 2>/dev/null \
|
|
5130
|
-
| grep -
|
|
5131
|
-
|
|
5130
|
+
| grep -vE '^(docs|chore)(\([^)]*\))?:[[:space:]]' \
|
|
5131
|
+
| wc -l | tr -d ' ')
|
|
5132
5132
|
[[ "${commits_with_code:-0}" -gt 0 ]] || return 1
|
|
5133
5133
|
local latest
|
|
5134
5134
|
latest=$(ls docs/briefs/*.md 2>/dev/null | sort | tail -1 || true)
|
package/lib/roll-loop-status.py
CHANGED
|
@@ -634,12 +634,15 @@ def render(events, cron, state, backlog, *, days=3, lang="both", now=None,
|
|
|
634
634
|
# 'in progress' indicator stays on the day band + muted deltas, not the
|
|
635
635
|
# column header (cramming '(in progress)' into 18 chars collides with
|
|
636
636
|
# the Yesterday column).
|
|
637
|
+
# Today column spans 22 cols = value(8) + gap(2) + delta(12), matching
|
|
638
|
+
# the metric row geometry exactly so Yesterday and −2d line up under
|
|
639
|
+
# their data — fixes the "yesterday/−2d squished" misalignment.
|
|
637
640
|
hdr_en = (" " + c("muted", pad("", 14)) +
|
|
638
|
-
c("fg", pad("Today",
|
|
641
|
+
c("fg", pad("Today", 22), bold=True) +
|
|
639
642
|
c("dim", pad("Yesterday", 10)) +
|
|
640
643
|
c("muted", pad("−2d", 8)))
|
|
641
644
|
hdr_zh = (" " + c("muted", pad("", 14)) +
|
|
642
|
-
c("dim", pad("今日",
|
|
645
|
+
c("dim", pad("今日", 22)) +
|
|
643
646
|
c("muted", pad("昨日", 10)) +
|
|
644
647
|
c("muted", pad("前天", 8)))
|
|
645
648
|
bilingual(hdr_en, hdr_zh)
|
package/lib/roll_render.py
CHANGED
|
@@ -158,14 +158,18 @@ def metric(name: str, t: int, y: int, d2: int, kind: str, *,
|
|
|
158
158
|
partial: bool = False) -> None:
|
|
159
159
|
"""Print one metric row. When `partial=True` the delta is rendered in
|
|
160
160
|
muted gray instead of green/red — today's incomplete, so a 'down −23'
|
|
161
|
-
against yesterday's full day would otherwise read as an alarm.
|
|
161
|
+
against yesterday's full day would otherwise read as an alarm.
|
|
162
|
+
|
|
163
|
+
Column geometry (kept in lockstep with the header in roll-loop-status):
|
|
164
|
+
indent 2 · name 14 · today_value 8 · gap 2 · delta 12 · yest 10 · d2 8
|
|
165
|
+
"""
|
|
162
166
|
delta_text, delta_c = fmt_delta(float(t), float(y), kind=kind)
|
|
163
167
|
if partial and delta_c not in ("muted",):
|
|
164
168
|
delta_c = "muted"
|
|
165
169
|
yest_str = f"{y}" + (f" {yest_suffix}" if yest_suffix else "")
|
|
166
170
|
print(" " +
|
|
167
171
|
c("dim", pad(name, 14)) +
|
|
168
|
-
c("fg", pad(str(t),
|
|
172
|
+
c("fg", pad(str(t), 8, "r"), bold=True) + " " +
|
|
169
173
|
c(delta_c, pad(delta_text, 12), bold=(delta_c != "muted")) +
|
|
170
174
|
c(yest_color, pad(yest_str, 10), bold=bool(yest_suffix)) +
|
|
171
175
|
c("muted", pad(str(d2), 8)))
|
|
@@ -179,7 +183,7 @@ def metric_dur(name: str, t: int, y: int, d2: int, *, partial: bool = False) ->
|
|
|
179
183
|
delta_c = "muted"
|
|
180
184
|
print(" " +
|
|
181
185
|
c("dim", pad(name, 14)) +
|
|
182
|
-
c("fg", pad(fmt_dur(t),
|
|
186
|
+
c("fg", pad(fmt_dur(t), 8, "r"), bold=True) + " " +
|
|
183
187
|
c(delta_c, pad(delta_text, 12), bold=(delta_c != "muted")) +
|
|
184
188
|
c("dim", pad(fmt_dur(y), 10)) +
|
|
185
189
|
c("muted", pad(fmt_dur(d2), 8)))
|
|
@@ -214,7 +218,7 @@ def metric_tokens(name: str, t: int, y: int, d2: int, *, partial: bool = False)
|
|
|
214
218
|
delta_c = "muted"
|
|
215
219
|
print(" " +
|
|
216
220
|
c("dim", pad(name, 14)) +
|
|
217
|
-
c("fg", pad(fmt_tokens(t),
|
|
221
|
+
c("fg", pad(fmt_tokens(t), 8, "r"), bold=True) + " " +
|
|
218
222
|
c(delta_c, pad(delta_text, 12), bold=(delta_c != "muted")) +
|
|
219
223
|
c("dim", pad(fmt_tokens(y), 10)) +
|
|
220
224
|
c("muted", pad(fmt_tokens(d2), 8)))
|