@davmash/bygone 0.5.2 → 0.7.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.
package/CHANGELOG.md CHANGED
@@ -2,8 +2,21 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- - Added modified-file detection and drill-down for directory comparisons
6
- - Added prototype three-directory comparison with adjacent multi-panel file drill-down
5
+ ## 0.7.1
6
+
7
+ - Fixed the standalone npm package so the installed `bygone` command includes its tour coverage runtime
8
+ - Made Homebrew release staging update formula and cask versions and declare the cask's macOS requirement
9
+
10
+ ## 0.7.0
11
+
12
+ - Expanded directory comparison into an N-panel workspace with asynchronous diff computation, drill-down, and complete file navigation
13
+ - Added merge-base branch review with rename pairing, binary and image comparison, and consistent navigation across Git workflows
14
+ - Improved semantic highlighting, structural replacement matching, line-click selection, connector rendering, and multi-panel interaction consistency
15
+ - Added generated shell completions, desktop CLI installation, local rebuild/install automation, linting, and renderer bundle budgets
16
+ - Added authored, code-connected change tours, bounded LLM context, coverage reporting, stacked and deconstructed explanations, the PR Tour Guide skill, and independent narrative and file navigation
17
+ - Added refreshable sessions, pane-local find, persistent line wrapping, resizable sidebars, and horizontal multi-panel scrolling
18
+ - Adopted the crisp-hybrid Bygone icon and strengthened release packaging across npm, VS Code, desktop, and Homebrew artifacts
19
+ - Added a [guided 0.7 release tour](./tours/v0.7.bygone.yaml) covering the complete range from `v0.6.3`
7
20
 
8
21
  ## 0.2.0
9
22
 
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
+ ---
2
+ author: David Mashburn
3
+ created_at: 2026-06-01T20:35:08Z
4
+ modified_at: 2026-07-31T20:56:06Z
5
+ generated_by: Codex
6
+ generated_for: David Mashburn
7
+ reviewed_by:
8
+ approved_by:
9
+ repo: https://github.com/davidmashburn/bygone
10
+ branch: main
11
+ repo_branch_url: https://github.com/davidmashburn/bygone/tree/main
12
+ repo_head_commit_url: https://github.com/davidmashburn/bygone/commit/183156146f54cc35b9304d6b2131e066b88e97dc
13
+ ---
14
+
1
15
  # Bygone
2
16
 
3
17
  Bygone is a VS Code extension and standalone desktop app for visual diff and file history.
@@ -10,19 +24,12 @@ Bygone is a VS Code extension and standalone desktop app for visual diff and fil
10
24
  - Dynamic adjacent multi-panel diff view
11
25
  - Flowing connectors, block contours, and inline change highlighting
12
26
  - Git file history viewer with commit-by-commit navigation
27
+ - Local branch-review workspace using merge-base-to-tip semantics and changed-file progress
28
+ - Binary-aware comparisons with side-by-side image previews and byte-equality reporting
13
29
 
14
30
  ## Status
15
31
 
16
- This project is usable as a local or private pre-release VS Code extension. The two-way diff and git history flows are the most complete. The multi-panel view is diff-focused, not a merge tool.
17
-
18
- ## Migration Note
19
-
20
- This project was renamed from `Melden` to `Bygone`.
21
-
22
- - VS Code extension id changed from `davidmashburn.melden` to `davidmashburn.bygone`
23
- - CLI changed from `melden` to `bygone`
24
- - Git difftool names changed from `melden` / `melden-vscode` to `bygone` / `bygone-vscode`
25
- - Existing users should reinstall the extension and re-run the difftool setup scripts
32
+ This project is usable as a VS Code extension and standalone desktop app. The two-way diff and git history flows are the most complete. The multi-panel view is diff-focused, not a merge tool.
26
33
 
27
34
  ## Install For Development
28
35
 
@@ -58,23 +65,59 @@ npm install -g .
58
65
  bygone
59
66
  bygone path/to/file-or-directory
60
67
  bygone --diff path/to/left path/to/right
68
+ bygone --diff
61
69
  bygone --diff path/to/file1 path/to/file2 path/to/file3 [...]
62
- bygone --dir path/to/left-dir path/to/right-dir
63
- bygone --diff3 path/to/left path/to/middle path/to/right
64
- bygone --dir3 path/to/left-dir path/to/middle-dir path/to/right-dir
65
- bygone --history path/to/file
70
+ bygone --history path/to/path
71
+ bygone review
72
+ bygone review feature/my-branch --base origin/main
73
+ bygone present feature/my-branch --base origin/main
74
+ bygone completion zsh
66
75
  bygone --help
