@entelligentsia/forgecli 0.8.4 → 0.9.1

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 (170) hide show
  1. package/CHANGELOG.md +61 -0
  2. package/README.md +165 -2
  3. package/dist/bin/argv.d.ts +2 -2
  4. package/dist/bin/argv.js +17 -0
  5. package/dist/bin/argv.js.map +1 -1
  6. package/dist/bin/config.d.ts +69 -0
  7. package/dist/bin/config.js +315 -0
  8. package/dist/bin/config.js.map +1 -0
  9. package/dist/bin/doctor.d.ts +1 -0
  10. package/dist/bin/doctor.js +12 -0
  11. package/dist/bin/doctor.js.map +1 -1
  12. package/dist/bin/forge.js +7 -0
  13. package/dist/bin/forge.js.map +1 -1
  14. package/dist/extensions/forgecli/config-command.d.ts +8 -0
  15. package/dist/extensions/forgecli/config-command.js +66 -0
  16. package/dist/extensions/forgecli/config-command.js.map +1 -0
  17. package/dist/extensions/forgecli/config-layer.d.ts +38 -0
  18. package/dist/extensions/forgecli/config-layer.js +68 -0
  19. package/dist/extensions/forgecli/config-layer.js.map +1 -0
  20. package/dist/extensions/forgecli/config-tui/component.d.ts +35 -0
  21. package/dist/extensions/forgecli/config-tui/component.js +236 -0
  22. package/dist/extensions/forgecli/config-tui/component.js.map +1 -0
  23. package/dist/extensions/forgecli/config-tui/handler.d.ts +40 -0
  24. package/dist/extensions/forgecli/config-tui/handler.js +240 -0
  25. package/dist/extensions/forgecli/config-tui/handler.js.map +1 -0
  26. package/dist/extensions/forgecli/config-tui/index.d.ts +5 -0
  27. package/dist/extensions/forgecli/config-tui/index.js +5 -0
  28. package/dist/extensions/forgecli/config-tui/index.js.map +1 -0
  29. package/dist/extensions/forgecli/config-tui/keys.d.ts +26 -0
  30. package/dist/extensions/forgecli/config-tui/keys.js +33 -0
  31. package/dist/extensions/forgecli/config-tui/keys.js.map +1 -0
  32. package/dist/extensions/forgecli/config-tui/plugin-config-reader.d.ts +23 -0
  33. package/dist/extensions/forgecli/config-tui/plugin-config-reader.js +58 -0
  34. package/dist/extensions/forgecli/config-tui/plugin-config-reader.js.map +1 -0
  35. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.d.ts +7 -0
  36. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js +83 -0
  37. package/dist/extensions/forgecli/config-tui/screens/advanced-menu.js.map +1 -0
  38. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.d.ts +11 -0
  39. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js +54 -0
  40. package/dist/extensions/forgecli/config-tui/screens/confirm-quit.js.map +1 -0
  41. package/dist/extensions/forgecli/config-tui/screens/override-editor.d.ts +11 -0
  42. package/dist/extensions/forgecli/config-tui/screens/override-editor.js +233 -0
  43. package/dist/extensions/forgecli/config-tui/screens/override-editor.js.map +1 -0
  44. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.d.ts +7 -0
  45. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js +91 -0
  46. package/dist/extensions/forgecli/config-tui/screens/overrides-list-phases.js.map +1 -0
  47. package/dist/extensions/forgecli/config-tui/screens/overrides-list.d.ts +7 -0
  48. package/dist/extensions/forgecli/config-tui/screens/overrides-list.js +71 -0
  49. package/dist/extensions/forgecli/config-tui/screens/overrides-list.js.map +1 -0
  50. package/dist/extensions/forgecli/config-tui/screens/persona-editor.d.ts +10 -0
  51. package/dist/extensions/forgecli/config-tui/screens/persona-editor.js +182 -0
  52. package/dist/extensions/forgecli/config-tui/screens/persona-editor.js.map +1 -0
  53. package/dist/extensions/forgecli/config-tui/screens/persona-picker.d.ts +7 -0
  54. package/dist/extensions/forgecli/config-tui/screens/persona-picker.js +76 -0
  55. package/dist/extensions/forgecli/config-tui/screens/persona-picker.js.map +1 -0
  56. package/dist/extensions/forgecli/config-tui/screens/personas-list.d.ts +7 -0
  57. package/dist/extensions/forgecli/config-tui/screens/personas-list.js +98 -0
  58. package/dist/extensions/forgecli/config-tui/screens/personas-list.js.map +1 -0
  59. package/dist/extensions/forgecli/config-tui/screens/shared.d.ts +29 -0
  60. package/dist/extensions/forgecli/config-tui/screens/shared.js +100 -0
  61. package/dist/extensions/forgecli/config-tui/screens/shared.js.map +1 -0
  62. package/dist/extensions/forgecli/config-tui/screens/show-resolved.d.ts +23 -0
  63. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js +128 -0
  64. package/dist/extensions/forgecli/config-tui/screens/show-resolved.js.map +1 -0
  65. package/dist/extensions/forgecli/config-tui/screens/tier-menu.d.ts +7 -0
  66. package/dist/extensions/forgecli/config-tui/screens/tier-menu.js +135 -0
  67. package/dist/extensions/forgecli/config-tui/screens/tier-menu.js.map +1 -0
  68. package/dist/extensions/forgecli/config-tui/screens/tier-picker.d.ts +9 -0
  69. package/dist/extensions/forgecli/config-tui/screens/tier-picker.js +122 -0
  70. package/dist/extensions/forgecli/config-tui/screens/tier-picker.js.map +1 -0
  71. package/dist/extensions/forgecli/config-tui/screens/types.d.ts +24 -0
  72. package/dist/extensions/forgecli/config-tui/screens/types.js +5 -0
  73. package/dist/extensions/forgecli/config-tui/screens/types.js.map +1 -0
  74. package/dist/extensions/forgecli/config-tui/screens.d.ts +24 -0
  75. package/dist/extensions/forgecli/config-tui/screens.js +78 -0
  76. package/dist/extensions/forgecli/config-tui/screens.js.map +1 -0
  77. package/dist/extensions/forgecli/config-tui/state/buffer.d.ts +11 -0
  78. package/dist/extensions/forgecli/config-tui/state/buffer.js +91 -0
  79. package/dist/extensions/forgecli/config-tui/state/buffer.js.map +1 -0
  80. package/dist/extensions/forgecli/config-tui/state/constants.d.ts +4 -0
  81. package/dist/extensions/forgecli/config-tui/state/constants.js +14 -0
  82. package/dist/extensions/forgecli/config-tui/state/constants.js.map +1 -0
  83. package/dist/extensions/forgecli/config-tui/state/index.d.ts +6 -0
  84. package/dist/extensions/forgecli/config-tui/state/index.js +9 -0
  85. package/dist/extensions/forgecli/config-tui/state/index.js.map +1 -0
  86. package/dist/extensions/forgecli/config-tui/state/init.d.ts +2 -0
  87. package/dist/extensions/forgecli/config-tui/state/init.js +30 -0
  88. package/dist/extensions/forgecli/config-tui/state/init.js.map +1 -0
  89. package/dist/extensions/forgecli/config-tui/state/model.d.ts +192 -0
  90. package/dist/extensions/forgecli/config-tui/state/model.js +4 -0
  91. package/dist/extensions/forgecli/config-tui/state/model.js.map +1 -0
  92. package/dist/extensions/forgecli/config-tui/state/reducer.d.ts +2 -0
  93. package/dist/extensions/forgecli/config-tui/state/reducer.js +212 -0
  94. package/dist/extensions/forgecli/config-tui/state/reducer.js.map +1 -0
  95. package/dist/extensions/forgecli/config-tui/state/selectors.d.ts +91 -0
  96. package/dist/extensions/forgecli/config-tui/state/selectors.js +231 -0
  97. package/dist/extensions/forgecli/config-tui/state/selectors.js.map +1 -0
  98. package/dist/extensions/forgecli/config-tui/state.d.ts +6 -0
  99. package/dist/extensions/forgecli/config-tui/state.js +11 -0
  100. package/dist/extensions/forgecli/config-tui/state.js.map +1 -0
  101. package/dist/extensions/forgecli/config-tui/theme.d.ts +37 -0
  102. package/dist/extensions/forgecli/config-tui/theme.js +88 -0
  103. package/dist/extensions/forgecli/config-tui/theme.js.map +1 -0
  104. package/dist/extensions/forgecli/config-tui/tier-meta.d.ts +28 -0
  105. package/dist/extensions/forgecli/config-tui/tier-meta.js +69 -0
  106. package/dist/extensions/forgecli/config-tui/tier-meta.js.map +1 -0
  107. package/dist/extensions/forgecli/config-writer.d.ts +16 -0
  108. package/dist/extensions/forgecli/config-writer.js +63 -0
  109. package/dist/extensions/forgecli/config-writer.js.map +1 -0
  110. package/dist/extensions/forgecli/fix-bug.js +85 -1
  111. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  112. package/dist/extensions/forgecli/forge-cli-schema.json +54 -0
  113. package/dist/extensions/forgecli/forge-commands.js +3 -8
  114. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  115. package/dist/extensions/forgecli/forge-subagent.d.ts +13 -0
  116. package/dist/extensions/forgecli/forge-subagent.js +19 -0
  117. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  118. package/dist/extensions/forgecli/index.js +16 -0
  119. package/dist/extensions/forgecli/index.js.map +1 -1
  120. package/dist/extensions/forgecli/input-router.d.ts +33 -0
  121. package/dist/extensions/forgecli/input-router.js +133 -0
  122. package/dist/extensions/forgecli/input-router.js.map +1 -0
  123. package/dist/extensions/forgecli/model-resolver.d.ts +32 -0
  124. package/dist/extensions/forgecli/model-resolver.js +65 -0
  125. package/dist/extensions/forgecli/model-resolver.js.map +1 -0
  126. package/dist/extensions/forgecli/model-validator.d.ts +29 -0
  127. package/dist/extensions/forgecli/model-validator.js +107 -0
  128. package/dist/extensions/forgecli/model-validator.js.map +1 -0
  129. package/dist/extensions/forgecli/run-sprint.js +59 -0
  130. package/dist/extensions/forgecli/run-sprint.js.map +1 -1
  131. package/dist/extensions/forgecli/run-task.js +93 -1
  132. package/dist/extensions/forgecli/run-task.js.map +1 -1
  133. package/dist/extensions/forgecli/thread-switcher.js +5 -2
  134. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  135. package/dist/extensions/forgecli/whats-new-widget.js +5 -2
  136. package/dist/extensions/forgecli/whats-new-widget.js.map +1 -1
  137. package/package.json +11 -3
  138. package/dist/extensions/forgecli/review-command.d.ts +0 -2
  139. package/dist/extensions/forgecli/review-command.js +0 -184
  140. package/dist/extensions/forgecli/review-command.js.map +0 -1
  141. package/dist/forge-payload/.tools/banners.cjs +0 -435
  142. package/dist/forge-payload/.tools/build-context-pack.cjs +0 -290
  143. package/dist/forge-payload/.tools/build-init-context.cjs +0 -322
  144. package/dist/forge-payload/.tools/build-overlay.cjs +0 -326
  145. package/dist/forge-payload/.tools/build-persona-pack.cjs +0 -226
  146. package/dist/forge-payload/.tools/collate.cjs +0 -1041
  147. package/dist/forge-payload/.tools/generation-manifest.cjs +0 -311
  148. package/dist/forge-payload/.tools/lib/forge-root.cjs +0 -59
  149. package/dist/forge-payload/.tools/lib/paths.cjs +0 -29
  150. package/dist/forge-payload/.tools/lib/pricing.cjs +0 -165
  151. package/dist/forge-payload/.tools/lib/project-root.cjs +0 -32
  152. package/dist/forge-payload/.tools/lib/result.js +0 -40
  153. package/dist/forge-payload/.tools/lib/store-facade.cjs +0 -162
  154. package/dist/forge-payload/.tools/lib/store-nlp.cjs +0 -250
  155. package/dist/forge-payload/.tools/lib/store-query-exec.cjs +0 -272
  156. package/dist/forge-payload/.tools/lib/validate.js +0 -141
  157. package/dist/forge-payload/.tools/manage-config.cjs +0 -340
  158. package/dist/forge-payload/.tools/manage-versions.cjs +0 -365
  159. package/dist/forge-payload/.tools/package.json +0 -3
  160. package/dist/forge-payload/.tools/parse-gates.cjs +0 -151
  161. package/dist/forge-payload/.tools/parse-verdict.cjs +0 -67
  162. package/dist/forge-payload/.tools/preflight-gate.cjs +0 -350
  163. package/dist/forge-payload/.tools/prompts/sprint-plan-prompt.md +0 -70
  164. package/dist/forge-payload/.tools/schemas/task-list.schema.json +0 -53
  165. package/dist/forge-payload/.tools/seed-store.cjs +0 -237
  166. package/dist/forge-payload/.tools/store-cli.cjs +0 -1226
  167. package/dist/forge-payload/.tools/store-query.cjs +0 -319
  168. package/dist/forge-payload/.tools/store.cjs +0 -315
  169. package/dist/forge-payload/.tools/substitute-placeholders.cjs +0 -625
  170. package/dist/forge-payload/.tools/validate-store.cjs +0 -593
