@promptctl/cc-candybar 1.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.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +145 -0
  3. package/bin/cc-candybar +6 -0
  4. package/dist/index.mjs +185 -0
  5. package/package.json +99 -0
  6. package/plugin/.claude-plugin/plugin.json +11 -0
  7. package/plugin/bin/preview.sh +305 -0
  8. package/plugin/commands/candybar.md +403 -0
  9. package/plugin/templates/config-essential.json +36 -0
  10. package/plugin/templates/config-full.json +55 -0
  11. package/plugin/templates/config-standard.json +39 -0
  12. package/plugin/templates/config-tui-compact.json +48 -0
  13. package/plugin/templates/config-tui-full.json +89 -0
  14. package/plugin/templates/config-tui-standard.json +56 -0
  15. package/plugin/templates/config-tui.json +18 -0
  16. package/plugin/templates/nerd-fonts-sample.txt +5 -0
  17. package/schema/cc-candybar.schema.json +1379 -0
  18. package/src/click/wire.ts +113 -0
  19. package/src/config/action.ts +91 -0
  20. package/src/config/cli.ts +170 -0
  21. package/src/config/default-dsl-config.ts +661 -0
  22. package/src/config/dsl-loader.ts +265 -0
  23. package/src/config/dsl-types.ts +425 -0
  24. package/src/config/loader/actions.ts +530 -0
  25. package/src/config/loader/cache.ts +206 -0
  26. package/src/config/loader/cross-ref.ts +326 -0
  27. package/src/config/loader/cycles.ts +148 -0
  28. package/src/config/loader/diagnostics.ts +99 -0
  29. package/src/config/loader/discovery.ts +182 -0
  30. package/src/config/loader/emit-schema.ts +63 -0
  31. package/src/config/loader/globals.ts +42 -0
  32. package/src/config/loader/helpers.ts +48 -0
  33. package/src/config/loader/layout.ts +688 -0
  34. package/src/config/loader/merge.ts +40 -0
  35. package/src/config/loader/refs.ts +96 -0
  36. package/src/config/loader/segments.ts +120 -0
  37. package/src/config/loader/validate-core.ts +674 -0
  38. package/src/config/loader/variables.ts +260 -0
  39. package/src/daemon/acquire.ts +411 -0
  40. package/src/daemon/cache/git.ts +553 -0
  41. package/src/daemon/cache/render.ts +449 -0
  42. package/src/daemon/cache/session-usage-store.ts +446 -0
  43. package/src/daemon/cache/watchers.ts +245 -0
  44. package/src/daemon/client-debug.ts +120 -0
  45. package/src/daemon/client-stats.ts +129 -0
  46. package/src/daemon/client-transport.ts +273 -0
  47. package/src/daemon/client.ts +75 -0
  48. package/src/daemon/debug-types.ts +91 -0
  49. package/src/daemon/debug.ts +264 -0
  50. package/src/daemon/limits.ts +154 -0
  51. package/src/daemon/log.ts +69 -0
  52. package/src/daemon/parent-watchdog.ts +80 -0
  53. package/src/daemon/paths.ts +127 -0
  54. package/src/daemon/protocol.ts +235 -0
  55. package/src/daemon/render-payload.ts +611 -0
  56. package/src/daemon/server.ts +1103 -0
  57. package/src/daemon/session-state-file.ts +108 -0
  58. package/src/daemon/session-state.ts +237 -0
  59. package/src/daemon/stats.ts +229 -0
  60. package/src/daemon/verbs/index.ts +458 -0
  61. package/src/daemon/verbs/state-validators.ts +708 -0
  62. package/src/demo/dsl.ts +117 -0
  63. package/src/demo/mock-data.ts +67 -0
  64. package/src/demo/statusline.json5 +92 -0
  65. package/src/dsl/node-registry.ts +281 -0
  66. package/src/dsl/render.ts +558 -0
  67. package/src/index.ts +206 -0
  68. package/src/install/index.ts +410 -0
  69. package/src/proc/launch.ts +451 -0
  70. package/src/proc/stats-handle.ts +13 -0
  71. package/src/render/action.ts +458 -0
  72. package/src/render/diagnostic-style.ts +23 -0
  73. package/src/render/diagnostic-text.ts +77 -0
  74. package/src/render/error-glyph.ts +53 -0
  75. package/src/render/outcome-plan.ts +45 -0
  76. package/src/render/picker.ts +231 -0
  77. package/src/render/split-lines.ts +51 -0
  78. package/src/render/strip.ts +103 -0
  79. package/src/segments/cache.ts +131 -0
  80. package/src/segments/context.ts +190 -0
  81. package/src/segments/git.ts +561 -0
  82. package/src/segments/metrics.ts +101 -0
  83. package/src/segments/pricing.ts +452 -0
  84. package/src/segments/session.ts +188 -0
  85. package/src/segments/tmux.ts +74 -0
  86. package/src/template-engine/cells.ts +90 -0
  87. package/src/template-engine/colors.ts +102 -0
  88. package/src/template-engine/engine.ts +108 -0
  89. package/src/template-engine/funcs.ts +216 -0
  90. package/src/template-engine/index.ts +11 -0
  91. package/src/template-engine/layout.ts +112 -0
  92. package/src/template-engine/scope.ts +62 -0
  93. package/src/themes/index.ts +19 -0
  94. package/src/themes/palette-resolvers.ts +86 -0
  95. package/src/themes/policy.ts +79 -0
  96. package/src/themes/session-random.ts +88 -0
  97. package/src/utils/cache.ts +206 -0
  98. package/src/utils/claude.ts +616 -0
  99. package/src/utils/color-support.ts +118 -0
  100. package/src/utils/formatters.ts +77 -0
  101. package/src/utils/logger.ts +5 -0
  102. package/src/utils/outcome.ts +33 -0
  103. package/src/utils/schema-validator.ts +126 -0
  104. package/src/utils/single-flight.ts +57 -0
  105. package/src/utils/terminal-width.ts +43 -0
  106. package/src/utils/terminal.ts +11 -0
  107. package/src/utils/transcript-fs.ts +162 -0
  108. package/src/var-system/index.ts +24 -0
  109. package/src/var-system/sources.ts +1038 -0
  110. package/src/var-system/store.ts +223 -0
  111. package/src/var-system/types.ts +57 -0
