@flyingrobots/bijou-mcp 7.0.0 → 7.2.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 (94) hide show
  1. package/dist/context.d.ts.map +1 -1
  2. package/dist/context.js.map +1 -1
  3. package/dist/output.d.ts.map +1 -1
  4. package/dist/output.js +5 -4
  5. package/dist/output.js.map +1 -1
  6. package/dist/server.js +11 -1
  7. package/dist/server.js.map +1 -1
  8. package/dist/tools/accordion.js +1 -1
  9. package/dist/tools/accordion.js.map +1 -1
  10. package/dist/tools/alert.js +1 -1
  11. package/dist/tools/alert.js.map +1 -1
  12. package/dist/tools/badge.js +1 -1
  13. package/dist/tools/badge.js.map +1 -1
  14. package/dist/tools/box.js +2 -2
  15. package/dist/tools/box.js.map +1 -1
  16. package/dist/tools/breadcrumb.js +1 -1
  17. package/dist/tools/breadcrumb.js.map +1 -1
  18. package/dist/tools/constrain.js +1 -1
  19. package/dist/tools/constrain.js.map +1 -1
  20. package/dist/tools/dag.js +1 -1
  21. package/dist/tools/dag.js.map +1 -1
  22. package/dist/tools/docs-catalog/authoring.d.ts +3 -0
  23. package/dist/tools/docs-catalog/authoring.d.ts.map +1 -0
  24. package/dist/tools/docs-catalog/authoring.js +236 -0
  25. package/dist/tools/docs-catalog/authoring.js.map +1 -0
  26. package/dist/tools/docs-catalog/index.d.ts +4 -0
  27. package/dist/tools/docs-catalog/index.d.ts.map +1 -0
  28. package/dist/tools/docs-catalog/index.js +12 -0
  29. package/dist/tools/docs-catalog/index.js.map +1 -0
  30. package/dist/tools/docs-catalog/motion.d.ts +3 -0
  31. package/dist/tools/docs-catalog/motion.d.ts.map +1 -0
  32. package/dist/tools/docs-catalog/motion.js +174 -0
  33. package/dist/tools/docs-catalog/motion.js.map +1 -0
  34. package/dist/tools/docs-catalog/structure.d.ts +3 -0
  35. package/dist/tools/docs-catalog/structure.d.ts.map +1 -0
  36. package/dist/tools/docs-catalog/structure.js +232 -0
  37. package/dist/tools/docs-catalog/structure.js.map +1 -0
  38. package/dist/tools/docs-catalog/types.d.ts +21 -0
  39. package/dist/tools/docs-catalog/types.d.ts.map +1 -0
  40. package/dist/tools/docs-catalog/types.js +13 -0
  41. package/dist/tools/docs-catalog/types.js.map +1 -0
  42. package/dist/tools/docs-catalog/workflow.d.ts +3 -0
  43. package/dist/tools/docs-catalog/workflow.d.ts.map +1 -0
  44. package/dist/tools/docs-catalog/workflow.js +236 -0
  45. package/dist/tools/docs-catalog/workflow.js.map +1 -0
  46. package/dist/tools/docs-values.d.ts +4 -0
  47. package/dist/tools/docs-values.d.ts.map +1 -0
  48. package/dist/tools/docs-values.js +16 -0
  49. package/dist/tools/docs-values.js.map +1 -0
  50. package/dist/tools/docs.d.ts +1 -20
  51. package/dist/tools/docs.d.ts.map +1 -1
  52. package/dist/tools/docs.js +101 -916
  53. package/dist/tools/docs.js.map +1 -1
  54. package/dist/tools/docs.test-support.d.ts +2 -0
  55. package/dist/tools/docs.test-support.d.ts.map +1 -0
  56. package/dist/tools/docs.test-support.js +48 -0
  57. package/dist/tools/docs.test-support.js.map +1 -0
  58. package/dist/tools/docs.test.js +49 -69
  59. package/dist/tools/docs.test.js.map +1 -1
  60. package/dist/tools/enumerated-list.js +1 -1
  61. package/dist/tools/enumerated-list.js.map +1 -1
  62. package/dist/tools/explainability.js +1 -1
  63. package/dist/tools/explainability.js.map +1 -1
  64. package/dist/tools/hyperlink.js +1 -1
  65. package/dist/tools/hyperlink.js.map +1 -1
  66. package/dist/tools/inspector.js +1 -1
  67. package/dist/tools/inspector.js.map +1 -1
  68. package/dist/tools/kbd.js +1 -1
  69. package/dist/tools/kbd.js.map +1 -1
  70. package/dist/tools/log.js +1 -1
  71. package/dist/tools/log.js.map +1 -1
  72. package/dist/tools/output.test.js +21 -4
  73. package/dist/tools/output.test.js.map +1 -1
  74. package/dist/tools/paginator.js +1 -1
  75. package/dist/tools/paginator.js.map +1 -1
  76. package/dist/tools/progress.js +1 -1
  77. package/dist/tools/progress.js.map +1 -1
  78. package/dist/tools/separator.js +1 -1
  79. package/dist/tools/separator.js.map +1 -1
  80. package/dist/tools/skeleton.js +1 -1
  81. package/dist/tools/skeleton.js.map +1 -1
  82. package/dist/tools/stepper.js +1 -1
  83. package/dist/tools/stepper.js.map +1 -1
  84. package/dist/tools/table.js +1 -1
  85. package/dist/tools/table.js.map +1 -1
  86. package/dist/tools/tabs.js +1 -1
  87. package/dist/tools/tabs.js.map +1 -1
  88. package/dist/tools/timeline.js +1 -1
  89. package/dist/tools/timeline.js.map +1 -1
  90. package/dist/tools/tree.js +1 -1
  91. package/dist/tools/tree.js.map +1 -1
  92. package/dist/types.d.ts +2 -1
  93. package/dist/types.d.ts.map +1 -1
  94. package/package.json +2 -2
