burgee 0.3.0 → 0.5.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 (53) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +61 -9
  3. package/dist/brand.d.ts +62 -1
  4. package/dist/brand.js +73 -7
  5. package/dist/{commander-argument.js → commander/argument.js} +4 -1
  6. package/dist/{commander-command.d.ts → commander/command.d.ts} +5 -5
  7. package/dist/{commander-command.js → commander/command.js} +159 -22
  8. package/dist/{commander-error.js → commander/error.js} +2 -0
  9. package/dist/{commander-help.d.ts → commander/help.d.ts} +3 -3
  10. package/dist/{commander-help.js → commander/help.js} +18 -1
  11. package/dist/{commander-option.d.ts → commander/option.d.ts} +1 -1
  12. package/dist/{commander-option.js → commander/option.js} +15 -1
  13. package/dist/commander.d.ts +8 -8
  14. package/dist/commander.js +8 -8
  15. package/dist/completions.js +15 -4
  16. package/dist/contrast.d.ts +9 -11
  17. package/dist/contrast.js +2 -33
  18. package/dist/execute.js +39 -24
  19. package/dist/schema.d.ts +34 -0
  20. package/dist/schema.js +12 -0
  21. package/dist/unknown-option.d.ts +9 -0
  22. package/dist/unknown-option.js +27 -0
  23. package/dist/{yargs-burgee.d.ts → yargs/burgee.d.ts} +2 -2
  24. package/dist/{yargs-burgee.js → yargs/burgee.js} +15 -1
  25. package/dist/{yargs-cliui.js → yargs/cliui.js} +8 -0
  26. package/dist/{yargs-command.d.ts → yargs/command.d.ts} +5 -5
  27. package/dist/{yargs-command.js → yargs/command.js} +9 -2
  28. package/dist/{yargs-completion.d.ts → yargs/completion.d.ts} +3 -3
  29. package/dist/{yargs-completion.js → yargs/completion.js} +7 -2
  30. package/dist/{yargs-factory.d.ts → yargs/factory.d.ts} +8 -8
  31. package/dist/{yargs-factory.js → yargs/factory.js} +63 -13
  32. package/dist/{yargs-middleware.js → yargs/middleware.js} +6 -1
  33. package/dist/{yargs-shim.d.ts → yargs/shim.d.ts} +3 -3
  34. package/dist/{yargs-shim.js → yargs/shim.js} +24 -5
  35. package/dist/{yargs-usage.d.ts → yargs/usage.d.ts} +1 -1
  36. package/dist/{yargs-usage.js → yargs/usage.js} +9 -1
  37. package/dist/{yargs-utils.js → yargs/utils.js} +11 -0
  38. package/dist/{yargs-validation.d.ts → yargs/validation.d.ts} +1 -1
  39. package/dist/{yargs-validation.js → yargs/validation.js} +7 -1
  40. package/dist/{yargs-y18n.js → yargs/y18n.js} +6 -0
  41. package/dist/yargs-helpers.d.ts +1 -1
  42. package/dist/yargs-helpers.js +1 -1
  43. package/dist/yargs.d.ts +4 -4
  44. package/dist/yargs.js +5 -5
  45. package/package.json +5 -1
  46. /package/dist/{commander-argument.d.ts → commander/argument.d.ts} +0 -0
  47. /package/dist/{commander-error.d.ts → commander/error.d.ts} +0 -0
  48. /package/dist/{commander-suggest.d.ts → suggest.d.ts} +0 -0
  49. /package/dist/{commander-suggest.js → suggest.js} +0 -0
  50. /package/dist/{yargs-cliui.d.ts → yargs/cliui.d.ts} +0 -0
  51. /package/dist/{yargs-middleware.d.ts → yargs/middleware.d.ts} +0 -0
  52. /package/dist/{yargs-utils.d.ts → yargs/utils.d.ts} +0 -0
  53. /package/dist/{yargs-y18n.d.ts → yargs/y18n.d.ts} +0 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ofri Peretz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,13 +1,35 @@
