@seanyao/roll 2026.529.5 → 2026.601.2

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.
Files changed (59) hide show
  1. package/CHANGELOG.md +57 -25
  2. package/README.md +10 -7
  3. package/bin/roll +3952 -317
  4. package/conventions/config.yaml +7 -0
  5. package/lib/__pycache__/github_sync.cpython-314.pyc +0 -0
  6. package/lib/__pycache__/loop_result_eval.cpython-314.pyc +0 -0
  7. package/lib/__pycache__/model_prices.cpython-314.pyc +0 -0
  8. package/lib/__pycache__/roll-home.cpython-314.pyc +0 -0
  9. package/lib/__pycache__/roll-loop-status.cpython-314.pyc +0 -0
  10. package/lib/__pycache__/roll_git.cpython-314.pyc +0 -0
  11. package/lib/__pycache__/slides-render.cpython-314.pyc +0 -0
  12. package/lib/agent_usage/__init__.py +4 -0
  13. package/lib/agent_usage/__pycache__/__init__.cpython-314.pyc +0 -0
  14. package/lib/agent_usage/__pycache__/gemini.cpython-314.pyc +0 -0
  15. package/lib/agent_usage/__pycache__/kimi.cpython-314.pyc +0 -0
  16. package/lib/agent_usage/__pycache__/openai.cpython-314.pyc +0 -0
  17. package/lib/agent_usage/__pycache__/qwen.cpython-314.pyc +0 -0
  18. package/lib/agent_usage/gemini.py +127 -0
  19. package/lib/agent_usage/kimi.py +127 -0
  20. package/lib/agent_usage/openai.py +126 -0
  21. package/lib/agent_usage/qwen.py +128 -0
  22. package/lib/context_feed_budget.sh +194 -0
  23. package/lib/github_sync.py +876 -0
  24. package/lib/i18n/agent.sh +54 -0
  25. package/lib/i18n/init.sh +22 -0
  26. package/lib/i18n/peer.sh +7 -0
  27. package/lib/i18n/peer_help.sh +4 -0
  28. package/lib/i18n/skills_catalog.sh +30 -0
  29. package/lib/loop-exit-summary.py +393 -0
  30. package/lib/loop-fmt.py +93 -75
  31. package/lib/loop_pick_agent.py +241 -170
  32. package/lib/loop_result_eval.py +469 -0
  33. package/lib/model_prices.py +0 -10
  34. package/lib/roll-home.py +1 -28
  35. package/lib/roll-loop-status.py +330 -40
  36. package/lib/roll-onboard-render.py +378 -0
  37. package/lib/roll-peer.py +1 -1
  38. package/lib/roll-plan-validate.py +165 -0
  39. package/lib/roll_git.py +41 -0
  40. package/lib/slides/components/README.md +8 -2
  41. package/lib/slides/templates/introduction-v3.html +1 -6
  42. package/lib/slides-render.py +305 -15
  43. package/lib/slides-validate.py +195 -7
  44. package/package.json +1 -1
  45. package/skills/roll-.changelog/SKILL.md +67 -56
  46. package/skills/roll-brief/SKILL.md +1 -1
  47. package/skills/roll-build/SKILL.md +14 -12
  48. package/skills/roll-deck/SKILL.md +152 -0
  49. package/skills/roll-design/SKILL.md +13 -6
  50. package/skills/roll-doc/SKILL.md +269 -6
  51. package/skills/roll-fix/SKILL.md +15 -9
  52. package/skills/roll-loop/SKILL.md +9 -7
  53. package/skills/roll-notes/SKILL.md +1 -1
  54. package/skills/roll-onboard/SKILL.md +85 -0
  55. package/skills/roll-peer/SKILL.md +6 -5
  56. package/lib/agent_routes_lint.py +0 -203
  57. package/skills/roll-research/SKILL.md +0 -316
  58. package/skills/roll-research/references/schema.json +0 -166
  59. package/skills/roll-research/scripts/md_to_pdf.py +0 -289