@@ -3,937 +3,60 @@ import { brailleChartSurface, guidedFlow, markdown, perfOverlaySurface, preferen
3
3
  import { plainStyle } from '@flyingrobots/bijou/adapters/test';
4
4
  import { mcpContext } from '../context.js';
5
5
  import { buildStructuredToolResult, structuredToolOutputShape, withOutputMode, } from '../output.js';
6
- const DEFAULT_INTERACTION_PROFILES = {
7
- interactive: 'Rendered through a plain-style interactive context with Unicode structure and no ANSI color.',
8
- static: 'Matches the interactive MCP output because the wrapper returns a plain-text rendering rather than a live terminal state.',
9
- pipe: 'Not separately lowered by the MCP wrapper; the returned result is already plain text suitable for logs, prompts, and transcripts.',
10
- accessible: 'No dedicated accessible lowering is exposed by this MCP wrapper yet, so callers should treat the plain-text structure as the accessible fallback.',
11
- };
12
- const DEFAULT_DOCS_ONLY_INTERACTION_PROFILES = {
13
- interactive: 'No dedicated MCP render tool is exposed today. bijou_docs documents this first-party family directly and can synthesize a representative plain-text example when examples are requested.',
14
- static: 'Matches the docs-only interactive sample because bijou_docs returns a plain-text reference rendering rather than a live terminal session.',
15
- pipe: 'The synthesized example output is already plain text suitable for logs, prompts, and transcripts.',
16
- accessible: 'No dedicated accessible lowering is exposed for this docs-only entry yet, so callers should treat the plain-text sample and guidance as the accessible fallback.',
17
- };
18
- export const MCP_DOCS_CATALOG = [
19
- {
20
- toolName: 'bijou_table',
21
- family: 'table()',
22
- category: 'Data and Structure',
23
- summary: 'Rectangular data grid with headers and box-drawing borders.',
24
- aliases: ['table', 'grid', 'rows', 'columns'],
25
- useWhen: [
26
- 'Your data is naturally row-and-column shaped.',
27
- 'People need to compare values across consistent fields.',
28
- ],
29
- avoidWhen: [
30
- 'The structure is hierarchical rather than tabular.',
31
- 'You need narrative prose more than aligned comparison.',
32
- ],
33
- related: ['dag()', 'tree()', 'enumeratedList()'],
34
- exampleArgs: {
35
- columns: [{ header: 'Service' }, { header: 'Status' }],
36
- rows: [['api', 'healthy'], ['worker', 'healthy']],
37
- width: 40,
38
- },
39
- },
40
- {
41
- toolName: 'bijou_tree',
42
- family: 'tree()',
43
- category: 'Data and Structure',
44
- summary: 'Nested hierarchy with Unicode connectors.',
45
- aliases: ['tree', 'hierarchy', 'outline', 'nested'],
46
- useWhen: [
47
- 'The data has parent-child nesting.',
48
- 'Expansion order matters more than cross-node comparison.',
49
- ],
50
- avoidWhen: [
51
- 'You need many-to-many relationships or cross-links.',
52
- 'The data is better scanned as rows and columns.',
53
- ],
54
- related: ['dag()', 'enumeratedList()', 'table()'],
55
- exampleArgs: {
56
- nodes: [
57
- {
58
- label: 'docs',
59
- children: [{ label: 'design-system' }, { label: 'release' }],
60
- },
61
- ],
62
- },
63
- },
64
- {
65
- toolName: 'bijou_dag',
66
- family: 'dag()',
67
- category: 'Data and Structure',
68
- summary: 'Directed graph with boxed nodes and routed edges.',
69
- aliases: ['dag', 'graph', 'dependency graph', 'flow graph'],
70
- useWhen: [
71
- 'Relationships are graph-shaped rather than purely hierarchical.',
72
- 'You need to show dependencies, branches, or converging flows.',
73
- ],
74
- avoidWhen: [
75
- 'A linear timeline or step list would explain the flow more clearly.',
76
- 'The structure is simple enough for a tree or breadcrumb.',
77
- ],
78
- related: ['tree()', 'timeline()', 'table()'],
79
- exampleArgs: {
80
- nodes: [
81
- { id: 'build', label: 'Build', edges: ['test'] },
82
- { id: 'test', label: 'Test', edges: ['deploy'] },
83
- { id: 'deploy', label: 'Deploy' },
84
- ],
85
- maxWidth: 60,
86
- },
87
- },
88
- {
89
- toolName: 'bijou_enumerated_list',
90
- family: 'enumeratedList()',
91
- category: 'Data and Structure',
92
- summary: 'List renderer for bullets, ordered steps, letters, or roman numerals.',
93
- aliases: ['enumerated list', 'list', 'bullet list', 'ordered list'],
94
- useWhen: [
95
- 'You need a lightweight ordered or unordered list.',
96
- 'Sequence matters but rich container chrome would be overkill.',
97
- ],
98
- avoidWhen: [
99
- 'Items need per-row metadata or aligned fields.',
100
- 'The structure is better represented as tabs, steps, or a tree.',
101
- ],
102
- related: ['stepper()', 'timeline()', 'table()'],
103
- exampleArgs: {
104
- items: ['Build', 'Test', 'Deploy'],
105
- style: 'arabic',
106
- },
107
- },
108
- {
109
- toolName: 'bijou_box',
110
- family: 'box()',
111
- category: 'Containers and Layout',
112
- summary: 'Generic bordered container for prose, status, or grouped content.',
113
- aliases: ['box', 'panel', 'container'],
114
- useWhen: [
115
- 'Content needs a visible boundary or title.',
116
- 'You want a neutral container that does not imply workflow state.',
117
- ],
118
- avoidWhen: [
119
- 'The content wants a more opinionated component such as alert or inspector.',
120
- 'The output is simple enough to stay as plain text.',
121
- ],
122
- related: ['headerBox()', 'alert()', 'inspector()'],
123
- exampleArgs: {
124
- content: 'Release ready for canary.',
125
- title: 'status',
126
- },
127
- },
128
- {
129
- toolName: 'bijou_header_box',
130
- family: 'headerBox()',
131
- category: 'Containers and Layout',
132
- summary: 'Compact labeled container with a heading and optional detail line.',
133
- aliases: ['header box', 'summary panel', 'heading panel'],
134
- useWhen: [
135
- 'A compact callout needs a strong label up front.',
136
- 'You want a container with more voice than a generic box.',
137
- ],
138
- avoidWhen: [
139
- 'You need multi-section detail instead of a headline summary.',
140
- 'The content is only one line and does not need chrome.',
141
- ],
142
- related: ['box()', 'inspector()', 'alert()'],
143
- exampleArgs: {
144
- label: 'Release',
145
- detail: 'stable',
146
- },
147
- },
148
- {
149
- toolName: 'bijou_separator',
150
- family: 'separator()',
151
- category: 'Containers and Layout',
152
- summary: 'Horizontal rule with optional centered label.',
153
- aliases: ['separator', 'divider', 'rule'],
154
- useWhen: [
155
- 'You need a clear break between sections.',
156
- 'A short label should orient the next block of content.',
157
- ],
158
- avoidWhen: [
159
- 'You need an actual container rather than a visual divider.',
160
- 'The surrounding layout already makes section boundaries obvious.',
161
- ],
162
- related: ['box()', 'headerBox()', 'tabs()'],
163
- exampleArgs: {
164
- label: 'release queue',
165
- width: 32,
166
- },
167
- },
168
- {
169
- toolName: 'bijou_constrain',
170
- family: 'constrain()',
171
- category: 'Containers and Layout',
172
- summary: 'Text truncation helper for bounded width and height.',
173
- aliases: ['constrain', 'truncate', 'clamp', 'ellipsis'],
174
- useWhen: [
175
- 'Free-form text must fit a strict width or height.',
176
- 'A preview should stay honest without rewriting the source text.',
177
- ],
178
- avoidWhen: [
179
- 'You actually need wrapping instead of truncation.',
180
- 'The source content is important enough to merit a scrollable container.',
181
- ],
182
- related: ['box()', 'markdown()', 'table()'],
183
- exampleArgs: {
184
- content: 'This is a long release note preview that should be clipped before it overruns the surrounding layout.',
185
- maxWidth: 26,
186
- },
187
- },
188
- {
189
- toolName: 'bijou_alert',
190
- family: 'alert()',
191
- category: 'Feedback and Status',
192
- summary: 'Severity callout with icon and bordered container.',
193
- aliases: ['alert', 'warning', 'error', 'success', 'info'],
194
- useWhen: [
195
- 'A message needs strong severity signaling.',
196
- 'The reader should treat the content as a callout rather than ambient copy.',
197
- ],
198
- avoidWhen: [
199
- 'You need transient notification behavior rather than a static panel.',
200
- 'The state is low-stakes enough for a badge or note.',
201
- ],
202
- related: ['badge()', 'log()', 'explainability()'],
203
- exampleArgs: {
204
- message: 'Canary error budget is almost exhausted.',
205
- variant: 'warning',
206
- },
207
- },
208
- {
209
- toolName: 'bijou_note',
210
- family: 'note()',
211
- category: 'Feedback and Status',
212
- summary: 'Calm explanatory note for form flows and inline guidance without alert-level urgency.',
213
- aliases: ['note', 'helper text', 'supporting note', 'inline guidance'],
214
- useWhen: [
215
- 'You need supportive explanatory text that should not compete with primary status messaging.',
216
- 'A form, guided flow, or inspector needs clarifying context without turning into an alert.',
217
- ],
218
- avoidWhen: [
219
- 'The message carries urgency, severity, or a required next action.',
220
- 'The content is long-form prose that should live in markdown() or guidedFlow().',
221
- ],
222
- related: ['alert()', 'markdown()', 'group() / wizard()'],
223
- exampleArgs: {
224
- title: 'Deploy window',
225
- message: 'Rotate credentials after the canary completes.',
226
- },
227
- },
228
- {
229
- toolName: 'bijou_progress_bar',
230
- family: 'progressBar()',
231
- category: 'Feedback and Status',
232
- summary: 'Static completion bar with optional percent label.',
233
- aliases: ['progress', 'progress bar', 'percent', 'completion'],
234
- useWhen: [
235
- 'A single bounded percentage is the important state.',
236
- 'You need a compact progress signal inside another surface.',
237
- ],
238
- avoidWhen: [
239
- 'The workflow has named steps rather than a pure percent.',
240
- 'The state is indeterminate and should be shown as loading instead.',
241
- ],
242
- related: ['stepper()', 'timeline()', 'skeleton()'],
243
- exampleArgs: {
244
- percent: 72,
245
- width: 20,
246
- },
247
- },
248
- {
249
- toolName: 'bijou_stepper',
250
- family: 'stepper()',
251
- category: 'Feedback and Status',
252
- summary: 'Horizontal step-progress indicator with completed and active states.',
253
- aliases: ['stepper', 'steps', 'wizard progress', 'workflow'],
254
- useWhen: [
255
- 'The process has named sequential stages.',
256
- 'You want to show both completed and upcoming steps.',
257
- ],
258
- avoidWhen: [
259
- 'The flow branches or loops like a graph.',
260
- 'Only a raw percentage matters.',
261
- ],
262
- related: ['timeline()', 'progressBar()', 'tabs()'],
263
- exampleArgs: {
264
- steps: [{ label: 'Build' }, { label: 'Test' }, { label: 'Deploy' }],
265
- current: 1,
266
- },
267
- },
268
- {
269
- toolName: 'bijou_timeline',
270
- family: 'timeline()',
271
- category: 'Feedback and Status',
272
- summary: 'Vertical sequence of timestamp-like events with status markers.',
273
- aliases: ['timeline', 'history', 'event stream', 'chronology'],
274
- useWhen: [
275
- 'Order over time is the main story.',
276
- 'Each event needs a short label and optional detail.',
277
- ],
278
- avoidWhen: [
279
- 'The relationship is graph-shaped rather than sequential.',
280
- 'You only need the current step, not the event history.',
281
- ],
282
- related: ['stepper()', 'log()', 'dag()'],
283
- exampleArgs: {
284
- events: [
285
- { label: 'Build', status: 'success' },
286
- { label: 'Deploy', description: 'Canary 25%', status: 'active' },
287
- ],
288
- },
289
- },
290
- {
291
- toolName: 'bijou_log',
292
- family: 'log()',
293
- category: 'Feedback and Status',
294
- summary: 'Single structured log line with severity treatment.',
295
- aliases: ['log', 'log line', 'event', 'status line'],
296
- useWhen: [
297
- 'You need terse operational events.',
298
- 'Severity should be visible without a full alert box.',
299
- ],
300
- avoidWhen: [
301
- 'The message needs explanation, evidence, or grouped detail.',
302
- 'You are rendering a multi-row history rather than one event.',
303
- ],
304
- related: ['timeline()', 'alert()', 'badge()'],
305
- exampleArgs: {
306
- level: 'info',
307
- message: 'Deployment completed.',
308
- },
309
- },
310
- {
311
- toolName: 'bijou_badge',
312
- family: 'badge()',
313
- category: 'Feedback and Status',
314
- summary: 'Compact inline status pill.',
315
- aliases: ['badge', 'pill', 'status chip', 'label'],
316
- useWhen: [
317
- 'A short state label should stay inline with surrounding content.',
318
- 'You need low-chrome categorical emphasis.',
319
- ],
320
- avoidWhen: [
321
- 'The message needs body text or explanation.',
322
- 'The state changes over time and deserves a richer progress surface.',
323
- ],
324
- related: ['alert()', 'log()', 'progressBar()'],
325
- exampleArgs: {
326
- text: 'LIVE',
327
- variant: 'success',
328
- },
329
- },
330
- {
331
- toolName: 'bijou_tabs',
332
- family: 'tabs()',
333
- category: 'Navigation',
334
- summary: 'Horizontal section switcher with one active tab.',
335
- aliases: ['tabs', 'tab bar', 'sections', 'navigation tabs'],
336
- useWhen: [
337
- 'People switch between peer views or sections.',
338
- 'One active choice should be visible at a glance.',
339
- ],
340
- avoidWhen: [
341
- 'The choices are sequential workflow steps.',
342
- 'The navigation is path-like rather than peer-to-peer.',
343
- ],
344
- related: ['breadcrumb()', 'paginator()', 'stepper()'],
345
- exampleArgs: {
346
- items: [{ label: 'Overview' }, { label: 'Logs' }, { label: 'Settings' }],
347
- active: 1,
348
- },
349
- },
350
- {
351
- toolName: 'bijou_breadcrumb',
352
- family: 'breadcrumb()',
353
- category: 'Navigation',
354
- summary: 'Path trail showing where the current surface sits inside a hierarchy.',
355
- aliases: ['breadcrumb', 'path', 'location trail'],
356
- useWhen: [
357
- 'Location context matters more than peer switching.',
358
- 'You need to show depth inside a hierarchy.',
359
- ],
360
- avoidWhen: [
361
- 'Users choose between peer views rather than nested locations.',
362
- 'The hierarchy is dense enough to need a tree.',
363
- ],
364
- related: ['tabs()', 'tree()', 'paginator()'],
365
- exampleArgs: {
366
- items: ['Home', 'Docs', 'API'],
367
- },
368
- },
369
- {
370
- toolName: 'bijou_paginator',
371
- family: 'paginator()',
372
- category: 'Navigation',
373
- summary: 'Compact indicator for current page or viewport position.',
374
- aliases: ['paginator', 'pagination', 'page indicator', 'page dots'],
375
- useWhen: [
376
- 'The user needs lightweight position awareness across pages.',
377
- 'Full tabs or breadcrumbs would be too heavy for the surface.',
378
- ],
379
- avoidWhen: [
380
- 'Page labels matter more than page count.',
381
- 'The navigation is hierarchical rather than sequential.',
382
- ],
383
- related: ['tabs()', 'breadcrumb()', 'stepper()'],
384
- exampleArgs: {
385
- current: 2,
386
- total: 5,
387
- style: 'dots',
388
- },
389
- },
390
- {
391
- toolName: 'bijou_explainability',
392
- family: 'explainability()',
393
- category: 'Rich Panels',
394
- summary: 'Decision card with rationale, evidence, confidence, and next action.',
395
- aliases: ['explainability', 'explanation', 'decision card', 'ai rationale'],
396
- useWhen: [
397
- 'A recommendation or decision needs supporting evidence.',
398
- 'You want the reader to audit reasoning, not just accept output.',
399
- ],
400
- avoidWhen: [
401
- 'The content is simple status or prose without structured rationale.',
402
- 'A generic box or inspector already carries enough context.',
403
- ],
404
- related: ['inspector()', 'alert()', 'note()'],
405
- exampleArgs: {
406
- title: 'Choose table()',
407
- label: 'Recommendation',
408
- rationale: 'The data is rectangular and the reader needs aligned field comparison.',
409
- evidence: [
410
- { label: 'Shape', value: 'rows × columns' },
411
- { label: 'Need', value: 'compare values side by side' },
412
- ],
413
- confidence: 'high',
414
- },
415
- },
416
- {
417
- toolName: 'bijou_inspector',
418
- family: 'inspector()',
419
- category: 'Rich Panels',
420
- summary: 'Detail panel with a primary value and structured supporting sections.',
421
- aliases: ['inspector', 'detail panel', 'detail view', 'property panel'],
422
- useWhen: [
423
- 'A single object or resource needs focused inspection.',
424
- 'You need a primary value plus labeled supporting sections.',
425
- ],
426
- avoidWhen: [
427
- 'The content is really an alert or recommendation.',
428
- 'A flat table or list is enough.',
429
- ],
430
- related: ['explainability()', 'box()', 'headerBox()'],
431
- exampleArgs: {
432
- title: 'Service',
433
- currentValue: 'healthy',
434
- currentValueLabel: 'Status',
435
- supportingText: 'us-west-2',
436
- supportingTextLabel: 'Region',
437
- sections: [{ title: 'Deploy', content: 'Canary complete.' }],
438
- },
439
- },
440
- {
441
- toolName: 'bijou_accordion',
442
- family: 'accordion()',
443
- category: 'Rich Panels',
444
- summary: 'Collapsible sections for progressive disclosure.',
445
- aliases: ['accordion', 'collapsible', 'disclosure', 'expand/collapse'],
446
- useWhen: [
447
- 'Not every section should be open at once.',
448
- 'The reader benefits from progressive disclosure.',
449
- ],
450
- avoidWhen: [
451
- 'Everything should remain visible together for comparison.',
452
- 'The user is switching peer views rather than expanding sections.',
453
- ],
454
- related: ['tabs()', 'inspector()', 'box()'],
455
- exampleArgs: {
456
- sections: [
457
- { title: 'Deploy', content: 'Roll canaries to 25%.', expanded: true },
458
- { title: 'Rollback', content: 'Restore the previous stable build.' },
459
- ],
460
- },
461
- },
462
- {
463
- toolName: 'bijou_kbd',
464
- family: 'kbd()',
465
- category: 'Utility',
466
- summary: 'Keyboard keycap renderer for inline shortcut hints.',
467
- aliases: ['kbd', 'keycap', 'shortcut key', 'keyboard hint'],
468
- useWhen: [
469
- 'You need to show a shortcut inline.',
470
- 'The key label should read like UI chrome rather than plain prose.',
471
- ],
472
- avoidWhen: [
473
- 'The shortcut is incidental and plain text is enough.',
474
- 'You need a full help table rather than one key hint.',
475
- ],
476
- related: ['hyperlink()', 'badge()', 'tabs()'],
477
- exampleArgs: {
478
- key: 'Ctrl+P',
479
- },
480
- },
481
- {
482
- toolName: 'bijou_hyperlink',
483
- family: 'hyperlink()',
484
- category: 'Utility',
485
- summary: 'Terminal hyperlink with explicit plain-text fallback behavior.',
486
- aliases: ['hyperlink', 'link', 'url', 'osc 8'],
487
- useWhen: [
488
- 'The destination matters and should stay explicit.',
489
- 'A plain-text fallback still needs to make sense when OSC 8 is unavailable.',
490
- ],
491
- avoidWhen: [
492
- 'You are just styling text without a destination.',
493
- 'The raw URL alone is clearer than link text.',
494
- ],
495
- related: ['kbd()', 'markdown()', 'box()'],
496
- exampleArgs: {
497
- text: 'Bijou docs',
498
- url: 'https://github.com/flyingrobots/bijou',
499
- },
500
- },
501
- {
502
- toolName: 'bijou_skeleton',
503
- family: 'skeleton()',
504
- category: 'Utility',
505
- summary: 'Placeholder loading surface for still-unavailable content.',
506
- aliases: ['skeleton', 'placeholder', 'loading', 'shimmer'],
507
- useWhen: [
508
- 'Content is loading and the future shape matters.',
509
- 'You need a compact visual placeholder rather than a spinner alone.',
510
- ],
511
- avoidWhen: [
512
- 'The state is determinate enough for a progress bar or stepper.',
513
- 'The load is instantaneous and placeholder chrome adds noise.',
514
- ],
515
- related: ['progressBar()', 'badge()', 'alert()'],
516
- exampleArgs: {
517
- width: 24,
518
- lines: 2,
519
- },
520
- },
521
- {
522
- toolName: 'bijou_markdown',
523
- family: 'markdown()',
524
- category: 'Narrative and Content',
525
- summary: 'Mode-aware terminal markdown renderer for headings, lists, code blocks, links, and quotes.',
526
- aliases: ['markdown', 'md', 'rich text', 'docs prose'],
527
- useWhen: [
528
- 'Source text already exists as markdown and should stay authored that way.',
529
- 'You need headings, lists, quotes, and inline emphasis without rebuilding the prose by hand.',
530
- ],
531
- avoidWhen: [
532
- 'The content is structured data that should be table-, tree-, or graph-shaped.',
533
- 'You need one focused callout rather than a narrative document block.',
534
- ],
535
- related: ['hyperlink()', 'box()', 'guidedFlow()'],
536
- exampleArgs: {
537
- source: '# Release\n\n- Build\n- Test\n- Deploy',
538
- width: 32,
539
- },
540
- },
541
- {
542
- toolName: 'bijou_guided_flow',
543
- family: 'guidedFlow()',
544
- category: 'Narrative and Content',
545
- summary: 'Structured explainability block for posture, steps, sections, and next action.',
546
- aliases: ['guided flow', 'runbook', 'operator guide', 'playbook'],
547
- useWhen: [
548
- 'Readers need a guided operational story instead of an undifferentiated text dump.',
549
- 'You want summary, steps, supporting sections, and a next action inside one coherent block.',
550
- ],
551
- avoidWhen: [
552
- 'A lightweight list, table, or alert would explain the state more directly.',
553
- 'The content is free-form markdown rather than a guided operational flow.',
554
- ],
555
- related: ['explainability()', 'markdown()', 'stepper()'],
556
- exampleArgs: {
557
- title: 'Release canary',
558
- label: 'Flow',
559
- summary: 'Roll canaries to 25% before global promote.',
560
- steps: [
561
- { title: 'Build', status: 'complete' },
562
- { title: 'Canary', status: 'current', detail: 'Watch error budget for 15 minutes.' },
563
- { title: 'Promote', status: 'pending' },
564
- ],
565
- nextAction: 'Hold at 25% until latency stays green.',
566
- width: 48,
567
- },
568
- },
569
- {
570
- toolName: 'bijou_preference_list',
571
- family: 'preferenceListSurface()',
572
- category: 'Forms and Settings',
573
- summary: 'Structured settings list with toggles, actions, descriptions, and selected-row state.',
574
- aliases: ['preference list', 'settings list', 'preferences', 'settings panel'],
575
- useWhen: [
576
- 'Settings need sectioned rows, values, and secondary descriptions.',
577
- 'A shell or page needs a settings surface rather than an ad hoc list of toggles.',
578
- ],
579
- avoidWhen: [
580
- 'You only need one or two status pills or buttons.',
581
- 'The content is narrative guidance rather than configurable rows.',
582
- ],
583
- related: ['tabs()', 'box()', 'guidedFlow()'],
584
- exampleArgs: {
585
- sections: [
586
- {
587
- id: 'shell',
588
- title: 'Shell',
589
- rows: [
590
- { id: 'theme', label: 'Theme', valueLabel: 'Verdant Plum', kind: 'choice' },
591
- { id: 'perf', label: 'Perf HUD', checked: true, kind: 'toggle', description: 'Show development perf overlay.' },
592
- ],
593
- },
594
- ],
595
- width: 42,
596
- selectedRowId: 'perf',
597
- },
598
- },
599
- {
600
- toolName: 'bijou_text_entry',
601
- family: 'input() / textarea()',
602
- category: 'Forms and Settings',
603
- summary: 'Short-form and multiline text-entry prompts for collecting authored input rather than choosing from a fixed set.',
604
- aliases: ['text entry', 'input', 'textarea', 'text field', 'free-form text'],
605
- useWhen: [
606
- 'The user needs to enter original text rather than choose a predefined value.',
607
- 'The difference between short-form and multiline entry matters to the task.',
608
- ],
609
- avoidWhen: [
610
- 'The result is really a choice from a stable option set.',
611
- 'A static content block or note would be more honest than an editable prompt.',
612
- ],
613
- related: ['select() / filter()', 'group() / wizard()', 'note()'],
614
- exampleArgs: {
615
- inputTitle: 'Cluster name',
616
- inputDefault: 'prod-us-west-2',
617
- textareaTitle: 'Rollback notes',
618
- textareaValue: 'Drain traffic\nPromote stable build',
619
- },
620
- },
621
- {
622
- toolName: 'bijou_single_choice',
623
- family: 'select() / filter()',
624
- category: 'Forms and Settings',
625
- summary: 'Single-choice prompt family for visible-list selection and searchable narrowing.',
626
- aliases: ['single choice', 'select', 'filter', 'dropdown', 'combo box'],
627
- useWhen: [
628
- 'The user is choosing one durable value from a known option set.',
629
- 'Search/narrowing helps, but the end result is still one selected value.',
630
- ],
631
- avoidWhen: [
632
- 'The user is building a set rather than making one choice.',
633
- 'The interaction is command dispatch rather than stored selection state.',
634
- ],
635
- related: ['multiselect()', 'input() / textarea()', 'group() / wizard()'],
636
- exampleArgs: {
637
- title: 'Release channel',
638
- options: ['stable', 'canary', 'nightly'],
639
- selected: 'canary',
640
- },
641
- },
642
- {
643
- toolName: 'bijou_multiple_choice',
644
- family: 'multiselect()',
645
- category: 'Forms and Settings',
646
- summary: 'Checkbox-style set builder for choosing several durable values.',
647
- aliases: ['multiple choice', 'multiselect', 'checkboxes', 'set selection'],
648
- useWhen: [
649
- 'The user is building a set of selected values.',
650
- 'The options read like members of one coherent collection.',
651
- ],
652
- avoidWhen: [
653
- 'Only one choice is valid.',
654
- 'The rows are commands or actions instead of lasting state.',
655
- ],
656
- related: ['select() / filter()', 'confirm()', 'group() / wizard()'],
657
- exampleArgs: {
658
- title: 'Deploy targets',
659
- options: ['api', 'web', 'worker'],
660
- selected: ['web', 'worker'],
661
- },
662
- },
663
- {
664
- toolName: 'bijou_binary_decision',
665
- family: 'confirm()',
666
- category: 'Forms and Settings',
667
- summary: 'Explicit yes-or-no confirmation prompt for genuinely binary decisions.',
668
- aliases: ['binary decision', 'confirm', 'yes no', 'confirmation'],
669
- useWhen: [
670
- 'The choice is honestly binary and the consequence of yes versus no matters.',
671
- 'A simple confirmation is clearer than a larger staged form.',
672
- ],
673
- avoidWhen: [
674
- 'The user really has multiple options or tradeoffs to compare.',
675
- 'The prompt needs rich evidence or explanation instead of a binary gate.',
676
- ],
677
- related: ['alert()', 'multiselect()', 'group() / wizard()'],
678
- exampleArgs: {
679
- title: 'Continue deployment',
680
- defaultValue: true,
681
- answer: 'y',
682
- },
683
- },
684
- {
685
- toolName: 'bijou_multi_field_forms',
686
- family: 'group() / wizard()',
687
- category: 'Forms and Settings',
688
- summary: 'Grouped and staged form orchestration for related inputs, progress, and branching flow.',
689
- aliases: ['group', 'wizard', 'multi-step form', 'staged form', 'grouped form'],
690
- useWhen: [
691
- 'Several related inputs belong together under one goal or workflow.',
692
- 'Progress, grouping, or branching matters more than one isolated prompt.',
693
- ],
694
- avoidWhen: [
695
- 'The task only needs one simple field.',
696
- 'The fields are unrelated and should not be bundled into one flow.',
697
- ],
698
- related: ['input() / textarea()', 'select() / filter()', 'confirm()', 'stepper()'],
699
- exampleArgs: {
700
- stepLabel: 'Step 2 of 3',
701
- stepTitle: 'Approval',
702
- fields: [
703
- 'Cluster name? [prod-us-west-2]',
704
- 'Release channel?',
705
- '1. stable',
706
- '2. canary',
707
- '3. nightly',
708
- '> 2',
709
- 'Continue deployment? [Y/n]',
710
- '> y',
711
- ],
712
- },
713
- },
714
- {
715
- toolName: 'bijou_spinner',
716
- family: 'spinnerFrame() / createSpinner()',
717
- category: 'Feedback and Status',
718
- summary: 'Inline spinner glyphs and live spinner controller for indeterminate work.',
719
- aliases: ['spinner', 'loading spinner', 'busy indicator', 'working'],
720
- useWhen: [
721
- 'Work is in flight but there is no honest percentage yet.',
722
- 'You need a compact live-status affordance rather than a large placeholder.',
723
- ],
724
- avoidWhen: [
725
- 'Progress is determinate enough for a progress bar or stepper.',
726
- 'The load state wants a full skeleton or empty-state narrative instead.',
727
- ],
728
- related: ['progressBar()', 'skeleton()', 'timer()'],
729
- exampleArgs: {
730
- tick: 3,
731
- label: 'Build',
732
- },
733
- },
734
- {
735
- toolName: 'bijou_timer',
736
- family: 'timer() / createTimer() / createStopwatch()',
737
- category: 'Feedback and Status',
738
- summary: 'Static and live timer family for countdowns, stopwatches, and elapsed-time readouts.',
739
- aliases: ['timer', 'countdown', 'stopwatch', 'elapsed time'],
740
- useWhen: [
741
- 'Time remaining or elapsed time is the core signal.',
742
- 'You need a compact time readout that can degrade across output modes.',
743
- ],
744
- avoidWhen: [
745
- 'The user needs task progression rather than wall-clock duration.',
746
- 'A timestamp label is enough and no live timer semantics are needed.',
747
- ],
748
- related: ['progressBar()', 'spinnerFrame() / createSpinner()', 'perfOverlaySurface()'],
749
- exampleArgs: {
750
- ms: 150000,
751
- label: 'Deploy',
752
- },
753
- },
754
- {
755
- toolName: 'bijou_sparkline',
756
- family: 'sparkline()',
757
- category: 'Data Visualization',
758
- summary: 'Compact inline trend graph using Unicode block characters.',
759
- aliases: ['sparkline', 'inline chart', 'trend line', 'micro chart'],
760
- useWhen: [
761
- 'You need a tiny trend summary inline with a metric.',
762
- 'A full chart would be too heavy for the available space.',
763
- ],
764
- avoidWhen: [
765
- 'The chart needs axes, dense labels, or higher detail.',
766
- 'The audience needs exact values rather than a quick trend read.',
767
- ],
768
- related: ['brailleChartSurface()', 'statsPanelSurface()', 'perfOverlaySurface()'],
769
- exampleArgs: {
770
- values: [1, 5, 3, 8, 2, 7],
771
- width: 8,
772
- },
773
- },
774
- {
775
- toolName: 'bijou_braille_chart',
776
- family: 'brailleChartSurface()',
777
- category: 'Data Visualization',
778
- summary: 'High-density filled area chart using Unicode Braille sub-pixels.',
779
- aliases: ['braille chart', 'area chart', 'dense chart', 'tiny chart'],
780
- useWhen: [
781
- 'You need more visual density than a sparkline can provide.',
782
- 'The chart should stay text-native but still show trend shape clearly.',
783
- ],
784
- avoidWhen: [
785
- 'Exact values or labeled axes matter more than density.',
786
- 'A single metric trend is compact enough for sparkline().',
787
- ],
788
- related: ['sparkline()', 'statsPanelSurface()', 'perfOverlaySurface()'],
789
- exampleArgs: {
790
- values: [1, 4, 2, 8, 3, 7, 5],
791
- width: 16,
792
- height: 4,
793
- },
794
- },
795
- {
796
- toolName: 'bijou_stats_panel',
797
- family: 'statsPanelSurface()',
798
- category: 'Data Visualization',
799
- summary: 'Aligned metrics panel with labels, values, and optional inline sparklines.',
800
- aliases: ['stats panel', 'metrics panel', 'telemetry panel', 'perf panel'],
801
- useWhen: [
802
- 'Several metrics belong together inside one compact panel.',
803
- 'Labels, values, and small trends should stay aligned and readable.',
804
- ],
805
- avoidWhen: [
806
- 'You only need one metric and a badge or inline value would do.',
807
- 'The content is narrative or workflow guidance rather than telemetry.',
808
- ],
809
- related: ['sparkline()', 'brailleChartSurface()', 'perfOverlaySurface()'],
810
- exampleArgs: {
811
- entries: [
812
- { label: 'FPS', value: '60' },
813
- { label: 'frame', value: '16.7 ms', sparkline: [15.8, 16.1, 16.7, 16.4, 17.0] },
814
- ],
815
- title: 'Perf',
816
- width: 28,
817
- },
818
- },
819
- {
820
- toolName: 'bijou_perf_overlay',
821
- family: 'perfOverlaySurface()',
822
- category: 'Data Visualization',
823
- summary: 'Prebuilt performance dashboard combining a stats panel and braille chart.',
824
- aliases: ['perf overlay', 'performance overlay', 'telemetry overlay', 'fps overlay'],
825
- useWhen: [
826
- 'You need an immediately useful perf HUD without composing several primitives yourself.',
827
- 'Frame timing, terminal size, and memory should be visible together as one overlay.',
828
- ],
829
- avoidWhen: [
830
- 'The app only needs one inline metric or a small sparkline.',
831
- 'The shell should stay clean and no telemetry overlay belongs on screen.',
832
- ],
833
- related: ['statsPanelSurface()', 'brailleChartSurface()', 'sparkline()'],
834
- exampleArgs: {
835
- fps: 60,
836
- frameTimeMs: 16.7,
837
- frameTimeHistory: [15.8, 16.1, 16.7, 16.4, 17.0],
838
- width: 80,
839
- height: 24,
840
- title: 'Perf',
841
- },
842
- },
843
- {
844
- toolName: 'bijou_branding',
845
- family: 'loadRandomLogo() / gradientText()',
846
- category: 'Narrative and Content',
847
- summary: 'Expressive branding helpers for deliberate splash, celebratory, and docs-opening moments.',
848
- aliases: ['branding', 'logo', 'gradient text', 'splash', 'hero'],
849
- useWhen: [
850
- 'The interface needs a deliberate branded or celebratory moment.',
851
- 'Expressive emphasis helps open or orient the experience without carrying critical state.',
852
- ],
853
- avoidWhen: [
854
- 'Routine app chrome or task-critical labels need maximum scanability.',
855
- 'Decoration would compete with the actual work or hide meaning behind color.',
856
- ],
857
- related: ['markdown()', 'box()', 'renderByMode()'],
858
- exampleArgs: {
859
- logo: 'BIJOU',
860
- headline: 'Release ready',
861
- },
862
- },
863
- {
864
- toolName: 'bijou_mode_aware_authoring',
865
- family: 'renderByMode()',
866
- category: 'Utility',
867
- summary: 'Authoring helper for building one semantic primitive that lowers honestly across output modes.',
868
- aliases: ['renderByMode', 'mode-aware primitive', 'custom primitive', 'lowering'],
869
- useWhen: [
870
- 'An app needs a domain-specific primitive that does not belong in the shared component catalog.',
871
- 'The same semantic thing must lower honestly across interactive, pipe, and accessible modes.',
872
- ],
873
- avoidWhen: [
874
- 'An existing Bijou family already matches the job.',
875
- 'Mode branching would only chase cosmetics instead of preserving meaning.',
876
- ],
877
- related: ['note()', 'badge()', 'markdown()'],
878
- exampleArgs: {
879
- semanticThing: 'build health',
880
- interactive: '[build][healthy]',
881
- pipe: 'build health: healthy',
882
- accessible: 'Build health is healthy.',
883
- },
884
- },
885
- ];
6
+ import { numbers, strings, text } from './docs-values.js';
7
+ import { DEFAULT_DOCS_ONLY_INTERACTION_PROFILES, DEFAULT_INTERACTION_PROFILES, MCP_DOCS_CATALOG, } from './docs-catalog/index.js';
8
+ export { MCP_DOCS_CATALOG } from './docs-catalog/index.js';
886
9
  const DOCS_ONLY_EXAMPLE_RENDERERS = {
887
- bijou_markdown: (args) => stripAnsi(markdown(String(args['source'] ?? ''), {
10
+ bijou_markdown: (args) => stripAnsi(markdown(text(args['source']), {
888
11
  width: typeof args['width'] === 'number' ? args['width'] : undefined,
889
12
  ctx: mcpContext(typeof args['width'] === 'number' ? args['width'] : undefined),
890
13
  })),
891
14
  bijou_note: (args) => {
892
15
  const title = typeof args['title'] === 'string' ? args['title'] : undefined;
893
- const message = String(args['message'] ?? '');
16
+ const message = text(args['message']);
894
17
  return title ? `Note (${title}): ${message}` : `Note: ${message}`;
895
18
  },
896
19
  bijou_guided_flow: (args) => stripAnsi(guidedFlow({
897
- ...args,
20
+ ...docsGuidedFlowOptions(args),
898
21
  ctx: mcpContext(typeof args['width'] === 'number' ? args['width'] : undefined),
899
22
  })),
900
- bijou_preference_list: (args) => surfaceToString(preferenceListSurface(args['sections'], {
23
+ bijou_preference_list: (args) => surfaceToString(preferenceListSurface(docsPreferenceSections(args['sections']), {
901
24
  width: Number(args['width'] ?? 40),
902
25
  selectedRowId: typeof args['selectedRowId'] === 'string' ? args['selectedRowId'] : undefined,
903
26
  ctx: mcpContext(typeof args['width'] === 'number' ? args['width'] : undefined),
904
27
  }), plainStyle()),
905
28
  bijou_text_entry: (args) => {
906
- const inputTitle = String(args['inputTitle'] ?? 'Cluster name');
907
- const inputDefault = String(args['inputDefault'] ?? '');
908
- const textareaTitle = String(args['textareaTitle'] ?? 'Details');
909
- const textareaValue = String(args['textareaValue'] ?? '');
29
+ const inputTitle = text(args['inputTitle'], 'Cluster name');
30
+ const inputDefault = text(args['inputDefault']);
31
+ const textareaTitle = text(args['textareaTitle'], 'Details');
32
+ const textareaValue = text(args['textareaValue']);
910
33
  return `${inputTitle}? [${inputDefault}]\n${textareaTitle}?\n${textareaValue}`;
911
34
  },
912
35
  bijou_single_choice: (args) => {
913
- const title = String(args['title'] ?? 'Select one');
914
- const options = Array.isArray(args['options']) ? args['options'].map(String) : [];
915
- const selected = String(args['selected'] ?? options[0] ?? '');
916
- const numbered = options.map((option, index) => `${index + 1}. ${option}`).join('\n');
36
+ const title = text(args['title'], 'Select one');
37
+ const options = strings(args['options']);
38
+ const selected = text(args['selected'], options[0] ?? '');
39
+ const numbered = options.map((option, index) => `${String(index + 1)}. ${option}`).join('\n');
917
40
  const selectedIndex = Math.max(options.findIndex((option) => option === selected), 0) + 1;
918
- return `${title}?\n${numbered}\n> ${selectedIndex}\nSelected: ${selected}`;
41
+ return `${title}?\n${numbered}\n> ${String(selectedIndex)}\nSelected: ${selected}`;
919
42
  },
920
43
  bijou_multiple_choice: (args) => {
921
- const title = String(args['title'] ?? 'Select one or more');
922
- const options = Array.isArray(args['options']) ? args['options'].map(String) : [];
923
- const selected = new Set(Array.isArray(args['selected']) ? args['selected'].map(String) : []);
44
+ const title = text(args['title'], 'Select one or more');
45
+ const options = strings(args['options']);
46
+ const selected = new Set(strings(args['selected']));
924
47
  const lines = options.map((option) => `[${selected.has(option) ? 'x' : ' '}] ${option}`);
925
48
  return `${title}?\n${lines.join('\n')}\nSelected: ${Array.from(selected).join(', ')}`;
926
49
  },
927
50
  bijou_binary_decision: (args) => {
928
- const title = String(args['title'] ?? 'Continue');
51
+ const title = text(args['title'], 'Continue');
929
52
  const defaultValue = args['defaultValue'] === false ? '[y/N]' : '[Y/n]';
930
- const answer = String(args['answer'] ?? '');
53
+ const answer = text(args['answer']);
931
54
  return `${title}? ${defaultValue}\n> ${answer}`;
932
55
  },
933
56
  bijou_multi_field_forms: (args) => {
934
- const stepLabel = String(args['stepLabel'] ?? 'Step 1 of 1');
935
- const stepTitle = String(args['stepTitle'] ?? 'Details');
936
- const fields = Array.isArray(args['fields']) ? args['fields'].map(String) : [];
57
+ const stepLabel = text(args['stepLabel'], 'Step 1 of 1');
58
+ const stepTitle = text(args['stepTitle'], 'Details');
59
+ const fields = strings(args['fields']);
937
60
  return `${stepLabel}: ${stepTitle}\n${fields.join('\n')}`;
938
61
  },
939
62
  bijou_spinner: (args) => spinnerFrame(Number(args['tick'] ?? 0), {
@@ -943,13 +66,13 @@ const DOCS_ONLY_EXAMPLE_RENDERERS = {
943
66
  label: typeof args['label'] === 'string' ? args['label'] : undefined,
944
67
  ctx: mcpContext(),
945
68
  })),
946
- bijou_sparkline: (args) => sparkline(args['values'] ?? [], { width: typeof args['width'] === 'number' ? args['width'] : undefined }),
947
- bijou_braille_chart: (args) => surfaceToString(brailleChartSurface(args['values'] ?? [], {
69
+ bijou_sparkline: (args) => sparkline(numbers(args['values']), { width: typeof args['width'] === 'number' ? args['width'] : undefined }),
70
+ bijou_braille_chart: (args) => surfaceToString(brailleChartSurface(numbers(args['values']), {
948
71
  width: Number(args['width'] ?? 0),
949
72
  height: Number(args['height'] ?? 0),
950
73
  ctx: mcpContext(typeof args['width'] === 'number' ? args['width'] : undefined),
951
74
  }), plainStyle()),
952
- bijou_stats_panel: (args) => surfaceToString(statsPanelSurface(args['entries'] ?? [], {
75
+ bijou_stats_panel: (args) => surfaceToString(statsPanelSurface(docsStatsPanelEntries(args['entries']), {
953
76
  title: typeof args['title'] === 'string' ? args['title'] : undefined,
954
77
  width: Number(args['width'] ?? 28),
955
78
  ctx: mcpContext(typeof args['width'] === 'number' ? args['width'] : undefined),
@@ -957,7 +80,7 @@ const DOCS_ONLY_EXAMPLE_RENDERERS = {
957
80
  bijou_perf_overlay: (args) => surfaceToString(perfOverlaySurface({
958
81
  fps: Number(args['fps'] ?? 0),
959
82
  frameTimeMs: Number(args['frameTimeMs'] ?? 0),
960
- frameTimeHistory: args['frameTimeHistory'] ?? [],
83
+ frameTimeHistory: numbers(args['frameTimeHistory']),
961
84
  width: Number(args['width'] ?? 80),
962
85
  height: Number(args['height'] ?? 24),
963
86
  }, {
@@ -965,20 +88,82 @@ const DOCS_ONLY_EXAMPLE_RENDERERS = {
965
88
  ctx: mcpContext(typeof args['width'] === 'number' ? args['width'] : undefined),
966
89
  }), plainStyle()),
967
90
  bijou_branding: (args) => {
968
- const logo = String(args['logo'] ?? 'BIJOU');
969
- const headline = String(args['headline'] ?? '');
91
+ const logo = text(args['logo'], 'BIJOU');
92
+ const headline = text(args['headline']);
970
93
  return `${logo}\n${headline}`.trimEnd();
971
94
  },
972
95
  bijou_mode_aware_authoring: (args) => {
973
- const semanticThing = String(args['semanticThing'] ?? 'semantic thing');
96
+ const semanticThing = text(args['semanticThing'], 'semantic thing');
974
97
  return [
975
98
  `${semanticThing}:`,
976
- `interactive -> ${String(args['interactive'] ?? '[rich output]')}`,
977
- `pipe -> ${String(args['pipe'] ?? 'plain fallback')}`,
978
- `accessible -> ${String(args['accessible'] ?? 'explicit reading-order fallback')}`,
99
+ `interactive -> ${text(args['interactive'], '[rich output]')}`,
100
+ `pipe -> ${text(args['pipe'], 'plain fallback')}`,
101
+ `accessible -> ${text(args['accessible'], 'explicit reading-order fallback')}`,
979
102
  ].join('\n');
980
103
  },
981
104
  };
105
+ function docsGuidedFlowOptions(args) {
106
+ return {
107
+ title: text(args['title'], 'Guided flow'),
108
+ label: text(args['label']),
109
+ summary: text(args['summary']),
110
+ metadata: strings(args['metadata']),
111
+ steps: records(args['steps']).map((step) => ({
112
+ title: text(step['title'], 'Step'),
113
+ detail: text(step['detail']),
114
+ status: guidedFlowStepStatus(step['status']),
115
+ })),
116
+ sections: records(args['sections']).map((section) => ({
117
+ title: text(section['title'], 'Section'),
118
+ content: text(section['content']),
119
+ tone: section['tone'] === 'muted' ? 'muted' : 'normal',
120
+ })),
121
+ nextAction: text(args['nextAction']),
122
+ nextActionLabel: text(args['nextActionLabel']),
123
+ width: Number(args['width'] ?? 48),
124
+ };
125
+ }
126
+ function docsPreferenceSections(value) {
127
+ return records(value).map((section, sectionIndex) => ({
128
+ id: text(section['id'], `section-${String(sectionIndex)}`),
129
+ title: text(section['title'], 'Settings'),
130
+ rows: docsPreferenceRows(section['rows']),
131
+ }));
132
+ }
133
+ function docsPreferenceRows(value) {
134
+ return records(value).map((row, rowIndex) => {
135
+ const kind = preferenceRowKind(row['kind']);
136
+ return {
137
+ id: text(row['id'], `row-${String(rowIndex)}`),
138
+ label: text(row['label'], 'Setting'),
139
+ description: text(row['description']),
140
+ valueLabel: text(row['valueLabel']),
141
+ checked: row['checked'] === true,
142
+ enabled: row['enabled'] !== false,
143
+ ...(kind === undefined ? {} : { kind }),
144
+ };
145
+ });
146
+ }
147
+ function docsStatsPanelEntries(value) {
148
+ return records(value).map((entry) => ({
149
+ label: text(entry['label'], 'metric'),
150
+ value: text(entry['value']),
151
+ sparkline: numbers(entry['sparkline']),
152
+ }));
153
+ }
154
+ function records(value) {
155
+ return Array.isArray(value)
156
+ ? value.filter((item) => (typeof item === 'object' && item !== null && !Array.isArray(item)))
157
+ : [];
158
+ }
159
+ function guidedFlowStepStatus(value) {
160
+ return value === 'complete' || value === 'current' || value === 'pending' ? value : undefined;
161
+ }
162
+ function preferenceRowKind(value) {
163
+ return value === 'toggle' || value === 'choice' || value === 'info' || value === 'action'
164
+ ? value
165
+ : undefined;
166
+ }
982
167
  function normalizeDocsTerm(value) {
983
168
  return value.toLowerCase().replace(/[^a-z0-9]+/g, ' ').trim();
984
169
  }
@@ -1014,7 +199,7 @@ function scoreDocsEntry(entry, normalizedQuery) {
1014
199
  return score;
1015
200
  }
1016
201
  function exampleText(result) {
1017
- return result.content.find((block) => block.type === 'text')?.text
202
+ return result.content[0]?.text
1018
203
  ?? (typeof result.structuredContent?.['rendered'] === 'string'
1019
204
  ? result.structuredContent['rendered']
1020
205
  : '');
@@ -1043,7 +228,7 @@ export function createDocsTool(tools) {
1043
228
  const inputSchema = z.object(inputShape);
1044
229
  return {
1045
230
  name: 'bijou_docs',
1046
- description: 'Query machine-readable documentation for the bijou-mcp render-tool surface plus the full public first-party Bijou component-family surface, including docs-only families that do not yet have dedicated MCP renderers. Returns usage guidance, interaction-profile notes, related tools, sample input, and optional rendered example output.',
231
+ description: 'Query machine-readable docs for bijou-mcp render tools and public first-party Bijou component families, including docs-only families before dedicated MCP renderers exist. Returns usage guidance, interaction profiles, related tools, sample input, and optional rendered example output.',
1047
232
  inputSchema: inputShape,
1048
233
  outputSchema: structuredToolOutputShape,
1049
234
  handler: async (args) => {
@@ -1076,14 +261,14 @@ export function createDocsTool(tools) {
1076
261
  aliases: entry.aliases,
1077
262
  };
1078
263
  if (entry.exampleArgs !== undefined) {
1079
- result['exampleInput'] = entry.exampleArgs;
264
+ result.exampleInput = entry.exampleArgs;
1080
265
  }
1081
266
  if (includeExamples && entry.exampleArgs !== undefined) {
1082
267
  if (tool !== undefined) {
1083
- result['exampleOutput'] = exampleText(await tool.handler(entry.exampleArgs));
268
+ result.exampleOutput = exampleText(await tool.handler(entry.exampleArgs));
1084
269
  }
1085
270
  else if (docsOnlyRenderer !== undefined) {
1086
- result['exampleOutput'] = docsOnlyRenderer(entry.exampleArgs);
271
+ result.exampleOutput = docsOnlyRenderer(entry.exampleArgs);
1087
272
  }
1088
273
  }
1089
274
  return result;