1
- # burgee
1
+ <p align="center">
2
+ <a href="https://github.com/ofri-peretz/burgee" target="blank">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/ofri-peretz/burgee/main/brand-assets/burgee-lockup.svg" />
5
+ <img src="https://raw.githubusercontent.com/ofri-peretz/burgee/main/brand-assets/burgee-lockup-light.svg" alt="burgee" width="360" />
6
+ </picture>
7
+ </a>
8
+ </p>
2
9
 
3
- **Not yet released.** This version reserves the name; the first working release is
4
- wave 1 of the roadmap.
10
+ <p align="center">
11
+ Everything a CLI needs that isn't your CLI. Written once, served to humans and agents alike.
12
+ </p>
13
+
14
+ <p align="center">
15
+ <a href="https://www.npmjs.com/package/burgee"><img src="https://img.shields.io/npm/v/burgee?style=flat-square&color=0a6b47" alt="npm version" /></a>
16
+ <a href="https://www.npmjs.com/package/burgee"><img src="https://img.shields.io/npm/dm/burgee?style=flat-square" alt="npm downloads" /></a>
17
+ <img src="https://img.shields.io/badge/runtime%20dependencies-0-0a6b47?style=flat-square" alt="Zero runtime dependencies" />
18
+ <img src="https://img.shields.io/badge/Node.js-24+-green.svg?style=flat-square" alt="Node.js 24+" />
19
+ <img src="https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square" alt="License: MIT" />
20
+ </p>
5
21
 
6
22
  A **burgee** is the small swallowtail flag a boat flies to say which club or fleet it
7
23
  belongs to — a flag of identity, not of instruction. That is what this framework does for
8
24
  a command-line program: a command declares itself once, and every surface is that
9
25
  declaration read by a different reader.
10
26
 
27
+ ## Start here
28
+
29
+ ```bash
30
+ npm install burgee
31
+ ```
32
+
11
33
  ```js
12
34
  // cli.mjs — the whole CLI
13
35
  import { defineCommand, run } from 'burgee';
@@ -35,7 +57,9 @@ hint: pass --name <value>
35
57
  One file. No build step, no config file, no directory convention. A test enforces
36
58
  that on every commit.
37
59
 
38
- ```
60
+ ## One declaration, every surface
61
+
62
+ ```text
39
63
  defineCommand() ──▶ manifest ──┬──▶ human help
40
64
  ├──▶ --json one stable envelope
41
65
  ├──▶ --schema versioned, JSON-Schema validated
@@ -45,18 +69,46 @@ defineCommand() ──▶ manifest ──┬──▶ human help
45
69
  └──▶ docs + llms.txt
46
70
  ```
47
71
 
72
+ Nothing here needs keeping in sync, because nothing is written twice.
73
+
74
+ ## Already on commander?
75
+
48
76
  Drop-in compatible with both incumbents, graded by **their own test suites** — 1,215
49
77
  commander tests and 1,185 yargs tests — with the pass rate published and ratcheting:
50
78
 
51
- ```js
79
+ ```diff
52
80
  - import { Command } from 'commander';
53
81
  + import { Command } from 'burgee/commander';
