@seanyao/roll 3.609.2 → 3.610.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.
- package/CHANGELOG.md +44 -1
- package/README.md +5 -6
- package/dist/roll.mjs +16146 -16186
- package/package.json +3 -2
- package/skills/README.md +14 -1
- package/skills/docs/skill-authoring.md +66 -0
- package/skills/reports/skill-audit-summary.md +53 -0
- package/skills/roll-.changelog/SKILL.md +25 -443
- package/skills/roll-.changelog/references/full-contract.md +462 -0
- package/skills/roll-.clarify/SKILL.md +6 -4
- package/skills/roll-.dream/SKILL.md +26 -353
- package/skills/roll-.dream/references/full-contract.md +365 -0
- package/skills/roll-.echo/SKILL.md +6 -4
- package/skills/roll-.qa/SKILL.md +25 -236
- package/skills/roll-.qa/references/full-contract.md +256 -0
- package/skills/roll-.review/SKILL.md +6 -2
- package/skills/roll-brief/SKILL.md +6 -8
- package/skills/roll-build/SKILL.md +28 -864
- package/skills/roll-build/references/full-contract.md +883 -0
- package/skills/roll-debug/SKILL.md +26 -585
- package/skills/roll-debug/references/full-contract.md +607 -0
- package/skills/roll-design/SKILL.md +28 -903
- package/skills/roll-design/references/full-contract.md +923 -0
- package/skills/roll-doc/SKILL.md +25 -574
- package/skills/roll-doc/references/full-contract.md +594 -0
- package/skills/roll-doctor/SKILL.md +21 -2
- package/skills/roll-fix/SKILL.md +28 -621
- package/skills/roll-fix/references/full-contract.md +640 -0
- package/skills/roll-idea/SKILL.md +6 -2
- package/skills/roll-loop/SKILL.md +27 -543
- package/skills/roll-loop/references/full-contract.md +555 -0
- package/skills/roll-notes/SKILL.md +6 -2
- package/skills/roll-onboard/SKILL.md +6 -2
- package/skills/roll-peer/SKILL.md +27 -316
- package/skills/roll-peer/references/full-contract.md +329 -0
- package/skills/roll-propose/SKILL.md +6 -8
- package/skills/roll-review-pr/SKILL.md +6 -2
- package/skills/roll-sentinel/SKILL.md +26 -344
- package/skills/roll-sentinel/references/full-contract.md +363 -0
- package/skills/roll-spar/SKILL.md +27 -269
- package/skills/roll-spar/references/full-contract.md +288 -0
- package/skills/route-cases/skills.json +235 -0
- package/skills/scripts/audit-skills.mjs +272 -0
- package/skills/scripts/test-audit-skills.mjs +39 -0
- package/skills/tests/fixtures/skill-audit/block-skill/SKILL.md +12 -0
- package/skills/tests/fixtures/skill-audit/minimal-skill/SKILL.md +8 -0
- package/skills/tests/fixtures/skill-audit/quoted-skill/SKILL.md +10 -0
- package/skills/tests/fixtures/skill-audit/route-cases.json +21 -0
- package/skills/tests/fixtures/skill-audit/spoke-skill/SKILL.md +12 -0
- package/skills/tests/fixtures/skill-audit/spoke-skill/references/runbook.md +3 -0
- package/bin/roll +0 -15361
- package/lib/backfill-pi-usage.py +0 -243
- package/lib/changelog_audit.py +0 -149
- package/lib/changelog_generate.py +0 -470
- package/lib/consistency_check.py +0 -409
- package/lib/context_feed_budget.sh +0 -194
- package/lib/github_sync.py +0 -876
- package/lib/i18n/slides.sh +0 -3
- package/lib/i18n/slides_build.sh +0 -38
- package/lib/i18n/slides_delete.sh +0 -19
- package/lib/i18n/slides_list.sh +0 -14
- package/lib/i18n/slides_logs.sh +0 -12
- package/lib/i18n/slides_new.sh +0 -15
- package/lib/i18n/slides_preview.sh +0 -14
- package/lib/i18n/slides_templates.sh +0 -7
- package/lib/i18n.sh +0 -211
- package/lib/loop-exit-summary.py +0 -393
- package/lib/loop-fmt.py +0 -589
- package/lib/loop_pick_agent.py +0 -316
- package/lib/loop_result_eval.py +0 -469
- package/lib/loop_unstick.py +0 -180
- package/lib/model_prices.py +0 -194
- package/lib/prices_fetcher.py +0 -534
- package/lib/roll-backlog.py +0 -225
- package/lib/roll-brief.py +0 -286
- package/lib/roll-help.py +0 -158
- package/lib/roll-home.py +0 -556
- package/lib/roll-init.py +0 -156
- package/lib/roll-loop-status.py +0 -1691
- package/lib/roll-loop-story.py +0 -191
- package/lib/roll-peer.py +0 -252
- package/lib/roll-setup.py +0 -102
- package/lib/roll-status.py +0 -367
- package/lib/roll_git.py +0 -41
- package/lib/roll_render.py +0 -414
- package/lib/slides/components/README.md +0 -123
- package/lib/slides/components/cards-2.html +0 -9
- package/lib/slides/components/cards-3.html +0 -9
- package/lib/slides/components/cards-4.html +0 -9
- package/lib/slides/components/compare.html +0 -22
- package/lib/slides/components/highlight.html +0 -9
- package/lib/slides/components/pipeline.html +0 -12
- package/lib/slides/components/plain.html +0 -7
- package/lib/slides/components/quote.html +0 -4
- package/lib/slides/components/timeline.html +0 -9
- package/lib/slides/templates/introduction-v3.html +0 -571
- package/lib/slides/templates/pitch.html +0 -0
- package/lib/slides-render.py +0 -778
- package/lib/slides-validate.py +0 -357
- package/lib/test_quality_gate.py +0 -143
- package/skills/roll-deck/SKILL.md +0 -296
- /package/skills/roll-debug/{injectable-bb.js → assets/injectable-bb.js} +0 -0
- /package/skills/roll-design/{ENGINEERING_CHECKLIST.md → references/engineering-checklist.md} +0 -0
package/lib/i18n/slides.sh
DELETED
package/lib/i18n/slides_build.sh
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_build.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_build.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_build.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_build.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
_i18n_set en slides_build.slides_toolchain_missing_re_run_roll "Slides toolchain missing — re-run 'roll setup'"
|
|
7
|
-
_i18n_set zh slides_build.slides_toolchain_missing_re_run_roll "渲染工具缺失,请运行 roll setup"
|
|
8
|
-
_i18n_set en slides_build.deck_md " deck.md"
|
|
9
|
-
_i18n_set zh slides_build.deck_md "校验失败,请先修复上方提示再重试。"
|
|
10
|
-
_i18n_set en slides_build.template_not_found "Template not found: %s"
|
|
11
|
-
_i18n_set zh slides_build.template_not_found "未找到模板:%s"
|
|
12
|
-
_i18n_set en slides_build.render_failed_for "Render failed for %s"
|
|
13
|
-
_i18n_set zh slides_build.render_failed_for "渲染失败:%s"
|
|
14
|
-
_i18n_set en slides_build.rendered "Rendered → %s"
|
|
15
|
-
_i18n_set zh slides_build.rendered "渲染完成 → %s"
|
|
16
|
-
|
|
17
|
-
# US-DECK-012: build failure recovery paths
|
|
18
|
-
_i18n_set en slides_build.validation_failed_for "Validation failed for %s"
|
|
19
|
-
_i18n_set zh slides_build.validation_failed_for "校验失败:%s"
|
|
20
|
-
_i18n_set en slides_build.hint_fix_and_rerun "Hint: fix the issues above in %s and re-run 'roll slides build %s'"
|
|
21
|
-
_i18n_set zh slides_build.hint_fix_and_rerun "提示:请按上方提示修复 %s,然后重新运行 'roll slides build %s'"
|
|
22
|
-
_i18n_set en slides_build.available_templates "Available templates:"
|
|
23
|
-
_i18n_set zh slides_build.available_templates "可用模板:"
|
|
24
|
-
_i18n_set en slides_build.templates_list_hint "Hint: 'roll slides templates' lists all installed templates"
|
|
25
|
-
_i18n_set zh slides_build.templates_list_hint "提示:'roll slides templates' 列出所有已安装模板"
|
|
26
|
-
_i18n_set en slides_build.renderer_crashed_for "Renderer crashed for %s"
|
|
27
|
-
_i18n_set zh slides_build.renderer_crashed_for "渲染器崩溃:%s"
|
|
28
|
-
_i18n_set en slides_build.see_full_error_logs "See full error: roll slides logs %s"
|
|
29
|
-
_i18n_set zh slides_build.see_full_error_logs "查看完整错误:roll slides logs %s"
|
|
30
|
-
_i18n_set en slides_build.last_5_lines_of_renderer_output "Last 5 lines of renderer output:"
|
|
31
|
-
_i18n_set zh slides_build.last_5_lines_of_renderer_output "渲染器最后 5 行:"
|
|
32
|
-
|
|
33
|
-
_i18n_set en slides_build.usage_roll_slides_build_slug_no "Usage: roll slides build <slug> [--no-open]"
|
|
34
|
-
_i18n_set zh slides_build.usage_roll_slides_build_slug_no "用法: roll slides build <slug> [--no-open]"
|
|
35
|
-
_i18n_set en slides_build.en_deck "[EN: 未找到 deck 文件:%s...]"
|
|
36
|
-
_i18n_set zh slides_build.en_deck " 未找到 deck 文件:%s"
|
|
37
|
-
_i18n_set en slides_build.en_roll_slides_new "[EN: 提示:先运行 'roll slides new \"<主题>\"' 生成新的幻灯片。...]"
|
|
38
|
-
_i18n_set zh slides_build.en_roll_slides_new " 提示:先运行 'roll slides new \"<主题>\"' 生成新的幻灯片。"
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_delete.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_delete.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_delete.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_delete.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
_i18n_set en slides_delete.deck_not_found "Deck not found: %s"
|
|
7
|
-
_i18n_set zh slides_delete.deck_not_found "未找到幻灯片:%s"
|
|
8
|
-
_i18n_set en slides_delete.non_interactive_terminal_must_use_force "Non-interactive terminal: must use --force to delete"
|
|
9
|
-
_i18n_set zh slides_delete.non_interactive_terminal_must_use_force "非交互终端:需使用 --force 参数"
|
|
10
|
-
_i18n_set en slides_delete.cancelled "Cancelled"
|
|
11
|
-
_i18n_set zh slides_delete.cancelled "已取消"
|
|
12
|
-
_i18n_set en slides_delete.deleted "Deleted %s"
|
|
13
|
-
_i18n_set zh slides_delete.deleted "已删除 %s"
|
|
14
|
-
|
|
15
|
-
_i18n_set en slides_delete.usage_roll_slides_delete_slug_force "Usage: roll slides delete <slug> [--force]"
|
|
16
|
-
_i18n_set zh slides_delete.usage_roll_slides_delete_slug_force "用法: roll slides delete <slug> [--force]"
|
|
17
|
-
|
|
18
|
-
_i18n_set en slides_delete.prompt 'Delete deck "%s"? (y/N)'
|
|
19
|
-
_i18n_set zh slides_delete.prompt '删除幻灯片 "%s"?(y/N)'
|
package/lib/i18n/slides_list.sh
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_list.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_list.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_list.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_list.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
_i18n_set en slides_list.no_decks_found_under_roll_slides "No decks found under .roll/slides/"
|
|
7
|
-
_i18n_set zh slides_list.no_decks_found_under_roll_slides "无幻灯片"
|
|
8
|
-
_i18n_set en slides_list.no_decks_found_under_roll_slides_2 "No decks found under .roll/slides/"
|
|
9
|
-
_i18n_set zh slides_list.no_decks_found_under_roll_slides_2 "无幻灯片"
|
|
10
|
-
|
|
11
|
-
_i18n_set en slides_list.en_roll_slides_new "[EN: 提示:运行 'roll slides new \"<主题>\"' 创建第一个幻灯片。...]"
|
|
12
|
-
_i18n_set zh slides_list.en_roll_slides_new " 提示:运行 'roll slides new \"<主题>\"' 创建第一个幻灯片。"
|
|
13
|
-
_i18n_set en slides_list.en_roll_slides_new_2 "[EN: 提示:运行 'roll slides new \"<主题>\"' 创建第一个幻灯片。...]"
|
|
14
|
-
_i18n_set zh slides_list.en_roll_slides_new_2 " 提示:运行 'roll slides new \"<主题>\"' 创建第一个幻灯片。"
|
package/lib/i18n/slides_logs.sh
DELETED
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_logs.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_logs.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_logs.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_logs.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
_i18n_set en slides_logs.deck_not_found "Deck not found: %s"
|
|
7
|
-
_i18n_set zh slides_logs.deck_not_found "未找到幻灯片:%s"
|
|
8
|
-
_i18n_set en slides_logs.no_failure_records_for "No failure records for %s"
|
|
9
|
-
_i18n_set zh slides_logs.no_failure_records_for "该幻灯片没有失败记录"
|
|
10
|
-
|
|
11
|
-
_i18n_set en slides_logs.usage_roll_slides_logs_slug "Usage: roll slides logs <slug>"
|
|
12
|
-
_i18n_set zh slides_logs.usage_roll_slides_logs_slug "用法: roll slides logs <slug>"
|
package/lib/i18n/slides_new.sh
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_new.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_new.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_new.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_new.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
|
|
7
|
-
_i18n_set en slides_new.en_roll_slides_new_template "[EN: 用法:roll slides new \"<主题>\" [--template <模板名>] [...]"
|
|
8
|
-
_i18n_set zh slides_new.en_roll_slides_new_template " 用法:roll slides new \"<主题>\" [--template <模板名>] [--quiet] [--no-build]"
|
|
9
|
-
_i18n_set en slides_new.en_slug "[EN: 无法从主题派生 slug:%s...]"
|
|
10
|
-
_i18n_set zh slides_new.en_slug " 无法从主题派生 slug:%s"
|
|
11
|
-
_i18n_set en slides_new.en_roll_slides_build "[EN:下一步:roll slides build %s...]"
|
|
12
|
-
_i18n_set zh slides_new.en_roll_slides_build "下一步:roll slides build %s"
|
|
13
|
-
|
|
14
|
-
_i18n_set en slides_new.template_requires_value "--template requires a value"
|
|
15
|
-
_i18n_set zh slides_new.template_requires_value "--template 需要一个值"
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_preview.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_preview.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_preview.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_preview.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
_i18n_set en slides_preview.preview "Preview → %s"
|
|
7
|
-
_i18n_set zh slides_preview.preview "打开预览 → %s"
|
|
8
|
-
|
|
9
|
-
_i18n_set en slides_preview.usage_roll_slides_preview_slug_no "Usage: roll slides preview <slug> [--no-open]"
|
|
10
|
-
_i18n_set zh slides_preview.usage_roll_slides_preview_slug_no "用法: roll slides preview <slug> [--no-open]"
|
|
11
|
-
_i18n_set en slides_preview.en_html "[EN: 未找到已渲染的 HTML:%s...]"
|
|
12
|
-
_i18n_set zh slides_preview.en_html " 未找到已渲染的 HTML:%s"
|
|
13
|
-
_i18n_set en slides_preview.en_roll_slides_build "[EN: 提示:先运行 'roll slides build %s' 渲染幻灯片。...]"
|
|
14
|
-
_i18n_set zh slides_preview.en_roll_slides_build " 提示:先运行 'roll slides build %s' 渲染幻灯片。"
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
_i18n_set en slides_templates.unknown_option_1 "Unknown option: %s"
|
|
3
|
-
_i18n_set zh slides_templates.unknown_option_1 "未知选项: %s"
|
|
4
|
-
_i18n_set en slides_templates.unexpected_argument_1 "Unexpected argument: %s"
|
|
5
|
-
_i18n_set zh slides_templates.unexpected_argument_1 "多余参数: %s"
|
|
6
|
-
_i18n_set en slides_templates.no_templates_found "No templates found"
|
|
7
|
-
_i18n_set zh slides_templates.no_templates_found "无可用模板"
|
package/lib/i18n.sh
DELETED
|
@@ -1,211 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# Roll i18n engine — US-I18N-001.
|
|
3
|
-
#
|
|
4
|
-
# Provides:
|
|
5
|
-
# _i18n_resolve_lang — resolve user's language per precedence
|
|
6
|
-
# msg <key> [args...] — look up message catalog with EN fallback
|
|
7
|
-
# _i18n_set <lang> <key> <value> — fill the catalog (used by modules)
|
|
8
|
-
#
|
|
9
|
-
# Storage is bash 3.2-compatible (macOS default ships 3.2): each catalog entry
|
|
10
|
-
# is held in a plain variable named `MSG_<LANG>_<key>`, looked up via indirect
|
|
11
|
-
# reference. Bash 4 assoc arrays would be cleaner but break macOS default bash
|
|
12
|
-
# (see AGENTS.md §4: single bash script, no runtime).
|
|
13
|
-
#
|
|
14
|
-
# Precedence: ROLL_LANG env > ~/.roll/config.yaml `lang` > LC_ALL > LANG
|
|
15
|
-
# > (macOS) AppleLanguages > 'en'.
|
|
16
|
-
# Decision: value starting with `zh` → "zh", everything else → "en".
|
|
17
|
-
|
|
18
|
-
# Fill the catalog. Modules call this at source-time:
|
|
19
|
-
# _i18n_set en hello "Hello, %s!"
|
|
20
|
-
# _i18n_set zh hello "你好,%s!"
|
|
21
|
-
# No subshell forks: lang uppercasing uses a case statement (bash 3.2-safe;
|
|
22
|
-
# ${var^^} requires bash 4+), key sanitization uses inline param-expansion.
|
|
23
|
-
_i18n_set() {
|
|
24
|
-
local lang="$1" key="$2" val="$3"
|
|
25
|
-
local upper safe varname
|
|
26
|
-
case "$lang" in
|
|
27
|
-
en|EN) upper=EN ;;
|
|
28
|
-
zh|ZH) upper=ZH ;;
|
|
29
|
-
*) upper="$(printf '%s' "$lang" | tr '[:lower:]' '[:upper:]')" ;;
|
|
30
|
-
esac
|
|
31
|
-
safe="${key//[^A-Za-z0-9_]/_}" # inline param-expansion — no subshell fork
|
|
32
|
-
varname="MSG_${upper}_${safe}"
|
|
33
|
-
printf -v "$varname" '%s' "$val"
|
|
34
|
-
export "$varname"
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
# Decide "zh" or "en" from a free-form locale string.
|
|
38
|
-
_i18n_classify() {
|
|
39
|
-
case "${1:-}" in
|
|
40
|
-
zh*) echo zh ;;
|
|
41
|
-
*) echo en ;;
|
|
42
|
-
esac
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
# Resolve the active language. Caches in ROLL_LANG_RESOLVED so later calls are
|
|
46
|
-
# free.
|
|
47
|
-
_i18n_resolve_lang() {
|
|
48
|
-
if [[ -n "${ROLL_LANG_RESOLVED:-}" ]]; then
|
|
49
|
-
echo "$ROLL_LANG_RESOLVED"
|
|
50
|
-
return
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
local lang=""
|
|
54
|
-
|
|
55
|
-
if [[ -n "${ROLL_LANG:-}" ]]; then
|
|
56
|
-
lang=$(_i18n_classify "$ROLL_LANG")
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
if [[ -z "$lang" && -n "${ROLL_CONFIG:-}" && -f "${ROLL_CONFIG}" ]]; then
|
|
60
|
-
local cfg
|
|
61
|
-
cfg=$(grep -E '^lang:' "$ROLL_CONFIG" 2>/dev/null | head -1 \
|
|
62
|
-
| sed 's/^lang:[[:space:]]*//' \
|
|
63
|
-
| sed 's/[[:space:]]*#.*$//' \
|
|
64
|
-
| sed 's/[[:space:]]*$//')
|
|
65
|
-
[[ -n "$cfg" ]] && lang=$(_i18n_classify "$cfg")
|
|
66
|
-
fi
|
|
67
|
-
|
|
68
|
-
if [[ -z "$lang" && -n "${LC_ALL:-}" ]]; then
|
|
69
|
-
lang=$(_i18n_classify "$LC_ALL")
|
|
70
|
-
fi
|
|
71
|
-
|
|
72
|
-
if [[ -z "$lang" && -n "${LANG:-}" ]]; then
|
|
73
|
-
lang=$(_i18n_classify "$LANG")
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
if [[ -z "$lang" ]] && command -v defaults >/dev/null 2>&1; then
|
|
77
|
-
local apple
|
|
78
|
-
apple=$(defaults read -g AppleLanguages 2>/dev/null | head -2 | tail -1 \
|
|
79
|
-
| tr -d ' ",()' | head -1 || true)
|
|
80
|
-
[[ -n "$apple" ]] && lang=$(_i18n_classify "$apple")
|
|
81
|
-
fi
|
|
82
|
-
|
|
83
|
-
[[ -z "$lang" ]] && lang="en"
|
|
84
|
-
|
|
85
|
-
ROLL_LANG_RESOLVED="$lang"
|
|
86
|
-
echo "$lang"
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
# Look up message catalog entry. Falls back to EN, then to the key itself so
|
|
90
|
-
# missing translations stay visible without crashing the caller.
|
|
91
|
-
msg() {
|
|
92
|
-
local key="$1"; shift || true
|
|
93
|
-
local lang safe
|
|
94
|
-
# FIX: avoid subshell forks — check cached value first, inline key sanitize
|
|
95
|
-
if [[ -n "${ROLL_LANG_RESOLVED:-}" ]]; then
|
|
96
|
-
lang="$ROLL_LANG_RESOLVED"
|
|
97
|
-
else
|
|
98
|
-
lang=$(_i18n_resolve_lang)
|
|
99
|
-
fi
|
|
100
|
-
safe="${key//[^A-Za-z0-9_]/_}" # FIX: inline — no subshell fork
|
|
101
|
-
|
|
102
|
-
local zh_var="MSG_ZH_${safe}"
|
|
103
|
-
local en_var="MSG_EN_${safe}"
|
|
104
|
-
local tmpl=""
|
|
105
|
-
|
|
106
|
-
if [[ "$lang" == "zh" && -n "${!zh_var:-}" ]]; then
|
|
107
|
-
tmpl="${!zh_var}"
|
|
108
|
-
elif [[ -n "${!en_var:-}" ]]; then
|
|
109
|
-
tmpl="${!en_var}"
|
|
110
|
-
else
|
|
111
|
-
tmpl="$key"
|
|
112
|
-
fi
|
|
113
|
-
|
|
114
|
-
# shellcheck disable=SC2059 — template comes from our own catalog
|
|
115
|
-
printf "$tmpl" "$@"
|
|
116
|
-
echo
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
# Look up message catalog entry with an explicit language, bypassing ROLL_LANG env.
|
|
120
|
-
# Usage: msg_lang <lang> <key> [printf-args...]
|
|
121
|
-
# Useful when the caller tracks language independently of the process env (e.g.
|
|
122
|
-
# _loop_schedule_desc passes an explicit lang= parameter).
|
|
123
|
-
msg_lang() {
|
|
124
|
-
local lang="$1" key="$2"; shift 2 || true
|
|
125
|
-
local upper safe varname tmpl
|
|
126
|
-
case "$lang" in
|
|
127
|
-
en|EN) upper=EN ;;
|
|
128
|
-
zh|ZH) upper=ZH ;;
|
|
129
|
-
*) upper="$(printf '%s' "$lang" | tr '[:lower:]' '[:upper:]')" ;;
|
|
130
|
-
esac
|
|
131
|
-
safe="${key//[^A-Za-z0-9_]/_}"
|
|
132
|
-
varname="MSG_${upper}_${safe}"
|
|
133
|
-
tmpl="${!varname:-}"
|
|
134
|
-
if [[ -z "$tmpl" ]]; then
|
|
135
|
-
varname="MSG_EN_${safe}"
|
|
136
|
-
tmpl="${!varname:-$key}"
|
|
137
|
-
fi
|
|
138
|
-
# shellcheck disable=SC2059 — template comes from our own catalog
|
|
139
|
-
printf "$tmpl" "$@"
|
|
140
|
-
echo
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
# ── CJK display-width helpers (US-I18N-006) ──────────────────────────────
|
|
144
|
-
# Compute visual display width of a string (CJK fullwidth = 2 cells,
|
|
145
|
-
# ASCII = 1 cell). Delegates to python3 which is already a dependency.
|
|
146
|
-
_strw() {
|
|
147
|
-
python3 -c "
|
|
148
|
-
import sys
|
|
149
|
-
from unicodedata import east_asian_width
|
|
150
|
-
s = sys.argv[1]
|
|
151
|
-
w = sum(2 if east_asian_width(c) in ('F','W') else 1 for c in s)
|
|
152
|
-
print(w)
|
|
153
|
-
" "$1"
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
# Left-pad a string to a target visual display width (CJK-aware).
|
|
157
|
-
_pad_l() {
|
|
158
|
-
local s="$1" target="$2"
|
|
159
|
-
local sw
|
|
160
|
-
sw=$(_strw "$s")
|
|
161
|
-
local pad=$((target - sw))
|
|
162
|
-
if ((pad <= 0)); then
|
|
163
|
-
printf '%s' "$s"
|
|
164
|
-
else
|
|
165
|
-
printf '%s%*s' "$s" "$pad" ''
|
|
166
|
-
fi
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
# Right-pad a string to a target visual display width (CJK-aware).
|
|
170
|
-
_pad_r() {
|
|
171
|
-
local s="$1" target="$2"
|
|
172
|
-
local sw
|
|
173
|
-
sw=$(_strw "$s")
|
|
174
|
-
local pad=$((target - sw))
|
|
175
|
-
if ((pad <= 0)); then
|
|
176
|
-
printf '%s' "$s"
|
|
177
|
-
else
|
|
178
|
-
printf '%*s%s' "$pad" '' "$s"
|
|
179
|
-
fi
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
# ── Load per-command message catalogs (US-I18N-002) ──
|
|
183
|
-
# Source all lib/i18n/*.sh files (skip self). Called once at bin/roll startup.
|
|
184
|
-
_i18n_load_catalogs() {
|
|
185
|
-
# FIX: guard — skip re-load when catalogs already loaded in this shell.
|
|
186
|
-
# Each bats test sources bin/roll in its own subshell so the guard resets
|
|
187
|
-
# automatically; but within a single shell (e.g. integration tests) this
|
|
188
|
-
# prevents 2.6s of catalog I/O on every subsequent source.
|
|
189
|
-
[[ -n "${_I18N_CATALOGS_LOADED:-}" ]] && return 0
|
|
190
|
-
local i18n_dir
|
|
191
|
-
i18n_dir="$(dirname "${BASH_SOURCE[0]:-$0}")/i18n"
|
|
192
|
-
if [[ -d "$i18n_dir" ]]; then
|
|
193
|
-
local f
|
|
194
|
-
# Load per-command catalogs
|
|
195
|
-
for f in "$i18n_dir"/*.sh; do
|
|
196
|
-
[[ -f "$f" ]] || continue
|
|
197
|
-
# shellcheck source=/dev/null
|
|
198
|
-
source "$f"
|
|
199
|
-
done
|
|
200
|
-
# Load skill catalogs (US-I18N-003)
|
|
201
|
-
if [[ -d "$i18n_dir/skills" ]]; then
|
|
202
|
-
for f in "$i18n_dir/skills"/*.sh; do
|
|
203
|
-
[[ -f "$f" ]] || continue
|
|
204
|
-
# shellcheck source=/dev/null
|
|
205
|
-
source "$f"
|
|
206
|
-
done
|
|
207
|
-
fi
|
|
208
|
-
fi
|
|
209
|
-
_I18N_CATALOGS_LOADED=1
|
|
210
|
-
}
|
|
211
|
-
_i18n_load_catalogs
|