67
76
  ```
68
77
 
69
- CLI defaults:
78
+ For a one-command local sync that builds the repo, installs the global CLI, packages the desktop app, and auto-installs the VSIX and desktop app for your current platform:
70
79
 
71
- - `bygone` opens Git directory history for the current directory.
80
+ ```bash
81
+ npm run dev:sync
82
+ ```
83
+
84
+ CLI defaults:
85
+ - In a Git repo, `bygone` opens directory history for the current directory.
86
+ - Outside a Git repo, `bygone` opens a blank editable diff.
72
87
  - `bygone <file>` opens file history.
73
88
  - `bygone <directory>` opens Git directory history for that directory.
89
+ - `bygone --history <path>` opens file or directory history.
90
+ - `bygone review [<head>] [--base <base>]` reviews the committed branch tip against its merge base. The base is detected from `origin/HEAD`, `main`, or `master` when omitted.
91
+ - `bygone present [<head>] [--base <base>]` opens the same committed range as an app-hosted change tour, grouped into a deterministic suggested reading order.
74
92
  - `bygone <left> <right>` auto-selects file diff or directory compare.
75
- - `bygone --diff <file1> <file2> <file3> ...` opens multi-panel diff.
93
+ - `bygone --diff <file1> <file2> <file3> ...` opens multi-panel diff or multi-directory compare.
94
+
95
+ On macOS, desktop-backed CLI commands and change tours route into the running
96
+ Bygone app instead of opening another app instance and Dock item. Starting a
97
+ new comparison brings the central app window forward and replaces its current
98
+ session after any unsaved-change confirmation. Tours reuse a dedicated window
99
+ owned by that same app process.
100
+
101
+ Shell completion is generated from the same command specification as `bygone --help`, including contextual file arguments and local Git refs for branch review. `npm run dev:sync`, the desktop app's command-line installer, and the Homebrew packages install completions automatically. To install one manually, choose the command for your shell:
102
+
103
+ ```bash
104
+ mkdir -p ~/.zfunc ~/.local/share/bash-completion/completions ~/.config/fish/completions
105
+ bygone completion zsh > ~/.zfunc/_bygone # add ~/.zfunc to fpath before compinit
106
+ bygone completion bash > ~/.local/share/bash-completion/completions/bygone
107
+ bygone completion fish > ~/.config/fish/completions/bygone.fish
108
+ ```
109
+
110
+ If the native desktop app is installed, the npm/source launcher prefers it and forwards the shell working directory. Set `BYGONE_FORCE_BUNDLED=1` to force the npm-bundled Electron runtime instead. The launcher removes `ELECTRON_RUN_AS_NODE` before starting Electron so editor-integrated terminals cannot accidentally force the app into Node mode.
111
+
112
+ Branch review compares the selected head directly with `merge-base(base, head)`, so merge commits are represented correctly in the aggregate diff. Commit metadata retains every parent of merge commits for future temporal review. Dirty index and working-tree changes are reported but never silently included. Detected renames keep distinct old and new paths for correct drill-down and navigation without adding persistent cross-tree connectors.
113
+
114
+ Two-file and directory drill-down comparisons detect binary content before decoding it as text. Images render side by side when they are small enough to preview inline; all binary comparisons report whether the underlying bytes are identical.
115
+
116
+ Change tours can layer an authored, code-connected narrative over the complete generated change set. Pass a `.bygone.yaml` file with `--tour`; named source anchors compile to exact commit and line references, while an independent Files rail keeps every changed file available without adding filler scenes to the narrative. Try the self-referencing example with `bygone present --tour examples/bygone-history.bygone.yaml`, then see the [change tour format](./docs/change-tour-format.md).
117
+
118
+ Agents can validate anchors and structure with `bygone tour validate <file.bygone.yaml> --json`, compile a portable manifest with `bygone tour compile`, and retrieve the authoring contract with `bygone tour schema`. See [Generating change tours with an LLM](./docs/generating-change-tours.md), or give a compatible agent the repository's vendor-neutral [`pr-tour-guide` skill](./skills/pr-tour-guide/SKILL.md).
76
119
 
77
- If the native desktop app is installed, the npm/source launcher prefers it and forwards the shell working directory. Set `BYGONE_FORCE_BUNDLED=1` to force the npm-bundled Electron runtime instead.
120
+ `bygone tour context [<head>] --base <base>` produces a provider-neutral change dossier with bounded patches, changed ranges, file roles, symbol hints, commits, renames, and explicit binary omissions for an LLM to consume.
78
121
 
79
122
  The desktop app also includes `Help -> Install Command Line Tools...` for non-npm installs. Homebrew cask installs can provide the `bygone` command automatically.
80
123
 
@@ -114,7 +157,7 @@ npm run package:npm
114
157
  npm pack --dry-run ./dist/npm-package
115
158
  ```