package/package.json ADDED
@@ -0,0 +1,99 @@
1
+ {
2
+ "name": "@promptctl/cc-candybar",
3
+ "version": "1.0.0",
4
+ "description": "Statusline renderer for Claude Code \u2014 a JSON5-configurable DSL with daemon-cached data sources, byte-clean palette-aware composition, and OSC8 click verbs.",
5
+ "type": "module",
6
+ "main": "./dist/index.mjs",
7
+ "module": "./dist/index.mjs",
8
+ "exports": {
9
+ ".": "./dist/index.mjs"
10
+ },
11
+ "bin": {
12
+ "cc-candybar": "./bin/cc-candybar"
13
+ },
14
+ "scripts": {
15
+ "build": "tsx scripts/gen-schema.ts && tsdown",
16
+ "build:rust": "cd rust-client && cargo build --release",
17
+ "dev": "tsdown --watch",
18
+ "typecheck": "tsc --noEmit",
19
+ "start": "node dist/index.mjs",
20
+ "lint": "eslint src/",
21
+ "lint:fix": "eslint src/ --fix",
22
+ "test": "jest",
23
+ "test:watch": "jest --watch",
24
+ "test:coverage": "jest --coverage",
25
+ "demo": "tsx src/demo/dsl.ts",
26
+ "benchmark:timing": "scripts/benchmark_timing.sh",
27
+ "check:protocol": "node scripts/check-protocol.mjs",
28
+ "gen:schema": "tsx scripts/gen-schema.ts",
29
+ "check:schema": "tsx scripts/check-schema.ts",
30
+ "prepack": "node scripts/write-bin-placeholder.mjs",
31
+ "postinstall": "node scripts/postinstall.mjs",
32
+ "prepublishOnly": "npm run lint && npm run typecheck && npm run check:protocol && npm run check:schema && npm run build"
33
+ },
34
+ "keywords": [
35
+ "claude",
36
+ "powerline",
37
+ "statusline",
38
+ "typescript",
39
+ "claude-code"
40
+ ],
41
+ "author": "Brandon Fryslie",
42
+ "license": "MIT",
43
+ "homepage": "https://github.com/promptctl/cc-candybar#readme",
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/promptctl/cc-candybar.git"
47
+ },
48
+ "bugs": {
49
+ "url": "https://github.com/promptctl/cc-candybar/issues"
50
+ },
51
+ "engines": {
52
+ "node": "^20.19.0 || >=22.12.0"
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "bin",
57
+ "plugin",
58
+ "src",
59
+ "schema",
60
+ "README.md",
61
+ "LICENSE"
62
+ ],
63
+ "publishConfig": {
64
+ "access": "public"
65
+ },
66
+ "devDependencies": {
67
+ "@eslint/js": "^9.33.0",
68
+ "@semantic-release/changelog": "^6.0.3",
69
+ "@semantic-release/exec": "^7.0.3",
70
+ "@semantic-release/git": "^10.0.1",
71
+ "@types/jest": "^30.0.0",
72
+ "@types/node": "^20.0.0",
73
+ "@typescript-eslint/eslint-plugin": "^8.39.0",
74
+ "@typescript-eslint/parser": "^8.39.0",
75
+ "ajv": "^8.20.0",
76
+ "eslint": "^9.33.0",
77
+ "eslint-config-prettier": "^10.1.8",
78
+ "eslint-plugin-prettier": "^5.5.4",
79
+ "jest": "^30.0.5",
80
+ "prettier": "^3.6.2",
81
+ "semantic-release": "^25.0.2",
82
+ "ts-jest": "^29.4.1",
83
+ "tsdown": "^0.21.4",
84
+ "tsx": "^4.21.0",
85
+ "typescript": "^5.0.0"
86
+ },
87
+ "dependencies": {
88
+ "@promptctl/go-template-js": "^0.3.0",
89
+ "@promptctl/rich-js": "^0.5.2",
90
+ "json5": "^2.2.3",
91
+ "mobx": "^6.15.0"
92
+ },
93
+ "optionalDependencies": {
94
+ "@promptctl/cc-candybar-darwin-arm64": "1.0.0",
95
+ "@promptctl/cc-candybar-darwin-x64": "1.0.0",
96
+ "@promptctl/cc-candybar-linux-x64": "1.0.0",
97
+ "@promptctl/cc-candybar-linux-arm64": "1.0.0"
98
+ }
99
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "name": "cc-candybar",
3
+ "version": "1.0.0",
4
+ "description": "CCCandybar statusline setup wizard",
5
+ "author": {
6
+ "name": "promptctl"
7
+ },
8
+ "repository": "https://github.com/promptctl/cc-candybar",
9
+ "license": "MIT",
10
+ "keywords": ["powerline", "statusline", "claude-code", "setup"]
11
+ }
@@ -0,0 +1,305 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -euo pipefail
4
+
5
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6
+ readonly SCRIPT_DIR
7
+ readonly PLUGIN_ROOT="${SCRIPT_DIR}/.."
8
+
9
+ readonly SAMPLE_JSON='{"model":{"id":"claude-sonnet-4-20250514","display_name":"Sonnet 4"},"cost":{"total_cost_usd":0.42,"message_cost_usd":0.03,"duration":"15m"},"context_window":{"context_window_size":200000,"used_percentage":35,"current_usage":{"input_tokens":50000,"cache_creation_input_tokens":10000,"cache_read_input_tokens":5000}},"cwd":"/home/user/my-project","workspace":{"current_dir":"/home/user/my-project"},"session_id":"abc123"}'
10
+
11
+ setup_tui_transcript() {
12
+ local now_iso user_iso cwd_encoded transcript_dir
13
+ now_iso=$(date -u +"%Y-%m-%dT%H:%M:%S.000Z")
14
+ user_iso=$(date -u -d "-13 seconds" +"%Y-%m-%dT%H:%M:%S.000Z" 2>/dev/null \
15
+ || date -u -v-13S +"%Y-%m-%dT%H:%M:%S.000Z" 2>/dev/null \
16
+ || echo "${now_iso}")
17
+
18
+ # Encode the preview CWD the same way Claude Code encodes project dirs
19
+ # On Windows: C:\Users\foo\proj -> C--Users-foo-proj
20
+ # On Unix: /home/user/proj -> -home-user-proj
21
+ local preview_cwd
22
+ # Create a temp git repo so previews always show git data
23
+ local fake_repo
24
+ fake_repo="$(mktemp -d)/my-project"
25
+ mkdir -p "${fake_repo}"
26
+ git -C "${fake_repo}" init -b main --quiet 2>/dev/null
27
+ git -C "${fake_repo}" -c user.name="User" -c user.email="u@e.co" commit --allow-empty -m "init" --quiet 2>/dev/null
28
+ git -C "${fake_repo}" checkout -b feat/my-feature --quiet 2>/dev/null
29
+ touch "${fake_repo}/newfile.txt"
30
+ printf 'change\n' > "${fake_repo}/README.md"
31
+ git -C "${fake_repo}" add README.md 2>/dev/null
32
+
33
+ # Use native path for Node.js compatibility (Windows needs C:\... not /c/...)
34
+ preview_cwd="$(cd "${fake_repo}" && pwd -W 2>/dev/null || pwd)"
35
+ cwd_encoded="$(printf '%s' "${preview_cwd}" | sed 's|[/\\]|-|g; s|:||g')"
36
+ TUI_PREVIEW_CWD="${preview_cwd}"
37
+
38
+ transcript_dir="${HOME}/.claude/projects/${cwd_encoded}"
39
+ TUI_TRANSCRIPT="${transcript_dir}/preview-123.jsonl"
40
+ mkdir -p "${transcript_dir}"
41
+ printf '{"timestamp":"%s","type":"user","message":{"role":"user","content":[{"type":"text","text":"hello"}]}}\n' "${user_iso}" > "${TUI_TRANSCRIPT}"
42
+ printf '{"timestamp":"%s","type":"assistant","message":{"role":"assistant","content":[{"type":"text","text":"done"}]}}\n' "${now_iso}" >> "${TUI_TRANSCRIPT}"
43
+ }
44
+
45
+ make_tui_sample_json() {
46
+ local resets_5h resets_7d
47
+ resets_5h=$(( $(date +%s) + 15480 ))
48
+ resets_7d=$(( $(date +%s) + 432000 ))
49
+
50
+ printf '{"model":{"id":"claude-sonnet-4-20250514","display_name":"Sonnet 4"},"cost":{"total_cost_usd":2.85,"message_cost_usd":0.12,"total_duration_ms":16200000,"total_api_duration_ms":480000,"total_lines_added":342,"total_lines_removed":87},"context_window":{"context_window_size":200000,"used_percentage":42,"total_input_tokens":72000,"total_output_tokens":12000,"current_usage":{"input_tokens":72000,"output_tokens":12000,"cache_creation_input_tokens":15000,"cache_read_input_tokens":8000}},"rate_limits":{"five_hour":{"used_percentage":35,"resets_at":%d},"seven_day":{"used_percentage":28,"resets_at":%d}},"cwd":"%s","workspace":{"current_dir":"%s","project_dir":"%s"},"session_id":"preview-123","version":"1.0.47"}' \
51
+ "${resets_5h}" "${resets_7d}" "${TUI_PREVIEW_CWD}" "${TUI_PREVIEW_CWD}" "${TUI_PREVIEW_CWD}"
52
+ }
53
+
54
+ readonly PREVIEW_CONFIG='{
55
+ "theme": "dark",
56
+ "display": {
57
+ "style": "minimal",
58
+ "charset": "unicode",
59
+ "colorCompatibility": "auto",
60
+ "autoWrap": true,
61
+ "padding": 1,
62
+ "lines": [
63
+ {
64
+ "segments": {
65
+ "directory": { "enabled": true, "style": "fish" },
66
+ "git": { "enabled": true },
67
+ "model": { "enabled": true },
68
+ "session": { "enabled": true, "type": "cost", "costSource": "calculated" },
69
+ "today": { "enabled": true, "type": "cost" },
70
+ "context": { "enabled": true, "showPercentageOnly": false, "displayStyle": "text", "autocompactBuffer": 33000 }
71
+ }
72
+ }
73
+ ]
74
+ },
75
+ "budget": {
76
+ "session": { "warningThreshold": 80 },
77
+ "today": { "amount": 50, "warningThreshold": 80 }
78
+ }
79
+ }'
80
+
81
+ THEME="dark"
82
+ STYLE="minimal"
83
+ CHARSET="unicode"
84
+ COMPARE_STYLES=false
85
+ COMPARE_THEMES=false
86
+ COMPARE_TUI_LAYOUTS=false
87
+ BIN=""
88
+ TEMP_FILES=()
89
+
90
+ # shellcheck disable=SC2329
91
+ cleanup() {
92
+ for f in "${TEMP_FILES[@]}"; do
93
+ rm -f "${f}"
94
+ done
95
+ }
96
+ trap cleanup EXIT
97
+
98
+ test_binary() {
99
+ local bin="$1"
100
+ printf '{}' | "${bin}" --help >/dev/null 2>&1
101
+ }
102
+
103
+ find_binary() {
104
+ local npm_bin="${PLUGIN_ROOT}/../bin/cc-candybar"
105
+ local dist_file="${PLUGIN_ROOT}/../dist/index.mjs"
106
+ if [[ -f "${npm_bin}" ]] && [[ -f "${dist_file}" ]] && test_binary "${npm_bin}"; then
107
+ printf '%s' "${npm_bin}"
108
+ return 0
109
+ fi
110
+
111
+ local path_bin
112
+ if path_bin="$(command -v cc-candybar 2>/dev/null)" && test_binary "${path_bin}"; then
113
+ printf '%s' "${path_bin}"
114
+ return 0
115
+ fi
116
+
117
+ printf 'npx'
118
+ return 0
119
+ }
120
+
121
+ make_temp_config() {
122
+ local preview_theme="$1"
123
+ local preview_style="$2"
124
+ local preview_charset="$3"
125
+ local tmp
126
+
127
+ tmp="$(mktemp)"
128
+ TEMP_FILES+=("${tmp}")
129
+ printf '%s' "${PREVIEW_CONFIG}" |
130
+ sed -e "s/\"theme\": \"dark\"/\"theme\": \"${preview_theme}\"/" \
131
+ -e "s/\"style\": \"minimal\"/\"style\": \"${preview_style}\"/" \
132
+ -e "s/\"charset\": \"unicode\"/\"charset\": \"${preview_charset}\"/" \
133
+ >"${tmp}"
134
+ printf '%s' "${tmp}"
135
+ }
136
+
137
+ run_preview() {
138
+ local preview_theme="$1"
139
+ local preview_style="$2"
140
+ local preview_charset="$3"
141
+ local tmp_config
142
+
143
+ tmp_config="$(make_temp_config "${preview_theme}" "${preview_style}" "${preview_charset}")"
144
+
145
+ if [[ "${BIN}" == "npx" ]]; then
146
+ printf '%s' "${SAMPLE_JSON}" | FORCE_COLOR=3 npx -y @promptctl/cc-candybar@latest \
147
+ --config="${tmp_config}"
148
+ else
149
+ printf '%s' "${SAMPLE_JSON}" | FORCE_COLOR=3 "${BIN}" \
150
+ --config="${tmp_config}"
151
+ fi
152
+ }
153
+
154
+ run_preview_config() {
155
+ local config_file="$1"
156
+ local sample_data="$2"
157
+
158
+ if [[ "${BIN}" == "npx" ]]; then
159
+ printf '%s' "${sample_data}" | FORCE_COLOR=3 npx -y @promptctl/cc-candybar@latest \
160
+ --config="${config_file}"
161
+ else
162
+ printf '%s' "${sample_data}" | FORCE_COLOR=3 "${BIN}" \
163
+ --config="${config_file}"
164
+ fi
165
+ }
166
+
167
+ run_compare_tui_layouts() {
168
+ local layouts=(compact standard full)
169
+ local label
170
+ local template_file tmp tui_json fake_repo
171
+
172
+ setup_tui_transcript
173
+ tui_json="$(make_tui_sample_json)"
174
+ fake_repo="${TUI_PREVIEW_CWD}"
175
+
176
+ for label in "${layouts[@]}"; do
177
+ template_file="${PLUGIN_ROOT}/templates/config-tui-${label}.json"
178
+ if [[ ! -f "${template_file}" ]]; then
179
+ printf '%s: template not found\n\n' "${label}"
180
+ continue
181
+ fi
182
+
183
+ tmp="$(mktemp)"
184
+ TEMP_FILES+=("${tmp}")
185
+ sed -e "s/replace:THEME/${THEME}/g" \
186
+ -e "s/replace:CHARSET/${CHARSET}/g" \
187
+ -e "s/\"replace:TODAY_BUDGET\"/50/g" \
188
+ "${template_file}" >"${tmp}"
189
+
190
+ printf '%s:\n' "${label}"
191
+ run_preview_config "${tmp}" "${tui_json}"
192
+ printf '\n\n'
193
+ done
194
+
195
+ # Clean up fake transcript and git repo
196
+ rm -f "${TUI_TRANSCRIPT}" 2>/dev/null
197
+ rm -rf "${fake_repo%/my-project}" 2>/dev/null
198
+ }
199
+
200
+ run_compare_styles() {
201
+ local styles=(minimal powerline capsule tui)
202
+ local s
203
+
204
+ for s in "${styles[@]}"; do
205
+ printf '%s:\n' "${s}"
206
+ run_preview "${THEME}" "${s}" "${CHARSET}"
207
+ printf '\n\n'
208
+ done
209
+ }
210
+
211
+ run_compare_themes() {
212
+ local themes=(dark light nord tokyo-night rose-pine gruvbox)
213
+ local t
214
+
215
+ for t in "${themes[@]}"; do
216
+ printf '%s:\n' "${t}"
217
+ run_preview "${t}" "${STYLE}" "${CHARSET}"
218
+ printf '\n\n'
219
+ done
220
+ }
221
+
222
+ parse_args() {
223
+ while [[ $# -gt 0 ]]; do
224
+ case "$1" in
225
+ --theme=*)
226
+ THEME="${1#*=}"
227
+ shift
228
+ ;;
229
+ --theme)
230
+ [[ $# -ge 2 ]] || {
231
+ printf 'Missing value for --theme\n' >&2
232
+ exit 1
233
+ }
234
+ THEME="$2"
235
+ shift 2
236
+ ;;
237
+ --style=*)
238
+ STYLE="${1#*=}"
239
+ shift
240
+ ;;
241
+ --style)
242
+ [[ $# -ge 2 ]] || {
243
+ printf 'Missing value for --style\n' >&2
244
+ exit 1
245
+ }
246
+ STYLE="$2"
247
+ shift 2
248
+ ;;
249
+ --charset=*)
250
+ CHARSET="${1#*=}"
251
+ shift
252
+ ;;
253
+ --charset)
254
+ [[ $# -ge 2 ]] || {
255
+ printf 'Missing value for --charset\n' >&2
256
+ exit 1
257
+ }
258
+ CHARSET="$2"
259
+ shift 2
260
+ ;;
261
+ --compare-styles)
262
+ COMPARE_STYLES=true
263
+ shift
264
+ ;;
265
+ --compare-themes)
266
+ COMPARE_THEMES=true
267
+ shift
268
+ ;;
269
+ --compare-tui-layouts)
270
+ COMPARE_TUI_LAYOUTS=true
271
+ shift
272
+ ;;
273
+ *)
274
+ printf 'Unknown option: %s\n' "$1" >&2
275
+ exit 1
276
+ ;;
277
+ esac
278
+ done
279
+ }
280
+
281
+ main() {
282
+ parse_args "$@"
283
+ BIN="$(find_binary)"
284
+
285
+ if [[ "${COMPARE_STYLES}" == "true" ]]; then
286
+ run_compare_styles
287
+ exit 0
288
+ fi
289
+
290
+ if [[ "${COMPARE_THEMES}" == "true" ]]; then
291
+ run_compare_themes
292
+ exit 0
293
+ fi
294
+
295
+ if [[ "${COMPARE_TUI_LAYOUTS}" == "true" ]]; then
296
+ run_compare_tui_layouts
297
+ exit 0
298
+ fi
299
+
300
+ run_preview "${THEME}" "${STYLE}" "${CHARSET}"
301
+ printf '\n'
302
+ exit 0
303
+ }
304
+
305
+ main "$@"