@@ -0,0 +1,194 @@
1
+ #!/usr/bin/env bash
2
+ # US-CTX-001: Context-feed budget (投喂预算).
3
+ #
4
+ # roll is an outer orchestrator: when it builds the inner agent's prompt it
5
+ # injects material — chiefly the story's feature .md file. Large stories used to
6
+ # be fed whole ("整文件硬塞"), which can blow the inner agent's context window.
7
+ #
8
+ # This module is the ContextFeed aggregate. It owns FeedBudget (a max byte size,
9
+ # configurable) and decides an InjectionPlan (full / summarized / chunked) for a
10
+ # given piece of material — never silently truncating, always annotating when it
11
+ # summarizes or chunks and always pointing at the full-text path.
12
+ #
13
+ # Boundary: token-level compression stays in the inner agent harness. This module
14
+ # only answers "what to feed, and how much".
15
+ #
16
+ # Pure bash 3.2: no ${var^^}, no mapfile, no declare -A. All functions read from
17
+ # args / env and write to stdout — no global state, no file writes.
18
+
19
+ # Default feed budget in bytes. Tuned to comfortably hold a normal story feature
20
+ # file while staying well under an inner agent's context window. Configurable via
21
+ # ROLL_FEED_BUDGET_BYTES so operators can dial it to the inner agent's capacity.
22
+ ROLL_FEED_BUDGET_DEFAULT_BYTES=16384
23
+
24
+ # _feed_budget_bytes
25
+ # Resolve the active feed budget (bytes). Honors ROLL_FEED_BUDGET_BYTES when set
26
+ # to a positive integer; otherwise falls back to the compiled-in default.
27
+ _feed_budget_bytes() {
28
+ local v="${ROLL_FEED_BUDGET_BYTES:-}"
29
+ case "$v" in
30
+ ''|*[!0-9]*) echo "$ROLL_FEED_BUDGET_DEFAULT_BYTES" ;;
31
+ *) if [ "$v" -gt 0 ]; then echo "$v"; else echo "$ROLL_FEED_BUDGET_DEFAULT_BYTES"; fi ;;
32
+ esac
33
+ }
34
+
35
+ # _feed_size_bytes <file>
36
+ # Byte size of a file. Echoes 0 for a missing/unreadable file.
37
+ _feed_size_bytes() {
38
+ local f="$1"
39
+ [ -f "$f" ] || { echo 0; return 0; }
40
+ # wc -c is portable across macOS bash 3.2 and Linux; strip leading spaces.
41
+ local n
42
+ n=$(wc -c < "$f" 2>/dev/null | tr -d ' ')
43
+ case "$n" in
44
+ ''|*[!0-9]*) echo 0 ;;
45
+ *) echo "$n" ;;
46
+ esac
47
+ }
48
+
49
+ # _feed_plan <file>
50
+ # Decide the InjectionPlan for a material file: prints one of
51
+ # full | summarized | chunked
52
+ # - Within budget → full
53
+ # - Over budget → summarized
54
+ # - Over 4x budget (huge) → chunked
55
+ # A missing file is treated as full (nothing to budget).
56
+ _feed_plan() {
57
+ local f="$1"
58
+ local size budget
59
+ size=$(_feed_size_bytes "$f")
60
+ budget=$(_feed_budget_bytes)
61
+ if [ "$size" -le "$budget" ]; then
62
+ echo full
63
+ elif [ "$size" -le "$((budget * 4))" ]; then
64
+ echo summarized
65
+ else
66
+ echo chunked
67
+ fi
68
+ }
69
+
70
+ # _feed_summary_notice <file> <plan>
71
+ # The explicit, non-silent annotation prepended to summarized/chunked material.
72
+ # Bilingual per project convention: EN and ZH on separate lines. Points at the
73
+ # full-text path so nothing is lost. Empty for the `full` plan.
74
+ _feed_summary_notice() {
75
+ local f="$1" plan="$2"
76
+ case "$plan" in
77
+ summarized)
78
+ printf '%s\n' "[context-feed] This story feature exceeds the feed budget — injected as a SUMMARY. Full text: ${f}"
79
+ printf '%s\n' "[投喂预算] 本故事 feature 超投喂预算,已摘要注入,全文见 ${f}"
80
+ ;;
81
+ chunked)
82
+ printf '%s\n' "[context-feed] This story feature far exceeds the feed budget — injected in CHUNKS. Full text: ${f}"
83
+ printf '%s\n' "[投喂预算] 本故事 feature 远超投喂预算,已分段注入,全文见 ${f}"
84
+ ;;
85
+ *) : ;;
86
+ esac
87
+ }
88
+
89
+ # _feed_budget_head <file>
90
+ # The leading <= budget bytes of <file>, trimmed back to the last COMPLETE line
91
+ # so we never cut mid-line silently. Keeps all whole lines that fit; if not even
92
+ # the first line fits, falls back to the raw byte head (a single very long line).
93
+ # Uses `dd` (not `head -c`) for portability across BSD/macOS and GNU coreutils.
94
+ _feed_budget_head() {
95
+ local f="$1"
96
+ local budget
97
+ budget=$(_feed_budget_bytes)
98
+ [ -f "$f" ] || return 0
99
+ local raw
100
+ raw=$(dd bs=1 count="$budget" if="$f" 2>/dev/null)
101
+ # Keep complete lines only. awk prints every line that ended with a newline
102
+ # within the byte window; the trailing partial line (no newline) is dropped.
103
+ # If awk yields nothing (the window is a single unterminated long line), keep
104
+ # the raw head so content is never silently emptied.
105
+ local trimmed
106
+ trimmed=$(printf '%s' "$raw" | awk '{
107
+ if (NR > 1) print buf
108
+ buf = $0
109
+ } END { }')
110
+ if [ -z "$trimmed" ]; then
111
+ printf '%s' "$raw"
112
+ else
113
+ printf '%s\n' "$trimmed"
114
+ fi
115
+ }
116
+
117
+ # _feed_summarize <file>
118
+ # Budget-fitting summary: the budget head (complete lines) plus an explicit,
119
+ # bilingual elision marker so the omission is never silent. Pure stdout.
120
+ _feed_summarize() {
121
+ local f="$1"
122
+ [ -f "$f" ] || return 0
123
+ _feed_budget_head "$f"
124
+ printf '%s\n' "[context-feed] ... summarized: tail elided, full text at the path noted above ..."
125
+ printf '%s\n' "[投喂预算] ……已摘要:尾部内容省略,全文见上方所注路径……"
126
+ }
127
+
128
+ # _feed_chunk_count <file>
129
+ # Number of budget-sized chunks the file spans (ceil(size / budget)), min 1.
130
+ _feed_chunk_count() {
131
+ local f="$1"
132
+ local size budget
133
+ size=$(_feed_size_bytes "$f")
134
+ budget=$(_feed_budget_bytes)
135
+ [ "$budget" -gt 0 ] || budget=1
136
+ local n=$(( (size + budget - 1) / budget ))
137
+ [ "$n" -lt 1 ] && n=1
138
+ echo "$n"
139
+ }
140
+
141
+ # _feed_chunk <file>
142
+ # Inject the FIRST budget-sized chunk (complete lines) with an explicit chunk
143
+ # header "chunk 1/N", so the slicing is real and labelled — not a mislabelled
144
+ # summary. Remaining chunks live in the full text at the noted path.
145
+ _feed_chunk() {
146
+ local f="$1"
147
+ [ -f "$f" ] || return 0
148
+ local n
149
+ n=$(_feed_chunk_count "$f")
150
+ printf '%s\n' "[context-feed] chunk 1/${n} (remaining chunks in full text at the path noted above):"
151
+ printf '%s\n' "[投喂预算] 第 1/${n} 段(其余段见上方所注路径全文):"
152
+ _feed_budget_head "$f"
153
+ }
154
+
155
+ # _feed_assemble <file>
156
+ # Top-level injector. Assembles the material to feed for <file> according to the
157
+ # active budget + plan, with explicit annotation for non-full plans. Pure stdout;
158
+ # callers capture this as the material to splice into the prompt.
159
+ _feed_assemble() {
160
+ local f="$1"
161
+ local plan
162
+ plan=$(_feed_plan "$f")
163
+ case "$plan" in
164
+ full)
165
+ [ -f "$f" ] && cat "$f"
166
+ ;;
167
+ summarized)
168
+ _feed_summary_notice "$f" "$plan"
169
+ printf '\n'
170
+ _feed_summarize "$f"
171
+ ;;
172
+ chunked)
173
+ _feed_summary_notice "$f" "$plan"
174
+ printf '\n'
175
+ _feed_chunk "$f"
176
+ ;;
177
+ esac
178
+ }
179
+
180
+ # _feed_log_line <file> <plan>
181
+ # A single structured log line recording the actual INJECTED size + chosen
182
+ # strategy, for the event log. "Injected" = the byte count _feed_assemble emits
183
+ # (for full == source size; for summarized/chunked == the bounded material),
184
+ # satisfying the AC's "记录实际注入体积". Format is grep-friendly and stable:
185
+ # context_feed file=<f> strategy=<plan> bytes=<n> budget=<b>
186
+ _feed_log_line() {
187
+ local f="$1" plan="${2:-}"
188
+ [ -n "$plan" ] || plan=$(_feed_plan "$f")
189
+ local bytes budget
190
+ budget=$(_feed_budget_bytes)
191
+ bytes=$(_feed_assemble "$f" | wc -c | tr -d ' ')
192
+ case "$bytes" in ''|*[!0-9]*) bytes=0 ;; esac
193
+ printf 'context_feed file=%s strategy=%s bytes=%s budget=%s\n' "$f" "$plan" "$bytes" "$budget"
194
+ }