116
159
 
117
- The root `package.json` is the VS Code extension manifest and intentionally keeps the unscoped extension name `bygone`. The npm distribution is staged separately as `@davidmashburn/bygone` so the global launcher can avoid the already-taken `bygone` package name while preserving the VS Code extension id `davidmashburn.bygone`.
160
+ The root `package.json` is the VS Code extension manifest and intentionally keeps the unscoped extension name `bygone`. The npm distribution is staged separately as `@davmash/bygone` so the global launcher can avoid the already-taken `bygone` package name while preserving the VS Code extension id `davidmashburn.bygone`.
118
161
 
119
162
  Homebrew packaging templates live in `packaging/homebrew/`. They are staged for local validation only until release artifact URLs and checksums exist.
120
163
 
@@ -132,7 +175,7 @@ Publishing is intentionally explicit:
132
175
  npm run release:publish
133
176
  ```
134
177
 
135
- `release:publish` publishes the npm package, publishes the VS Code package, creates a GitHub release for desktop artifacts, and updates a Homebrew tap. Set `BYGONE_HOMEBREW_TAP` to a local tap checkout before publishing Homebrew formulas.
178
+ `release:publish` publishes the npm package, creates a GitHub release for desktop artifacts, and updates a Homebrew tap. Set `BYGONE_HOMEBREW_TAP` to a local tap checkout before publishing Homebrew formulas. Until Marketplace trusted-publisher policies are publicly configurable, upload the packaged VSIX from the Visual Studio Marketplace publisher page.
136
179
 
137
180
  Run the release checks:
138
181
 
@@ -195,7 +238,7 @@ The wrapper scripts are:
195
238
 
196
239
  - `Bygone: Compare Files`
197
240
  - `Bygone: Compare Directories`
198
- - `Bygone: Compare Three Directories (Prototype)`
241
+ - `Bygone: Compare Multiple Directories`
199
242
  - `Bygone: Compare Multiple Files`
200
243
  - `Bygone: Compare with Selected`
201
244
  - `Bygone: Compare Test Files`
@@ -207,7 +250,6 @@ The wrapper scripts are:
207
250
  - Bygone is intentionally diff-focused; merge tooling is not exposed as a product feature.
208
251
  - Directory compare supports modified-file detection and file drill-down; deep tree ergonomics are still early.
209
252
  - The git history viewer currently steps through single-parent commit history for one file at a time.
210
- - Marketplace publishing metadata is not finalized yet.
211
253
 
212
254
  ## Release Work
213
255
 
package/bin/bygone.js CHANGED
@@ -4,57 +4,80 @@ const { spawn } = require('child_process');
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const packageJson = require('../package.json');
7
+ const { generateCompletion, SUPPORTED_SHELLS } = require('../cli/completions.js');
8
+ const { renderCliHelp, tokenMatches } = require('../cli/commandSpec.js');
9
+ const { startPresentation } = require('../cli/present.js');
10
+ const { runTourCommand } = require('../cli/tour.js');
7
11
 
8
12
  const args = process.argv.slice(2);
9
13
  const cliCwd = process.cwd();
14
+ const packageRoot = path.join(__dirname, '..');
10
15
 
11
- if (args.includes('--help') || args.includes('-h')) {
12
- process.stdout.write(`Bygone ${packageJson.version}
13
-
14
- Usage:
15
- bygone
16
- bygone <file-or-directory>
17
- bygone <left> <right>
18
- bygone --diff <left> <right>
19
- bygone --diff <file1> <file2> <file3> [...]
20
- bygone --diff3 <left> <middle> <right>
21
- bygone --dir <left-dir> <right-dir>
22
- bygone --dir3 <left-dir> <middle-dir> <right-dir>
23
- bygone --history <file>
24
- bygone --dir-history <directory>
25
- bygone --test
26
-
27
- Notes:
28
- - No args opens Git directory history for the current directory.
29
- - One positional path opens file history or Git directory history.
30
- - Two positional paths auto-select file diff or directory compare.
31
- - \`--diff\` accepts 2 or more files; 3+ opens multi-panel diff.
32
- - In the standalone app, drop 1 file for history, 2 files/directories for compare, 3+ files for multi-panel diff, or 3 directories for directory compare.
33
- `);
16
+ if (args.some((arg) => tokenMatches('help', arg))) {
17
+ process.stdout.write(renderCliHelp(packageJson.version));
34
18
  process.exit(0);
35
19
  }