54
82
  ```
55
83
 
56
- It stays a library you import in one file: no build step, no config, no directory
57
- convention, no scaffold. A test enforces that.
84
+ Your code and your tests are unchanged. A façade is never called "compatible" until its
85
+ host's own suite passes 100%; below that the rate is published instead of claimed.
86
+
87
+ ## What is in the box
88
+
89
+ | Import | Gives you |
90
+ | :-- | :-- |
91
+ | `burgee` | `defineCommand()`, `run()`, the exit-code contract and the JSON envelope. |
92
+ | `burgee/commander` | The commander API, graded by commander's suite. |
93
+ | `burgee/testing` | Run a command in-process and assert on its result — no spawning. |
94
+ | `burgee/brand` | One brand declaration → flag, favicon, OG card, cover, lockup. The logo above is its own output. |
95
+
96
+ ## Status
58
97
 
59
- Roadmap, architecture and the 79-requirement floor:
98
+ `burgee` is published and working: the engine, the exit-code contract, the JSON envelope,
99
+ help from the manifest, plugins with hook filters, and a `burgee/commander` façade that
100
+ runs a real commander program. The dev loop, prompts, lazy commands and groups are not
101
+ here yet.
102
+
103
+ Roadmap, architecture and the 101-requirement floor:
60
104
  <https://github.com/ofri-peretz/burgee>
61
105
 
62
- MIT © Interlace
106
+ ---
107
+
108
+ Part of the [burgee](https://github.com/ofri-peretz/burgee) family: a CLI on burgee declares
109
+ what it is, [roundel](https://www.npmjs.com/package/roundel) carries its colours,
110
+ [flagstaff](https://www.npmjs.com/package/flagstaff) flies it, and
111
+ [caique](https://www.npmjs.com/package/caique) answers back. Each is an independent package;
112
+ none requires the others.
113
+
114
+ MIT © Ofri Peretz — see [LICENSE](./LICENSE).
package/dist/brand.d.ts CHANGED
@@ -83,6 +83,58 @@ export interface BurgeeBrand {
83
83
  * time on a file you wrote, not on anything a user supplies at runtime.
84
84
  */
85
85
  charge?: string;
86
+ /**
87
+ * Your own silhouette instead of the swallowtail: SVG path data in the same
88
+ * `0 0 100 100` box, filled with the field and carrying the charge exactly as
89
+ * the flag does. For a sibling brand whose name is not a flag — a roundel is
90
+ * rings, a parrot is a parrot — the shape is the whole point, and drawing it
91
+ * here keeps every other projection (favicon, lockup, OG, cover) intact.
92
+ *
93
+ * Filled `evenodd`, so a subpath drawn inside another cuts a hole through it:
94
+ * that is how a ring gets its centre and an eye gets its white. Subpaths that
95
+ * are meant to read as one solid body must not overlap.
96
+ *
97
+ * Emitted verbatim, like {@link BurgeeBrand.charge}: a build-time value you
98
+ * wrote, never anything a user supplies at runtime.
99
+ */
100
+ shape?: string;
101
+ /**
102
+ * A sheen: a soft highlight laid across the field, `0` to `1`, where the
103
+ * number is how bright its brightest point is. Depth, not decoration — a flat
104
+ * gradient reads as printed ink, and one light source makes the same shape
105
+ * read as an object with a front.
106
+ *
107
+ * It is drawn INSIDE the silhouette (clipped to it), so it never softens the
108
+ * outline the mark is recognised by, and it sits under the charge, so it never
109
+ * touches the contrast the charge was measured at.
110
+ *
111
+ * The same layer is what moves in {@link Burgee.alive}.
112
+ */
113
+ sheen?: number;
114
+ /**
115
+ * A bevel: how strongly the mark's own edge catches the light, `0` to `1`.
116
+ *
117
+ * The whole of the third dimension a logo can afford. Two copies of the
118
+ * silhouette stroked and clipped to itself — light offset up toward the light
119
+ * source, dark offset away — so the edge lifts and the face stays flat. No
120
+ * extrusion, no renderer, and nothing that stops it being a 16px favicon: the
121
+ * bevel is sub-pixel there and simply disappears, which is the correct
122
+ * behaviour rather than a compromise.
123
+ */
124
+ bevel?: number;
125
+ /**
126
+ * Markings: SVG markup in the same `0 0 100 100` box as {@link BurgeeBrand.shape},
127
+ * drawn over the field and under the charge.
128
+ *
129
+ * One path can hold one fill, and some marks are not one colour — a roundel is
130
+ * concentric rings, a caique has a black cap over an orange throat over a white
131
+ * belly. Those are markings ON the body, not the body, and they are declared
132
+ * here rather than by stacking whole brands on top of each other.
133
+ *
134
+ * Emitted verbatim, like {@link BurgeeBrand.charge}: a build-time value you
135
+ * wrote, never anything a user supplies at runtime.
136
+ */
137
+ markings?: string;
86
138
  /**
87
139
  * The field, as gradient stops along {@link FIELD_AXIS}. One stop is a flat
88
140
  * field. Keep a dark stop under the charge or the mark will not read.
@@ -139,7 +191,7 @@ export declare function placeCharge(markup: string, scale?: number): string;
139
191
  /** The two Interlace bars, rotated and placed as the charge. */
140
192
  export declare function chargeGroup(colors: BurgeeColors, scale?: number): string;
141
193
  /** Field, charge and optional bordure — everything inside the viewBox. */
142
- export declare function burgeeBody(brand: BurgeeBrand, id?: string): string;
194
+ export declare function burgeeBody(brand: BurgeeBrand, id?: string, moving?: boolean): string;
143
195
  export interface CardOptions {
144
196
  width?: number;
145
197
  height?: number;
@@ -156,6 +208,15 @@ export interface CardOptions {
156
208
  export interface Burgee {
157
209
  /** The flag alone, square, at any size. */
158
210
  flag(size?: number): string;
211
+ /**
212
+ * The same mark with its sheen sweeping across it, for a page that can afford
213
+ * motion — a site header, a docs hero. Identical to {@link Burgee.flag} when
214
+ * no `sheen` is declared, and parked still under `prefers-reduced-motion`.
215
+ *
216
+ * Not the favicon and not the README: a tab icon that shimmers is a tab icon
217
+ * that distracts.
218
+ */
219
+ alive(size?: number): string;
159
220
  /** Favicon master. One file serves both themes — the flag carries its own field. */
160
221
  favicon(size?: number): string;
161
222
  /** Social card, 1200×630. */
package/dist/brand.js CHANGED
@@ -47,7 +47,9 @@ const HASH_SEED = 5381;
47
47
  const HASH_SHIFT = 5;
48
48
  const HASH_RADIX = 36;
49
49
  export function fieldId(brand) {
50
- const source = JSON.stringify([brand.field, brand.mark, brand.bordure, brand.charge]);
50
+ const base = [brand.field, brand.mark, brand.bordure, brand.charge];
51
+ const extra = [brand.shape, brand.sheen, brand.bevel, brand.markings].filter((v) => v !== undefined);
52
+ const source = JSON.stringify(extra.length === 0 ? base : [...base, ...extra]);
51
53
  let h = HASH_SEED;
52
54
  for (let i = 0; i < source.length; i++) {
53
55
  h = ((h << HASH_SHIFT) + h + (source.codePointAt(i) ?? 0)) >>> 0;
@@ -89,7 +91,7 @@ function bordureBands(brand) {
89
91
  if (bands.length === 0)
90
92
  return '';
91
93
  let total = bands.reduce((sum, band) => sum + band.width, 0);
92
- const path = burgeeFlagPath();
94
+ const path = silhouette(brand);
93
95
  const drawn = [];
94
96
  for (const band of bands) {
95
97
  drawn.push(`<path d="${path}" fill="none" stroke="${band.color}"` +
@@ -98,10 +100,70 @@ function bordureBands(brand) {
98
100
  }
99
101
  return drawn.join('');
100
102
  }
101
- export function burgeeBody(brand, id = fieldId(brand)) {
103
+ const SHEEN_START = -50;
104
+ const SHEEN_LEAN = -14;
105
+ const SHEEN = {
106
+ width: 34,
107
+ still: 8,
108
+ from: SHEEN_START,
109
+ to: 140,
110
+ seconds: 7,
111
+ lean: SHEEN_LEAN,
112
+ };
113
+ const SHEEN_LIGHT = '#ffffff';
114
+ const SHEEN_PEAK = 0.5;
115
+ function silhouette(brand) {
116
+ return brand.shape ?? burgeeFlagPath();
117
+ }
118
+ function sheenStop(offset, opacity) {
119
+ return (`<stop offset="${round(offset)}" stop-color="${SHEEN_LIGHT}"` +
120
+ ` stop-opacity="${round(opacity)}"/>`);
121
+ }
122
+ function clip(brand, id) {
123
+ if (brand.sheen === undefined && brand.bevel === undefined && brand.markings === undefined) {
124
+ return '';
125
+ }
126
+ return `<clipPath id="${id}-c"><path d="${silhouette(brand)}"/></clipPath>`;
127
+ }
128
+ function sheenBand(brand, id, moving) {
129
+ if (brand.sheen === undefined)
130
+ return '';
131
+ return (`<linearGradient id="${id}-s" gradientUnits="objectBoundingBox" x1="0" y1="0" x2="1" y2="0">` +
132
+ `${sheenStop(0, 0)}${sheenStop(SHEEN_PEAK, brand.sheen)}${sheenStop(1, 0)}</linearGradient>` +
133
+ `<g clip-path="url(#${id}-c)"><g transform="skewX(${SHEEN.lean})">` +
134
+ `<rect x="${moving ? SHEEN.from : SHEEN.still}" y="-20" width="${SHEEN.width}" height="140"` +
135
+ `${moving ? ` class="${id}-sweep"` : ''} fill="url(#${id}-s)"/></g></g>`);
136
+ }
137
+ function sheenStyle(id) {
138
+ return (`<style>` +
139
+ `@keyframes ${id}-sweep{from{transform:translateX(0)}` +
140
+ `to{transform:translateX(${SHEEN.to - SHEEN.from}px)}}` +
141
+ `.${id}-sweep{animation:${id}-sweep ${SHEEN.seconds}s ease-in-out infinite}` +
142
+ `@media (prefers-reduced-motion:reduce){.${id}-sweep{animation:none;` +
143
+ `transform:translateX(${SHEEN.still - SHEEN.from}px)}}` +
144
+ `</style>`);
145
+ }
146
+ const BEVEL = { offset: 0.55, width: 1.5, dark: 0.45 };
147
+ const BEVEL_DARK = '#000000';
148
+ function bevelEdges(brand, id) {
149
+ if (brand.bevel === undefined)
150
+ return '';
151
+ const shape = silhouette(brand);
152
+ const edge = (dx, dy, color, opacity) => `<path d="${shape}" fill="none" stroke="${color}" stroke-opacity="${round(opacity)}"` +
153
+ ` stroke-width="${BEVEL.width}" stroke-linejoin="round"` +
154
+ ` transform="translate(${round(dx)} ${round(dy)})"/>`;
155
+ const lit = edge(-BEVEL.offset, -BEVEL.offset, SHEEN_LIGHT, brand.bevel);
156
+ const shaded = edge(BEVEL.offset, BEVEL.offset, BEVEL_DARK, brand.bevel * BEVEL.dark);
157
+ return `<g clip-path="url(#${id}-c)">${lit}${shaded}</g>`;
158
+ }
159
+ export function burgeeBody(brand, id = fieldId(brand), moving = false) {
102
160
  const charge = brand.charge === undefined ? chargeGroup(brand.mark) : placeCharge(brand.charge);
103
- return (`${gradient(brand, id)}${bordureBands(brand)}` +
104
- `<path d="${burgeeFlagPath()}" fill="url(#${id})"/>${charge}`);
161
+ const field = brand.shape === undefined
162
+ ? `<path d="${burgeeFlagPath()}" fill="url(#${id})"/>`
163
+ : `<path d="${brand.shape}" fill="url(#${id})" fill-rule="evenodd"/>`;
164
+ const markings = brand.markings === undefined ? '' : `<g clip-path="url(#${id}-c)">${brand.markings}</g>`;
165
+ return (`${gradient(brand, id)}${bordureBands(brand)}${field}${clip(brand, id)}` +
166
+ `${markings}${sheenBand(brand, id, moving)}${bevelEdges(brand, id)}${charge}`);
105
167
  }
106
168
  const FONT = 'ui-monospace, SFMono-Regular, Menlo, monospace';
107
169
  const TRACKING_TIGHTEN = 0.03;
@@ -211,18 +273,22 @@ function renderCard(brand, options, size) {
211
273
  parts.push('</svg>');
212
274
  return parts.join('\n');
213
275
  }
214
- function renderFlag(brand, size) {
276
+ function renderFlag(brand, size, moving = false) {
215
277
  const label = brand.name ? ` role="img" aria-label="${escape(brand.name)}"` : ' aria-hidden="true"';
278
+ const id = fieldId(brand);
279
+ const alive = moving && brand.sheen !== undefined;
216
280
  return [
217
281
  `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${MARK.SPAN} ${MARK.SPAN}"` +