@@ -1,435 +0,0 @@
1
- 'use strict';
2
-
3
- // Forge Banner Library
4
- // Reusable agent identity display for tools, hooks, and skills.
5
- //
6
- // Render modes:
7
- // render(name) — full ASCII art block + emoji + name + tagline
8
- // badge(name) — single line: emoji + bold name + dim tagline
9
- // mark(name) — emoji only (for use alongside 〇△× status marks)
10
- // progressBar(n,total,opt) — unicode block bar with optional gradient + label
11
- // subtitle(text, opts) — dim italic line for under-banner taglines
12
- // phaseHeader(...) — convenience: badge + em-dash banner + progress bar
13
- //
14
- // Plain mode: ANSI escapes are stripped when any of these is true:
15
- // - env FORGE_BANNERS_PLAIN is set to a non-empty value
16
- // - env NO_COLOR is set to a non-empty value
17
- // - process.stdout.isTTY === false
18
- // - CLI flag --plain is passed
19
- //
20
- // Usage (module):
21
- // const banners = require('./banners.cjs');
22
- // console.log(banners.render('forge'));
23
- // console.log(banners.progressBar(5, 12, { label: 'Templates', color: [255,208,122] }));
24
- //
25
- // Usage (CLI):
26
- // node banners.cjs forge
27
- // node banners.cjs --badge north
28
- // node banners.cjs --mark tide
29
- // node banners.cjs --gallery
30
- // node banners.cjs --list
31
- // node banners.cjs --plain forge
32
- // node banners.cjs --subtitle "Forging your SDLC"
33
- // node banners.cjs --progress 5 12 "Templates"
34
- // node banners.cjs --phase 7 12 "Workflows" ember fast
35
-
36
- // ─── Plain-mode detection ─────────────────────────────────────────────────────
37
- // Resolved at call-time so tests can flip env vars dynamically.
38
- function isPlain() {
39
- if (process.env.FORGE_BANNERS_PLAIN) return true;
40
- if (process.env.NO_COLOR) return true;
41
- if (process.stdout && process.stdout.isTTY === false) return true;
42
- return false;
43
- }
44
-
45
- // Strip ANSI escape sequences (CSI). Conservative — matches \x1b[...m and friends.
46
- const ANSI_RE = /\x1b\[[0-9;]*[A-Za-z]/g;
47
- function stripAnsi(s) {
48
- return String(s).replace(ANSI_RE, '');
49
- }
50
-
51
- // Soft override — set by `--plain` CLI flag to force plain mode for one run.
52
- let FORCE_PLAIN = false;
53
-
54
- // ─── ANSI helpers ─────────────────────────────────────────────────────────────
55
- const R = '\x1b[0m';
56
- const B = '\x1b[1m';
57
- const D = '\x1b[2m';
58
- const I = '\x1b[3m';
59
- const f = (r, g, b) => `\x1b[38;2;${r};${g};${b}m`;
60
-
61
- // ─── Mode tints ───────────────────────────────────────────────────────────────
62
- // Mode-aware accent colour for progress bars and subtitles. Used by phaseHeader.
63
- const MODE_TINTS = {
64
- fast: [255, 208, 122], // lantern yellow
65
- full: [255, 138, 60], // ember orange
66
- };
67
-
68
- // ─── Zen blue ─────────────────────────────────────────────────────────────────
69
- // System-wide horizontal-rule tint. Applied to em-dash separators in
70
- // phaseHeader, ruleLine, and ensure-ready.cjs --announce framing. Picked to
71
- // read calm and cool without competing with banner colours.
72
- const ZEN_BLUE = [100, 140, 200];
73
-
74
- // ─── Banner registry ───────────────────────────────────────────────────────────
75
- const BANNERS = {
76
-
77
- ember: {
78
- emoji: '🔥',
79
- tagline: 'heat · ignition · drive',
80
- name: 'EMBER',
81
- kanji: '炎',
82
- color: [255, 170, 60],
83
- art: ` ${f(255,240,100)}) ${f(255,200,50)}) ${f(255,140,20)}(${f(230,80,10)}🔥${f(255,140,20)}) ${f(230,80,10)}~∿~ ${f(170,30,5)}≋≋≋`,
84
- },
85
-
86
- tide: {
87
- emoji: '🌊',
88
- tagline: 'rhythm · pull · depth',
89
- name: 'TIDE',
90
- kanji: '潮',
91
- color: [110, 200, 255],
92
- art: ` ${f(210,240,255)}∿ ${f(130,200,245)}≋≋≋ ${f(60,140,210)}≋≋≋ ${f(25,85,175)}▓▓▓ ${f(10,45,130)}▓▓▓`,
93
- },
94
-
95
- oracle: {
96
- emoji: '🌕',
97
- tagline: 'sight · pattern · knowing',
98
- name: 'ORACLE',
99
- kanji: '神託',
100
- color: [210, 160, 255],
101
- art: ` ${f(160,80,240)}· ◌ ${f(190,110,255)}◎ ${f(255,220,100)}◉ ${f(190,110,255)}◎ ${f(160,80,240)}◌ ·`,
102
- },
103
-
104
- rift: {
105
- emoji: '⚡',
106
- tagline: 'edge · fracture · crossing',
107
- name: 'RIFT',
108
- kanji: '裂',
109
- color: [100, 255, 240],
110
- art: ` ${f(0,240,230)}▓▒░ ${f(180,0,220)}╲ ${f(0,255,240)}⚡${f(180,0,220)} ╱ ${f(0,240,230)}░▒▓`,
111
- },
112
-
113
- bloom: {
114
- emoji: '🌸',
115
- tagline: 'growth · opening · becoming',
116
- name: 'BLOOM',
117
- kanji: '開花',
118
- color: [255, 160, 190],
119
- art: ` ${f(255,160,200)}✿ ✿ ${f(255,120,170)}✾ ${f(255,220,100)}✽ ${f(255,120,170)}✾ ${f(255,160,200)}✿ ✿`,
120
- },
121
-
122
- north: {
123
- emoji: '🧭',
124
- tagline: 'direction · clarity · cold',
125
- name: 'NORTH',
126
- kanji: '北',
127
- color: [190, 225, 255],
128
- art: ` ${f(200,230,255)}✦ ${f(150,195,240)}╱ ${f(100,160,230)}◈ ${f(150,195,240)}╲ ${f(200,230,255)}✦`,
129
- },
130
-
131
- lumen: {
132
- emoji: '✨',
133
- tagline: 'light · warmth · clarity',
134
- name: 'LUMEN',
135
- kanji: '光',
136
- color: [255, 245, 150],
137
- art: ` ${f(255,255,200)}✧ ${f(255,245,160)}── ${f(255,255,255)}◉ ${f(255,245,160)}── ${f(255,255,200)}✧`,
138
- },
139
-
140
- forge: {
141
- emoji: '🔨',
142
- tagline: 'making · heat · craft',
143
- name: 'FORGE',
144
- kanji: '鍛冶',
145
- color: [255, 160, 40],
146
- art: ` ${f(255,230,80)}✦ ${f(200,80,20)}▄${f(230,100,30)}▓▓▓▓▓${f(200,80,20)}▄ ${f(255,160,40)}≋ ≋ ≋`,
147
- },
148
-
149
- drift: {
150
- emoji: '🍃',
151
- tagline: 'ease · letting go · flow',
152
- name: 'DRIFT',
153
- kanji: '漂流',
154
- color: [150, 200, 165],
155
- art: ` ${f(160,200,170)}· ${f(140,180,155)}· ${f(120,165,140)}· ${f(100,150,125)}· ${f(80,135,110)}·`,
156
- },
157
-
158
- void: {
159
- emoji: '🌑',
160
- tagline: 'depth · silence · potential',
161
- name: 'VOID',
162
- kanji: '虚空',
163
- color: [130, 100, 200],
164
- art: ` ${f(30,20,60)} · ${f(70,50,120)}◌ ${f(50,35,90)}· `,
165
- },
166
-
167
- entelligentsia: {
168
- emoji: '🔗',
169
- tagline: 'linked · intellect · becoming',
170
- name: 'ENTELLIGENTSIA',
171
- kanji: '知性',
172
- color: [140, 200, 60],
173
- art: ` ${f(110,110,115)}╭──╯ ${f(140,200,60)}─╮ ╭─ ${f(110,110,115)}╰──╮`,
174
- },
175
-
176
- };
177
-
178
- // ─── Render helpers ────────────────────────────────────────────────────────────
179
-
180
- /** Dim dot rule — use between banners in a gallery */
181
- function rule() {
182
- return ` ${f(45,45,65)}${'·'.repeat(32)}${R}`;
183
- }
184
-
185
- /**
186
- * Full banner: ASCII art block + emoji + name + tagline.
187
- * @param {string} name Banner key (case-insensitive).
188
- * @returns {string}
189
- */
190
- function render(name) {
191
- const b = _get(name);
192
- const [r, g, bl] = b.color;
193
- const kanji = b.kanji ? ` ${D}${f(r,g,bl)}${b.kanji}${R}` : '';
194
- const label = ` ${b.emoji} ${B}${f(r,g,bl)}${b.name}${R}${kanji} ${D}${f(r,g,bl)}${b.tagline}${R}`;
195
- const out = '\n' + b.art + R + '\n' + label + '\n';
196
- return _maybePlain(out);
197
- }
198
-
199
- /**
200
- * Badge: single line — emoji + bold name + dim tagline.
201
- * Fits inline in status output or skill headers.
202
- * @param {string} name
203
- * @returns {string}
204
- */
205
- function badge(name) {
206
- const b = _get(name);
207
- const [r, g, bl] = b.color;
208
- const kanji = b.kanji ? ` ${D}${f(r,g,bl)}${b.kanji}${R}` : '';
209
- return _maybePlain(`${b.emoji} ${B}${f(r,g,bl)}${b.name}${R}${kanji} ${D}${f(r,g,bl)}${b.tagline}${R}`);
210
- }
211
-
212
- /**
213
- * Mark: emoji only.
214
- * Use alongside 〇△× status marks for agent attribution.
215
- * @param {string} name
216
- * @returns {string}
217
- */
218
- function mark(name) {
219
- return _get(name).emoji;
220
- }
221
-
222
- /**
223
- * Progress bar: unicode block bar with optional gradient tint and label.
224
- * ▰▰▰▰▰▱▱▱▱▱▱▱ Phase 5/12 · Templates
225
- *
226
- * @param {number} n Filled cells (clamped to [0, total])
227
- * @param {number} total Total cells across the bar's logical range
228
- * @param {object} [opts]
229
- * @param {number} [opts.width=12] Cells in the bar (independent of n/total)
230
- * @param {number[]} [opts.color] [r,g,b] tint for filled segment
231
- * @param {string} [opts.label] Trailing label after the bar
232
- * @param {string} [opts.fillGlyph='▰'] Override filled cell glyph
233
- * @param {string} [opts.emptyGlyph='▱'] Override empty cell glyph
234
- * @returns {string}
235
- */
236
- function progressBar(n, total, opts) {
237
- const o = opts || {};
238
- const width = Math.max(1, o.width || 12);
239
- const fillGlyph = o.fillGlyph || '▰';
240
- const emptyGlyph = o.emptyGlyph || '▱';
241
- const safeTotal = Math.max(1, total || 1);
242
- const safeN = Math.max(0, Math.min(n || 0, safeTotal));
243
- const filledCells = Math.round((safeN / safeTotal) * width);
244
- const emptyCells = width - filledCells;
245
-
246
- let bar = '';
247
- if (filledCells > 0) {
248
- const tint = Array.isArray(o.color) ? f(o.color[0], o.color[1], o.color[2]) : '';
249
- const reset = tint ? R : '';
250
- bar += `${tint}${fillGlyph.repeat(filledCells)}${reset}`;
251
- }
252
- if (emptyCells > 0) {
253
- bar += `${D}${emptyGlyph.repeat(emptyCells)}${R}`;
254
- }
255
-
256
- const counter = ` ${safeN}/${safeTotal}`;
257
- const label = o.label ? ` ${D}·${R} ${o.label}` : '';
258
- return _maybePlain(bar + counter + label);
259
- }
260
-
261
- /**
262
- * Subtitle: dim italic line under a banner. Single line.
263
- *
264
- * @param {string} text
265
- * @param {object} [opts]
266
- * @param {number[]} [opts.color] Optional tint
267
- * @returns {string}
268
- */
269
- function subtitle(text, opts) {
270
- const o = opts || {};
271
- const tint = Array.isArray(o.color) ? f(o.color[0], o.color[1], o.color[2]) : '';
272
- return _maybePlain(` ${D}${I}${tint}${text}${R}`);
273
- }
274
-
275
- /**
276
- * Em-dash rule line, tinted zen blue by default.
277
- * ━━━ {text} ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
278
- *
279
- * Use as a horizontal section separator anywhere in the system. Calling
280
- * with no text produces a plain rule (just em-dashes, no label).
281
- *
282
- * @param {string} [text] Optional label to embed in the rule
283
- * @param {object} [opts]
284
- * @param {number} [opts.width=65] Total line width
285
- * @param {number[]} [opts.color] [r,g,b] override tint; defaults to ZEN_BLUE
286
- * @returns {string}
287
- */
288
- function ruleLine(text, opts) {
289
- const o = opts || {};
290
- const width = o.width || 65;
291
- const color = Array.isArray(o.color) ? o.color : ZEN_BLUE;
292
- const tint = f(color[0], color[1], color[2]);
293
-
294
- let line;
295
- if (text && text.length > 0) {
296
- const head = `━━━ ${text} `;
297
- const padCount = Math.max(3, width - head.length);
298
- line = head + '━'.repeat(padCount);
299
- } else {
300
- line = '━'.repeat(width);
301
- }
302
- return _maybePlain(`${tint}${line}${R}`);
303
- }
304
-
305
- /**
306
- * Phase header: multi-line — badge, em-dash banner, progress bar.
307
- * Concatenates with newlines so a single println prints all three.
308
- *
309
- * @param {number} n Phase number (1-based)
310
- * @param {number} total Total phases
311
- * @param {string} name Phase display name
312
- * @param {string} bannerKey Banner registry key (e.g. 'north', 'forge')
313
- * @param {object|string} [opts]
314
- * Pass a string to use as the mode tint key ('fast' | 'full'),
315
- * or an opts object with { mode, color, width }.
316
- * @returns {string}
317
- */
318
- function phaseHeader(n, total, name, bannerKey, opts) {
319
- let o = {};
320
- if (typeof opts === 'string') {
321
- o = { mode: opts };
322
- } else if (opts) {
323
- o = opts;
324
- }
325
- const tint = o.color || (o.mode && MODE_TINTS[o.mode.toLowerCase()]) || null;
326
-
327
- const bar = progressBar(n, total, {
328
- width: o.width || 12,
329
- color: tint,
330
- label: name,
331
- });
332
-
333
- // Em-dash banner — match the existing format used across all forge commands,
334
- // tinted zen blue for system-wide visual consistency on horizontal rules.
335
- const emBanner = ruleLine(`Phase ${n}/${total} — ${name}`);
336
-
337
- const lines = [
338
- badge(bannerKey),
339
- emBanner,
340
- bar,
341
- ];
342
- return lines.join('\n');
343
- }
344
-
345
- /**
346
- * All available banner names.
347
- * @returns {string[]}
348
- */
349
- function list() {
350
- return Object.keys(BANNERS);
351
- }
352
-
353
- /**
354
- * Full gallery of all banners separated by rules.
355
- * @returns {string}
356
- */
357
- function gallery() {
358
- return list().map((name, i) => {
359
- const sep = i > 0 ? '\n' + rule() + '\n' : '';
360
- return sep + render(name);
361
- }).join('');
362
- }
363
-
364
- // ─── Internal ──────────────────────────────────────────────────────────────────
365
-
366
- function _get(name) {
367
- const b = BANNERS[name.toLowerCase()];
368
- if (!b) {
369
- throw new Error(`Unknown banner "${name}". Available: ${list().join(', ')}`);
370
- }
371
- return b;
372
- }
373
-
374
- function _maybePlain(s) {
375
- return (FORCE_PLAIN || isPlain()) ? stripAnsi(s) : s;
376
- }
377
-
378
- // ─── CLI ───────────────────────────────────────────────────────────────────────
379
- // node banners.cjs [<name>] [--gallery] [--badge <name>] [--mark <name>] [--list]
380
-
381
- if (require.main === module) {
382
- let args = process.argv.slice(2);
383
-
384
- // --plain may appear anywhere; consume it first.
385
- const plainIdx = args.indexOf('--plain');
386
- if (plainIdx !== -1) {
387
- FORCE_PLAIN = true;
388
- args = args.slice(0, plainIdx).concat(args.slice(plainIdx + 1));
389
- }
390
-
391
- try {
392
- if (!args.length || args[0] === '--gallery') {
393
- process.stdout.write(gallery() + '\n');
394
- } else if (args[0] === '--list') {
395
- console.log(list().map(n => {
396
- const b = BANNERS[n];
397
- return `${b.emoji} ${n.padEnd(8)} — ${b.tagline}`;
398
- }).join('\n'));
399
- } else if (args[0] === '--badge') {
400
- console.log(badge(args[1] || ''));
401
- } else if (args[0] === '--mark') {
402
- console.log(mark(args[1] || ''));
403
- } else if (args[0] === '--subtitle') {
404
- console.log(subtitle(args.slice(1).join(' ')));
405
- } else if (args[0] === '--progress') {
406
- const n = Number(args[1]);
407
- const total = Number(args[2]);
408
- const label = args.slice(3).join(' ') || undefined;
409
- console.log(progressBar(n, total, { label }));
410
- } else if (args[0] === '--phase') {
411
- const n = Number(args[1]);
412
- const total = Number(args[2]);
413
- const name = args[3] || '';
414
- const bannerKey = args[4] || 'forge';
415
- const mode = args[5]; // optional 'fast' | 'full'
416
- console.log(phaseHeader(n, total, name, bannerKey, mode ? { mode } : undefined));
417
- } else if (args[0] === '--rule') {
418
- // --rule [text] Zen-blue em-dash horizontal rule (with optional label)
419
- const text = args.slice(1).join(' ') || undefined;
420
- console.log(ruleLine(text));
421
- } else {
422
- process.stdout.write(render(args[0]) + '\n');
423
- }
424
- } catch (e) {
425
- console.error(e.message);
426
- process.exit(1);
427
- }
428
- }
429
-
430
- // ─── Exports ───────────────────────────────────────────────────────────────────
431
- module.exports = {
432
- render, badge, mark, list, gallery, rule, BANNERS,
433
- progressBar, subtitle, phaseHeader, ruleLine,
434
- isPlain, stripAnsi, MODE_TINTS, ZEN_BLUE,
435
- };