36
20
 
37
- if (args.includes('--version') || args.includes('-v')) {
21
+ if (args.some((arg) => tokenMatches('version', arg))) {
38
22
  process.stdout.write(`${packageJson.version}\n`);
39
23
  process.exit(0);
40
24
  }
41
25
 
42
- const packageRoot = path.join(__dirname, '..');
26
+ if (tokenMatches('completion', args[0])) {
27
+ const shell = args[1];
28
+ if (!SUPPORTED_SHELLS.includes(shell)) {
29
+ process.stderr.write(`Usage: bygone completion <${SUPPORTED_SHELLS.join('|')}>\n`);
30
+ process.exit(2);
31
+ }
32
+ process.stdout.write(generateCompletion(shell));
33
+ process.exit(0);
34
+ }
35
+
36
+ if (tokenMatches('tourCommand', args[0])) {
37
+ try {
38
+ runTourCommand(args.slice(1), cliCwd, packageRoot);
39
+ } catch (error) {
40
+ const message = error instanceof Error ? error.message : String(error);
41
+ process.stderr.write(args.includes('--json')
42
+ ? `${JSON.stringify({ ok: false, error: { message } }, null, 2)}\n`
43
+ : `Could not process change tour: ${message}\n`);
44
+ process.exitCode = 1;
45
+ }
46
+ } else if (tokenMatches('present', args[0])) {
47
+ const installedApp = findInstalledApp();
48
+ if (installedApp && process.env.BYGONE_FORCE_BUNDLED !== '1') {
49
+ launchDesktopApp({ waitForExit: false });
50
+ } else {
51
+ startPresentation(args.slice(1), cliCwd, packageRoot).catch((error) => {
52
+ process.stderr.write(`Could not start change tour: ${error instanceof Error ? error.message : String(error)}\n`);
53
+ process.exitCode = 1;
54
+ });
55
+ }
56
+ } else {
57
+ launchDesktopApp();
58
+ }
59
+
60
+ function launchDesktopApp({ waitForExit = true } = {}) {
43
61
  const electronBinary = process.platform === 'win32'
44
62
  ? '.\\node_modules\\.bin\\electron.cmd'
45
63
  : './node_modules/.bin/electron';
46
64
  const appEntry = './out/standalone-main.js';
47
65
  const forwardedArgs = ['--cwd', cliCwd, ...args];
48
66
  const installedApp = findInstalledApp();
49
- const preferBundledApp = fs.existsSync(path.join(packageRoot, '.git'));
50
67
 
51
68
  const child = installedApp && process.env.BYGONE_FORCE_BUNDLED !== '1'
52
- && !preferBundledApp
53
- ? spawnInstalledApp(installedApp, forwardedArgs)
69
+ ? spawnInstalledApp(installedApp, forwardedArgs, { waitForExit })
54
70
  : spawn(electronBinary, [appEntry, ...forwardedArgs], {
55
- cwd: packageRoot,
56
- stdio: 'inherit'
57
- });
71
+ cwd: packageRoot,
72
+ detached: !waitForExit,
73
+ stdio: waitForExit ? 'inherit' : 'ignore',
74
+ env: electronEnvironment()
75
+ });
76
+
77
+ if (!waitForExit) {
78
+ child.unref();
79
+ return;
80
+ }
58
81
 