218
282
  ` width="${size}" height="${size}"${label}>`,
219
- ` ${burgeeBody(brand)}`,
283
+ ...(alive ? [` ${sheenStyle(id)}`] : []),
284
+ ` ${burgeeBody(brand, id, alive)}`,
220
285
  '</svg>',
221
286
  ].join('\n');
222
287
  }
223
288
  export function defineBurgee(brand) {
224
289
  return {
225
290
  flag: (size = MARK.SPAN) => renderFlag(brand, size),
291
+ alive: (size = MARK.SPAN) => renderFlag(brand, size, true),
226
292
  favicon: (size = SIZES.FAVICON) => renderFlag(brand, size),
227
293
  og: (options = {}) => renderCard(brand, options, SIZES.OG),
228
294
  cover: (options = {}) => renderCard(brand, options, SIZES.COVER),
@@ -1,4 +1,4 @@
1
- import { InvalidArgumentError } from './commander-error.js';
1
+ import { InvalidArgumentError } from './error.js';
2
2
  export class Argument {
3
3
  description;
4
4
  variadic = false;
@@ -8,6 +8,7 @@ export class Argument {
8
8
  argChoices = undefined;
9
9
  required;
10
10
  _name;
11
+ /** `<required>`, `[optional]`, bare = required; a trailing `...` makes it variadic. */
11
12
  constructor(name, description) {
12
13
  this.description = description || '';
13
14
  switch (name[0]) {
@@ -66,7 +67,9 @@ export class Argument {
66
67
  return this;
67
68
  }
68
69
  }
70
+ /** `<name>` / `[name]` / `<name...>` for usage strings. */
69
71
  export function humanReadableArgName(arg) {
70
72
  const nameOutput = arg.name() + (arg.variadic ? '...' : '');
71
73
  return arg.required ? `<${nameOutput}>` : `[${nameOutput}]`;
72
74
  }
75
+ //# sourceMappingURL=argument.js.map
@@ -14,11 +14,11 @@
14
14
  */
15
15
  import childProcess from 'node:child_process';
16
16
  import { EventEmitter } from 'node:events';
17
- import { Argument, type ParseArg } from './commander-argument.js';
18
- import { CommanderError } from './commander-error.js';
19
- import { Help } from './commander-help.js';
20
- import { Option } from './commander-option.js';
21
- import { type Effects, Manifest, type OptionSpec, type Plugin } from './manifest.js';
17
+ import { type Effects, Manifest, type OptionSpec, type Plugin } from '../manifest.js';
18
+ import { Argument, type ParseArg } from './argument.js';
19
+ import { CommanderError } from './error.js';
20
+ import { Help } from './help.js';
21
+ import { Option } from './option.js';
22
22
  export interface OutputConfiguration {
23
23
  writeOut: (str: string) => void;
24
24
  writeErr: (str: string) => void;