Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,32 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [2.2.1] - 2026-06-10

### Added

- **`.links()` — OSC 8 hyperlinks in the root-help header** — the program name and version on the
first line of root `--help` output can now carry
[OSC 8 hyperlinks](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) in
supporting terminals ([#20](https://github.com/kjanat/dreamcli/issues/20)). Pass explicit URLs
(`.links({ name, version })`) or call `.links()` with no arguments to derive them from
`package.json` metadata when `.packageJson()` is active: the name links to the normalized
`repository` URL (falling back to `homepage`), and the version links to the forge release tag
(`{repo}/releases/tag/v{version}` on GitHub, `{repo}/-/releases/v{version}` on GitLab). Escapes
are emitted only when stdout is a TTY (overridable via the new `help.hyperlinks` option) and only
on the header line — usage lines, the `--help` hint, the commands table, `--version` output, and
completion scripts stay plain.
- **ANSI/OSC-aware help width helpers** — help padding and wrapping now measure _visible_ width:
the shared `padEnd()`/`wrapText()` helpers strip ANSI CSI (colors) and OSC (hyperlink) escape
sequences before counting columns, so escape-bearing text no longer mangles `--help` alignment.
New public exports `osc8(url, text)` (wrap text in an OSC 8 hyperlink) and `visibleWidth(text)`
(escape-aware width measurement) support custom help rendering.
- **`packageRepositoryUrl(pkg)`** — new public helper that normalizes a package's `repository`
field to a browsable `https://` URL, handling the locator formats npm accepts (`{ type, url }`
object form, `git+`/`.git` affixes, scp-style `git@host:u/r.git`, and the
`github:`/`gitlab:`/`bitbucket:`/bare `u/r` shorthands). `PackageJsonData` now also parses the
`homepage` and `repository` fields.

## [2.2.0] - 2026-06-09

### Added
Expand Down Expand Up @@ -764,7 +790,8 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
- MIT License.
- Markdownlint configuration.

[Unreleased]: https://github.com/kjanat/dreamcli/compare/v2.2.0...HEAD
[Unreleased]: https://github.com/kjanat/dreamcli/compare/v2.2.1...HEAD
[2.2.1]: https://github.com/kjanat/dreamcli/compare/v2.2.0...v2.2.1
[2.2.0]: https://github.com/kjanat/dreamcli/compare/v2.1.0...v2.2.0
[2.1.0]: https://github.com/kjanat/dreamcli/compare/v2.0.1...v2.1.0
[2.0.1]: https://github.com/kjanat/dreamcli/compare/v2.0.0...v2.0.1
Expand Down
2 changes: 1 addition & 1 deletion deno.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"$schema": "https://raw.githubusercontent.com/denoland/deno/main/cli/schemas/config-file.v1.json",
"name": "@kjanat/dreamcli",
"version": "2.2.0",
"version": "2.2.1",
"license": "MIT",
"tasks": {
"check": {
Expand Down
74 changes: 74 additions & 0 deletions docs/reference/main.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,39 @@ const deploy = command('deploy');
cli('mycli').packageJson(pkg).command(deploy).run();
```

### `.links(links?)`

Make the root-help header clickable with [OSC 8
hyperlinks](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda). The program name and
version on the first line of root `--help` output become links in supporting terminals. Escapes are
only emitted when stdout is a TTY (override with the `help.hyperlinks` run option), and only on the
header line — usage lines, the `--help` hint, the commands table, and completion scripts stay plain.

URLs not provided are derived from `package.json` metadata when `.packageJson()` is active (both the
discovery and pre-loaded data forms): the name links to the normalized `repository` URL (falling back
to `homepage`), and the version links to the forge release tag (`{repo}/releases/tag/v{version}` on
GitHub, `{repo}/-/releases/v{version}` on GitLab).

```ts twoslash
import { cli, command } from '@kjanat/dreamcli';

const deploy = command('deploy');

// Derive both links from package.json repository/homepage:
cli('mycli').packageJson().links().command(deploy).run();

// Explicit URLs (no package.json required):
cli('mycli')
.version('1.0.0')
.links({
name: 'https://github.com/me/mycli',
version:
'https://github.com/me/mycli/releases/tag/v1.0.0',
})
.command(deploy)
.run();
```

### `.plugin(definition)`

Register a CLI plugin created with `plugin(...)`. Plugins run in registration order and can observe
Expand Down Expand Up @@ -478,6 +511,47 @@ inferCliName({ bin: { mycli: './dist/cli.js' } }); // 'mycli'
inferCliName({ name: '@scope/mycli' }); // 'mycli'
```

### `packageRepositoryUrl(pkg)`

Resolve a package's `repository` field to a browsable `https://` URL. Handles the locator formats
npm accepts — the `{ type, url }` object form, `git+`-prefixed and `.git`-suffixed URLs, scp-style
locators (`git@host:u/r.git`), and the `github:`/`gitlab:`/`bitbucket:`/bare `u/r` shorthands.
Returns `undefined` when the field is absent or unrecognised. Used by `.links()` to derive the
header name link.

```ts twoslash
import { packageRepositoryUrl } from '@kjanat/dreamcli';

packageRepositoryUrl({
repository: 'git+https://github.com/me/mycli.git',
});
// 'https://github.com/me/mycli'
packageRepositoryUrl({ repository: 'github:me/mycli' });
// 'https://github.com/me/mycli'
```

## Help

### `osc8(url, text)`

Wrap `text` in an [OSC 8
hyperlink](https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda) pointing at `url`
(string or `URL`). Supporting terminals render clickable text; others ignore the escapes. Useful for
linking arbitrary help strings, e.g. `.version(osc8(releaseUrl, '1.0.0'))`.

### `visibleWidth(text)`

Measure the visible column width of `text`, ignoring ANSI CSI (colors) and OSC (hyperlink) escape
sequences. Help formatting uses this internally for padding and wrapping, so colored or linked text
no longer breaks table alignment.

```ts twoslash
import { osc8, visibleWidth } from '@kjanat/dreamcli';

const link = osc8('https://github.com/me/mycli', 'mycli');
visibleWidth(link); // 5
```

## Errors

### `CLIError`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@kjanat/dreamcli",
"version": "2.2.0",
"version": "2.2.1",
"description": "Schema-first, fully typed TypeScript CLI framework",
"keywords": [
"cli",
Expand Down
Loading
Loading