59
82
  child.on('exit', (code, signal) => {
60
83
  if (signal) {
@@ -64,6 +87,7 @@ child.on('exit', (code, signal) => {
64
87
 
65
88
  process.exit(code ?? 0);
66
89
  });
90
+ }
67
91
 
68
92
  function findInstalledApp() {
69
93
  if (process.platform === 'darwin') {
@@ -99,14 +123,24 @@ function findInstalledApp() {
99
123
  return candidates.find((candidate) => fs.existsSync(candidate));
100
124
  }
101
125
 
102
- function spawnInstalledApp(installedApp, launchArgs) {
126
+ function spawnInstalledApp(installedApp, launchArgs, { waitForExit = true } = {}) {
127
+ const spawnOptions = {
128
+ detached: !waitForExit,
129
+ stdio: waitForExit ? 'inherit' : 'ignore',
130
+ env: electronEnvironment()
131
+ };
132
+
103
133
  if (process.platform === 'darwin') {
104
- return spawn('open', ['-W', installedApp, '--args', ...launchArgs], {
105
- stdio: 'inherit'
106
- });
134
+ const executableName = path.basename(installedApp, '.app');
135
+ const executablePath = path.join(installedApp, 'Contents', 'MacOS', executableName);
136
+ return spawn(executablePath, launchArgs, spawnOptions);
107
137
  }
108
138
 
109
- return spawn(installedApp, launchArgs, {
110
- stdio: 'inherit'
111
- });
139
+ return spawn(installedApp, launchArgs, spawnOptions);
140
+ }
141
+
142
+ function electronEnvironment() {
143
+ const environment = { ...process.env };
144
+ delete environment.ELECTRON_RUN_AS_NODE;
145
+ return environment;
112
146
  }
@@ -0,0 +1,178 @@
1
+ const CLI_SPEC = Object.freeze({
2
+ name: 'bygone',
3
+ summary: 'Visual diff and file history',
4
+ usages: [
5
+ 'bygone',
6
+ 'bygone <file-or-directory>',
7
+ 'bygone <left> <right>',
8
+ 'bygone --diff <left> <right>',
9
+ 'bygone --diff',
10
+ 'bygone --diff <file1> <file2> <file3> [...]',
11
+ 'bygone --history <path>',
12
+ 'bygone --git-diff <ref1> <ref2> [<ref3>...]',
13
+ 'bygone review [<head>] [--base <base>]',
14
+ 'bygone present [<head>] [--base <base>] [--tour <file.bygone.yaml>]',
15
+ 'bygone tour validate <file.bygone.yaml> [--json]',
16
+ 'bygone tour compile <file.bygone.yaml> [--output <tour.json>]',
17
+ 'bygone tour context [<head>] [--base <base>] [--output <context.json>]',
18
+ 'bygone tour coverage <file.bygone.yaml> [--json] [--minimum-coverage <0-100>]',
19
+ 'bygone tour schema',
20
+ 'bygone --branch-diff [-b BRANCH] [-m MAIN]',
21
+ 'bygone completion <zsh|bash|fish>',
22
+ 'bygone --test'
23
+ ],
24
+ entries: [
25
+ {
26
+ id: 'review',
27
+ kind: 'command',
28
+ tokens: ['review'],
29
+ description: 'Review a committed branch against its merge base',
30
+ argument: 'git-ref'
31
+ },
32
+ {
33
+ id: 'completion',
34
+ kind: 'command',
35
+ tokens: ['completion'],
36
+ description: 'Print shell completion source',
37
+ argument: 'shell'
38
+ },
39
+ {
40
+ id: 'present',
41
+ kind: 'command',
42
+ tokens: ['present'],
43
+ description: 'Open an app-hosted tour of a committed branch range',
44
+ argument: 'git-ref'
45
+ },
46
+ {
47
+ id: 'tourCommand',
48
+ kind: 'command',
49
+ tokens: ['tour'],
50
+ description: 'Validate, compile, or inspect authored change tours',
51
+ argument: 'tour-action'
52
+ },
53
+ {
54
+ id: 'diff',
55
+ kind: 'mode',
56
+ tokens: ['--diff'],
57
+ description: 'Compare files, or open a blank editable diff',
58
+ argument: 'files'
59
+ },
60
+ {
61
+ id: 'history',
62
+ kind: 'mode',
63
+ tokens: ['--history'],
64
+ description: 'Open Git history for a file or directory',
65
+ argument: 'path'
66
+ },
67
+ {
68
+ id: 'gitDiff',
69
+ kind: 'mode',
70
+ tokens: ['--git-diff'],
71
+ description: 'Compare two or more Git sources',
72
+ argument: 'git-refs'
73
+ },
74
+ {
75
+ id: 'branchDiff',
76
+ kind: 'mode',
77
+ tokens: ['--branch-diff'],
78
+ description: 'Legacy alias for branch review',
79
+ argument: 'git-ref'
80
+ },
81
+ {
82
+ id: 'test',
83
+ kind: 'mode',
84
+ tokens: ['--test'],
85
+ description: 'Open the built-in test comparison',
86
+ argument: 'none'
87
+ },
88
+ {
89
+ id: 'includeStaged',
90
+ kind: 'option',
91
+ tokens: ['--include-staged', '--staged'],
92
+ description: 'Include staged state in Git history',
93
+ argument: 'none'
94
+ },
95
+ {
96
+ id: 'base',
97
+ kind: 'option',
98
+ tokens: ['--base', '-m', '--main'],
99
+ description: 'Set the branch-review base ref',
100
+ argument: 'git-ref'
101
+ },
102
+ {
103
+ id: 'tour',
104
+ kind: 'option',
105
+ tokens: ['--tour'],
106
+ description: 'Use an authored change-tour YAML file',
107
+ argument: 'path'
108
+ },
109
+ {
110
+ id: 'branch',
111
+ kind: 'option',
112
+ tokens: ['-b', '--branch'],
113
+ description: 'Set the branch-review head ref',
114
+ argument: 'git-ref'
115
+ },
116
+ {
117
+ id: 'help',
118
+ kind: 'global',
119
+ tokens: ['-h', '--help'],
120
+ description: 'Show command help',
121
+ argument: 'none'
122
+ },
123
+ {
124
+ id: 'version',
125
+ kind: 'global',
126
+ tokens: ['-v', '--version'],
127
+ description: 'Show the installed version',
128
+ argument: 'none'
129
+ }
130
+ ],
131
+ notes: [
132
+ 'No args opens Git directory history inside a Git repo, or a blank editable diff outside one.',
133
+ '`--diff` with no paths opens a blank editable diff.',
134
+ 'One positional path opens file history or Git directory history.',
135
+ '`--history` accepts either files or directories.',
136
+ 'Two positional paths auto-select file diff or directory compare.',
137
+ 'Three or more positional paths auto-select multi-panel file diff or multi-directory compare.',
138
+ '`--git-diff` accepts branches, tags, SHAs, HEAD~1, stash@{0}, INDEX, and WORKTREE.',
139
+ '`review` compares merge-base(BASE,HEAD) with HEAD and detects the default base when omitted.',
140
+ '`present` turns the same range into an app-hosted, ordered change tour.',
141
+ '`tour validate` resolves every authored anchor; add `--json` for agent-readable output.',
142
+ '`tour compile` writes a portable manifest to stdout or `--output`; `tour schema` prints its source schema.',
143
+ '`tour context` emits compact, structured Git evidence for an LLM without invoking a model.',
144
+ '`tour coverage` reports referenced changed hunks and author-declared explanation depth.',
145
+ '`--branch-diff` is retained as an alias for `review`.',
146
+ '`completion` prints a completion script for Zsh, Bash, or Fish.',
147
+ 'In the standalone app, drop 1 file for history, 2 files/directories for compare, or 3+ matching paths for multi-panel compare.'
148
+ ]
149
+ });
150
+
151
+ function getCliEntry(id) {
152
+ return CLI_SPEC.entries.find((entry) => entry.id === id);
153
+ }
154
+
155
+ function tokensFor(id) {
156
+ return getCliEntry(id)?.tokens ?? [];
157
+ }
158
+
159
+ function tokenMatches(id, token) {
160
+ return tokensFor(id).includes(token);
161
+ }
162
+
163
+ function renderCliHelp(version) {
164
+ const usage = CLI_SPEC.usages.map((line) => ` ${line}`).join('\n');
165
+ const entries = CLI_SPEC.entries.map((entry) => (
166
+ ` ${entry.tokens.join(', ')}\n ${entry.description}`
167
+ )).join('\n');
168
+ const notes = CLI_SPEC.notes.map((line) => ` - ${line}`).join('\n');
169
+ return `Bygone ${version}\n\nUsage:\n${usage}\n\nCommands and options:\n${entries}\n\nNotes:\n${notes}\n`;
170
+ }
171
+
172
+ module.exports = {
173
+ CLI_SPEC,
174
+ getCliEntry,
175
+ renderCliHelp,
176
+ tokenMatches,
177
+ tokensFor
178
+ };