@optique/core 1.4.0-dev.2897 → 1.4.0-dev.2928

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.
@@ -2,6 +2,108 @@ const require_usage = require('./usage.cjs');
2
2
 
3
3
  //#region src/usage-internals.ts
4
4
  /**
5
+ * Projects augmented runner usage onto a recorded canonical command path.
6
+ * Unsupported or ambiguous metadata returns undefined, preserving root usage.
7
+ * No parser, documentation, or usage-override callbacks are evaluated.
8
+ * @param usage The runner's original augmented usage.
9
+ * @param path The canonical path recorded by the failed parse.
10
+ * @param meta The known augmentation boundary, absent without alternatives.
11
+ * @returns The selected grammar, or undefined when projection is uncertain.
12
+ * @internal
13
+ */
14
+ function projectCommandUsage(usage, path, meta) {
15
+ if (path.length === 0) return void 0;
16
+ if (meta == null) return projectCommandTerms(usage, path, 0);
17
+ const top = usage[0];
18
+ if (usage.length !== 1 || top.type !== "exclusive" || top.terms.length !== meta.alternativeCount + 1) return void 0;
19
+ const selected = projectCommandTerms(top.terms[meta.optionCount], path, 0);
20
+ if (selected == null) return void 0;
21
+ const options = top.terms.slice(0, meta.optionCount).filter((branch) => branch.some((term) => term.type === "option" && !require_usage.isUsageHidden(term.hidden)));
22
+ return options.length === 0 ? selected : [{
23
+ type: "exclusive",
24
+ terms: [...options, selected]
25
+ }];
26
+ }
27
+ function containsCommand(usage) {
28
+ return usage.some((term) => {
29
+ switch (term.type) {
30
+ case "command": return true;
31
+ case "exclusive": return term.terms.some(containsCommand);
32
+ case "optional":
33
+ case "multiple":
34
+ case "sequence": return containsCommand(term.terms);
35
+ default: return false;
36
+ }
37
+ });
38
+ }
39
+ /** Undefined distinguishes unsupported metadata from a command-free branch. */
40
+ function firstCommandNames(usage) {
41
+ const hosts = usage.filter((term) => containsCommand([term]));
42
+ const first = hosts[0];
43
+ if (first == null) return [];
44
+ if (first.type === "command") return [first.name];
45
+ if (hosts.length !== 1) return void 0;
46
+ if (first.type === "sequence") return firstCommandNames(first.terms);
47
+ if (first.type !== "exclusive") return void 0;
48
+ const names = [];
49
+ for (const branch of first.terms) {
50
+ const found = firstCommandNames(branch);
51
+ if (found == null) return void 0;
52
+ names.push(...found);
53
+ }
54
+ return names;
55
+ }
56
+ function projectCommandTerms(usage, path, offset) {
57
+ if (offset === path.length) return usage;
58
+ const index = usage.findIndex((term) => containsCommand([term]));
59
+ if (index < 0) return void 0;
60
+ const host = usage[index];
61
+ const prefix = usage.slice(0, index);
62
+ const suffix = usage.slice(index + 1);
63
+ const orderedPrefix = prefix.some((term) => term.type === "option" || term.type === "argument" || term.type === "literal");
64
+ const preserveOrder = (terms$1) => orderedPrefix ? [{
65
+ type: "sequence",
66
+ terms: terms$1
67
+ }] : terms$1;
68
+ if (host.type === "command") {
69
+ if (host.name !== path[offset] || require_usage.isUsageHidden(host.hidden)) return void 0;
70
+ const rest = projectCommandTerms(suffix, path, offset + 1);
71
+ return rest == null ? void 0 : preserveOrder([
72
+ ...prefix,
73
+ host,
74
+ ...rest
75
+ ]);
76
+ }
77
+ if (containsCommand(suffix)) return void 0;
78
+ if (host.type === "sequence") {
79
+ const terms$1 = projectCommandTerms(host.terms, path, offset);
80
+ return terms$1 == null ? void 0 : preserveOrder([
81
+ ...prefix,
82
+ {
83
+ ...host,
84
+ terms: terms$1
85
+ },
86
+ ...suffix
87
+ ]);
88
+ }
89
+ if (host.type !== "exclusive") return void 0;
90
+ let selected;
91
+ for (const branch of host.terms) {
92
+ const names = firstCommandNames(branch);
93
+ if (names == null) return void 0;
94
+ if (!names.includes(path[offset])) continue;
95
+ if (selected != null) return void 0;
96
+ selected = branch;
97
+ }
98
+ if (selected == null) return void 0;
99
+ const terms = projectCommandTerms(selected, path, offset);
100
+ return terms == null ? void 0 : preserveOrder([
101
+ ...prefix,
102
+ ...terms,
103
+ ...suffix
104
+ ]);
105
+ }
106
+ /**
5
107
  * Collects option names and command names that are valid as the *immediate*
6
108
  * next token at the current parse position ("leading candidates").
7
109
  *
@@ -152,4 +254,5 @@ exports.collectActiveOptionNames = collectActiveOptionNames;
152
254
  exports.collectLeadingCandidates = collectLeadingCandidates;
153
255
  exports.collectOptionNamesAtCurrentCommandDepth = collectOptionNamesAtCurrentCommandDepth;
154
256
  exports.collectRootOptionNames = collectRootOptionNames;
155
- exports.extractLeadingCommandNames = extractLeadingCommandNames;
257
+ exports.extractLeadingCommandNames = extractLeadingCommandNames;
258
+ exports.projectCommandUsage = projectCommandUsage;
@@ -1,7 +1,109 @@
1
- import { isSuggestionHidden } from "./usage.js";
1
+ import { isSuggestionHidden, isUsageHidden } from "./usage.js";
2
2
 
3
3
  //#region src/usage-internals.ts
4
4
  /**
5
+ * Projects augmented runner usage onto a recorded canonical command path.
6
+ * Unsupported or ambiguous metadata returns undefined, preserving root usage.
7
+ * No parser, documentation, or usage-override callbacks are evaluated.
8
+ * @param usage The runner's original augmented usage.
9
+ * @param path The canonical path recorded by the failed parse.
10
+ * @param meta The known augmentation boundary, absent without alternatives.
11
+ * @returns The selected grammar, or undefined when projection is uncertain.
12
+ * @internal
13
+ */
14
+ function projectCommandUsage(usage, path, meta) {
15
+ if (path.length === 0) return void 0;
16
+ if (meta == null) return projectCommandTerms(usage, path, 0);
17
+ const top = usage[0];
18
+ if (usage.length !== 1 || top.type !== "exclusive" || top.terms.length !== meta.alternativeCount + 1) return void 0;
19
+ const selected = projectCommandTerms(top.terms[meta.optionCount], path, 0);
20
+ if (selected == null) return void 0;
21
+ const options = top.terms.slice(0, meta.optionCount).filter((branch) => branch.some((term) => term.type === "option" && !isUsageHidden(term.hidden)));
22
+ return options.length === 0 ? selected : [{
23
+ type: "exclusive",
24
+ terms: [...options, selected]
25
+ }];
26
+ }
27
+ function containsCommand(usage) {
28
+ return usage.some((term) => {
29
+ switch (term.type) {
30
+ case "command": return true;
31
+ case "exclusive": return term.terms.some(containsCommand);
32
+ case "optional":
33
+ case "multiple":
34
+ case "sequence": return containsCommand(term.terms);
35
+ default: return false;
36
+ }
37
+ });
38
+ }
39
+ /** Undefined distinguishes unsupported metadata from a command-free branch. */
40
+ function firstCommandNames(usage) {
41
+ const hosts = usage.filter((term) => containsCommand([term]));
42
+ const first = hosts[0];
43
+ if (first == null) return [];
44
+ if (first.type === "command") return [first.name];
45
+ if (hosts.length !== 1) return void 0;
46
+ if (first.type === "sequence") return firstCommandNames(first.terms);
47
+ if (first.type !== "exclusive") return void 0;
48
+ const names = [];
49
+ for (const branch of first.terms) {
50
+ const found = firstCommandNames(branch);
51
+ if (found == null) return void 0;
52
+ names.push(...found);
53
+ }
54
+ return names;
55
+ }
56
+ function projectCommandTerms(usage, path, offset) {
57
+ if (offset === path.length) return usage;
58
+ const index = usage.findIndex((term) => containsCommand([term]));
59
+ if (index < 0) return void 0;
60
+ const host = usage[index];
61
+ const prefix = usage.slice(0, index);
62
+ const suffix = usage.slice(index + 1);
63
+ const orderedPrefix = prefix.some((term) => term.type === "option" || term.type === "argument" || term.type === "literal");
64
+ const preserveOrder = (terms$1) => orderedPrefix ? [{
65
+ type: "sequence",
66
+ terms: terms$1
67
+ }] : terms$1;
68
+ if (host.type === "command") {
69
+ if (host.name !== path[offset] || isUsageHidden(host.hidden)) return void 0;
70
+ const rest = projectCommandTerms(suffix, path, offset + 1);
71
+ return rest == null ? void 0 : preserveOrder([
72
+ ...prefix,
73
+ host,
74
+ ...rest
75
+ ]);
76
+ }
77
+ if (containsCommand(suffix)) return void 0;
78
+ if (host.type === "sequence") {
79
+ const terms$1 = projectCommandTerms(host.terms, path, offset);
80
+ return terms$1 == null ? void 0 : preserveOrder([
81
+ ...prefix,
82
+ {
83
+ ...host,
84
+ terms: terms$1
85
+ },
86
+ ...suffix
87
+ ]);
88
+ }
89
+ if (host.type !== "exclusive") return void 0;
90
+ let selected;
91
+ for (const branch of host.terms) {
92
+ const names = firstCommandNames(branch);
93
+ if (names == null) return void 0;
94
+ if (!names.includes(path[offset])) continue;
95
+ if (selected != null) return void 0;
96
+ selected = branch;
97
+ }
98
+ if (selected == null) return void 0;
99
+ const terms = projectCommandTerms(selected, path, offset);
100
+ return terms == null ? void 0 : preserveOrder([
101
+ ...prefix,
102
+ ...terms,
103
+ ...suffix
104
+ ]);
105
+ }
106
+ /**
5
107
  * Collects option names and command names that are valid as the *immediate*
6
108
  * next token at the current parse position ("leading candidates").
7
109
  *
@@ -148,4 +250,4 @@ function collectRootOptionNamesFromTerm(term, names, rootLeadingNames) {
148
250
  }
149
251
 
150
252
  //#endregion
151
- export { collectActiveOptionNames, collectLeadingCandidates, collectOptionNamesAtCurrentCommandDepth, collectRootOptionNames, extractLeadingCommandNames };
253
+ export { collectActiveOptionNames, collectLeadingCandidates, collectOptionNamesAtCurrentCommandDepth, collectRootOptionNames, extractLeadingCommandNames, projectCommandUsage };
@@ -1,6 +1,7 @@
1
1
  const require_message = require('./message.cjs');
2
2
  const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
3
3
  const require_internal_dependency = require('./internal/dependency.cjs');
4
+ const require_internal_doc_metadata = require('./internal/doc-metadata.cjs');
4
5
  const require_suggestion = require('./suggestion.cjs');
5
6
  const require_nonempty = require('./nonempty.cjs');
6
7
 
@@ -92,7 +93,7 @@ function choice(choices, options = {}) {
92
93
  const numberInvalidChoice = options.errors?.invalidChoice;
93
94
  const numberStrings = numberChoices.map((v) => Object.is(v, -0) ? "-0" : String(v));
94
95
  const frozenNumberChoices = Object.freeze(numberChoices);
95
- return {
96
+ return require_internal_doc_metadata.captureDocValue({
96
97
  mode: "sync",
97
98
  metavar: metavar$1,
98
99
  placeholder: choices[0],
@@ -156,7 +157,12 @@ function choice(choices, options = {}) {
156
157
  text: valueStr
157
158
  }));
158
159
  }
159
- };
160
+ }, {
161
+ kind: "number",
162
+ choices: numberChoices,
163
+ preserveNegativeZero: true,
164
+ canFormatDefault: true
165
+ });
160
166
  }
161
167
  const stringChoices = Object.freeze([...new Set(choices)]);
162
168
  const stringOptions = options;
@@ -203,7 +209,7 @@ function choice(choices, options = {}) {
203
209
  maxDistance: stringSuggest.maxDistance,
204
210
  maxSuggestions: stringSuggest.maxSuggestions
205
211
  } : stringSuggest;
206
- return {
212
+ return require_internal_doc_metadata.captureDocValue({
207
213
  mode: "sync",
208
214
  metavar: metavar$1,
209
215
  placeholder: choices[0],
@@ -240,7 +246,11 @@ function choice(choices, options = {}) {
240
246
  text: value
241
247
  }));
242
248
  }
243
- };
249
+ }, {
250
+ kind: "string",
251
+ choices: stringChoices,
252
+ canFormatDefault: customKey === void 0 && !caseInsensitive
253
+ });
244
254
  }
245
255
  function biject(mapping) {
246
256
  if (mapping === null || typeof mapping !== "object") throw new TypeError("Expected object.");
@@ -723,7 +733,7 @@ function string(options = {}) {
723
733
  const patternSource = options.pattern?.source ?? null;
724
734
  const patternFlags = options.pattern?.flags ?? null;
725
735
  const patternMismatch = options.errors?.patternMismatch;
726
- return {
736
+ return require_internal_doc_metadata.captureDocValue({
727
737
  mode: "sync",
728
738
  metavar: metavar$1,
729
739
  placeholder: options.placeholder ?? "",
@@ -747,7 +757,7 @@ function string(options = {}) {
747
757
  format(value) {
748
758
  return value;
749
759
  }
750
- };
760
+ }, { kind: "string" });
751
761
  }
752
762
  /**
753
763
  * Creates a {@link ValueParser} that compiles regular expression sources.
@@ -1179,7 +1189,7 @@ function integer(options) {
1179
1189
  if (options?.type === "bigint") {
1180
1190
  const metavar$2 = options.metavar ?? "INTEGER";
1181
1191
  require_nonempty.ensureNonEmptyString(metavar$2);
1182
- return {
1192
+ return require_internal_doc_metadata.captureDocValue({
1183
1193
  mode: "sync",
1184
1194
  metavar: metavar$2,
1185
1195
  placeholder: options?.placeholder ?? (options?.min != null && options.min > 0n ? options.min : options?.max != null && options.max < 0n ? options.max : 0n),
@@ -1205,7 +1215,7 @@ function integer(options) {
1205
1215
  format(value) {
1206
1216
  return value.toString();
1207
1217
  }
1208
- };
1218
+ }, { kind: "bigint" });
1209
1219
  }
1210
1220
  const metavar$1 = options?.metavar ?? "INTEGER";
1211
1221
  require_nonempty.ensureNonEmptyString(metavar$1);
@@ -1223,7 +1233,7 @@ function integer(options) {
1223
1233
  error: unsafeIntegerError ? typeof unsafeIntegerError === "function" ? unsafeIntegerError(input) : unsafeIntegerError : require_message.message`Expected a safe integer between ${require_message.text(Number.MIN_SAFE_INTEGER.toLocaleString("en"))} and ${require_message.text(Number.MAX_SAFE_INTEGER.toLocaleString("en"))}, but got ${input}. Use type: "bigint" for large values.`
1224
1234
  };
1225
1235
  }
1226
- return {
1236
+ return require_internal_doc_metadata.captureDocValue({
1227
1237
  mode: "sync",
1228
1238
  metavar: metavar$1,
1229
1239
  placeholder: options?.placeholder ?? (firstAllowed > 0 ? firstAllowed : lastAllowed < 0 ? lastAllowed : 0),
@@ -1256,7 +1266,7 @@ function integer(options) {
1256
1266
  format(value) {
1257
1267
  return value.toString();
1258
1268
  }
1259
- };
1269
+ }, { kind: "number" });
1260
1270
  }
1261
1271
  /**
1262
1272
  * Creates a {@link ValueParser} for floating-point numbers.
@@ -1,6 +1,7 @@
1
1
  import { cloneMessage, lineBreak, message, metavar, text, valueSet } from "./message.js";
2
2
  import { mapMaybePromiseByMode, wrapForMode, wrapIterableForMode } from "./internal/mode-dispatch.js";
3
3
  import { defaultValues, dependencyId, dependencyIds, derivedValueParserMarker, getSnapshottedDefaultDependencyValues, isDependencySource, isDerivedValueParser, parseWithDependency, singleDefaultValue, snapshotDefaultDependencyValues, suggestWithDependency } from "./internal/dependency.js";
4
+ import { captureDocValue } from "./internal/doc-metadata.js";
4
5
  import { appendValueHint, appendValueSuggestions, deduplicateSuggestions } from "./suggestion.js";
5
6
  import { ensureNonEmptyString, isNonEmptyString } from "./nonempty.js";
6
7
 
@@ -92,7 +93,7 @@ function choice(choices, options = {}) {
92
93
  const numberInvalidChoice = options.errors?.invalidChoice;
93
94
  const numberStrings = numberChoices.map((v) => Object.is(v, -0) ? "-0" : String(v));
94
95
  const frozenNumberChoices = Object.freeze(numberChoices);
95
- return {
96
+ return captureDocValue({
96
97
  mode: "sync",
97
98
  metavar: metavar$1,
98
99
  placeholder: choices[0],
@@ -156,7 +157,12 @@ function choice(choices, options = {}) {
156
157
  text: valueStr
157
158
  }));
158
159
  }
159
- };
160
+ }, {
161
+ kind: "number",
162
+ choices: numberChoices,
163
+ preserveNegativeZero: true,
164
+ canFormatDefault: true
165
+ });
160
166
  }
161
167
  const stringChoices = Object.freeze([...new Set(choices)]);
162
168
  const stringOptions = options;
@@ -203,7 +209,7 @@ function choice(choices, options = {}) {
203
209
  maxDistance: stringSuggest.maxDistance,
204
210
  maxSuggestions: stringSuggest.maxSuggestions
205
211
  } : stringSuggest;
206
- return {
212
+ return captureDocValue({
207
213
  mode: "sync",
208
214
  metavar: metavar$1,
209
215
  placeholder: choices[0],
@@ -240,7 +246,11 @@ function choice(choices, options = {}) {
240
246
  text: value
241
247
  }));
242
248
  }
243
- };
249
+ }, {
250
+ kind: "string",
251
+ choices: stringChoices,
252
+ canFormatDefault: customKey === void 0 && !caseInsensitive
253
+ });
244
254
  }
245
255
  function biject(mapping) {
246
256
  if (mapping === null || typeof mapping !== "object") throw new TypeError("Expected object.");
@@ -723,7 +733,7 @@ function string(options = {}) {
723
733
  const patternSource = options.pattern?.source ?? null;
724
734
  const patternFlags = options.pattern?.flags ?? null;
725
735
  const patternMismatch = options.errors?.patternMismatch;
726
- return {
736
+ return captureDocValue({
727
737
  mode: "sync",
728
738
  metavar: metavar$1,
729
739
  placeholder: options.placeholder ?? "",
@@ -747,7 +757,7 @@ function string(options = {}) {
747
757
  format(value) {
748
758
  return value;
749
759
  }
750
- };
760
+ }, { kind: "string" });
751
761
  }
752
762
  /**
753
763
  * Creates a {@link ValueParser} that compiles regular expression sources.
@@ -1179,7 +1189,7 @@ function integer(options) {
1179
1189
  if (options?.type === "bigint") {
1180
1190
  const metavar$2 = options.metavar ?? "INTEGER";
1181
1191
  ensureNonEmptyString(metavar$2);
1182
- return {
1192
+ return captureDocValue({
1183
1193
  mode: "sync",
1184
1194
  metavar: metavar$2,
1185
1195
  placeholder: options?.placeholder ?? (options?.min != null && options.min > 0n ? options.min : options?.max != null && options.max < 0n ? options.max : 0n),
@@ -1205,7 +1215,7 @@ function integer(options) {
1205
1215
  format(value) {
1206
1216
  return value.toString();
1207
1217
  }
1208
- };
1218
+ }, { kind: "bigint" });
1209
1219
  }
1210
1220
  const metavar$1 = options?.metavar ?? "INTEGER";
1211
1221
  ensureNonEmptyString(metavar$1);
@@ -1223,7 +1233,7 @@ function integer(options) {
1223
1233
  error: unsafeIntegerError ? typeof unsafeIntegerError === "function" ? unsafeIntegerError(input) : unsafeIntegerError : message`Expected a safe integer between ${text(Number.MIN_SAFE_INTEGER.toLocaleString("en"))} and ${text(Number.MAX_SAFE_INTEGER.toLocaleString("en"))}, but got ${input}. Use type: "bigint" for large values.`
1224
1234
  };
1225
1235
  }
1226
- return {
1236
+ return captureDocValue({
1227
1237
  mode: "sync",
1228
1238
  metavar: metavar$1,
1229
1239
  placeholder: options?.placeholder ?? (firstAllowed > 0 ? firstAllowed : lastAllowed < 0 ? lastAllowed : 0),
@@ -1256,7 +1266,7 @@ function integer(options) {
1256
1266
  format(value) {
1257
1267
  return value.toString();
1258
1268
  }
1259
- };
1269
+ }, { kind: "number" });
1260
1270
  }
1261
1271
  /**
1262
1272
  * Creates a {@link ValueParser} for floating-point numbers.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.4.0-dev.2897",
3
+ "version": "1.4.0-dev.2928",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -251,6 +251,14 @@
251
251
  },
252
252
  "import": "./dist/internal/help-request.js",
253
253
  "require": "./dist/internal/help-request.cjs"
254
+ },
255
+ "./doc-pages": {
256
+ "types": {
257
+ "import": "./dist/doc-pages.d.ts",
258
+ "require": "./dist/doc-pages.d.cts"
259
+ },
260
+ "import": "./dist/doc-pages.js",
261
+ "require": "./dist/doc-pages.cjs"
254
262
  }
255
263
  },
256
264
  "imports": {
@@ -261,7 +269,7 @@
261
269
  },
262
270
  "sideEffects": false,
263
271
  "devDependencies": {
264
- "@optique/env": "1.4.0-dev.2897+0d4d0c4a",
272
+ "@optique/env": "1.4.0-dev.2928+80c003f2",
265
273
  "@types/node": "^24.0.0",
266
274
  "fast-check": "^4.7.0",
267
275
  "tsdown": "^0.13.0",
@@ -53,8 +53,9 @@ Core rules
53
53
  - Async value parsers like *@optique/git* make containing parsers async.
54
54
  Await `run()`/`parse()`/`runParser()` or, for `bindKeyring()`, `runAsync()`.
55
55
  - Use `dependency()` when one value parser controls another's valid values.
56
- Wrap middle steps too: `dependency(source.deriveSync(...))`. Resolution
57
- follows dependency order, independently of object/tuple field order.
56
+ For a multi-level chain, wrap the middle derivation too:
57
+ `dependency(source.deriveSync(...))`. Optique resolves such chains by
58
+ dependency order, independently of object/tuple field order.
58
59
  - Use `derivePromptConfig(source, resolver)` from *@optique/prompt* or its
59
60
  adapters for choices/messages depending on parsed values; use
60
61
  `[sourceA, sourceB]` for several sources. Async resolvers run at fallback
@@ -79,9 +80,9 @@ Core rules
79
80
  Show hidden `aliases` in help with `showAliases` on `command()` or runner.
80
81
  - Use `run(parser, { completion: "both" })` for completion, or the object form
81
82
  with `completion.errors` for custom shell errors. Do not hand-write scripts.
82
- - Use `usageLine: [{ type: "ellipsis" }]` in runner options when a large root
83
- synopsis should become a compact `Usage: myapp ...` line. This applies only
84
- to root full help; use `command()`'s `usageLine` for subcommand help.
83
+ - Use `usageLine: [{ type: "ellipsis" }]` for compact root full help and the
84
+ override on `command()` for subcommand help. `errorUsage: "command"` scopes
85
+ usage above errors with root fallback, ignoring help-only overrides.
85
86
  - Use `showUsage: false` in runner options when full help should show the
86
87
  brief and sections without `Usage:`. Set `command()`'s `showUsage: true`
87
88
  to restore it; children inherit the nearest explicit ancestor setting.
@@ -272,8 +273,7 @@ Reference links
272
273
  - Structured messages: <https://optique.dev/concepts/messages.md>
273
274
  - Shell completion: <https://optique.dev/concepts/completion.md>
274
275
  - Command discovery: <https://optique.dev/concepts/discover.md>
275
- - Man pages: <https://optique.dev/concepts/man.md>
276
- - Consumer skills: <https://optique.dev/consumer-skills.md>
276
+ - Markdown references: <https://optique.dev/concepts/markdown-reference.md>
277
277
 
278
278
 
279
279
  Integration packages
@@ -294,6 +294,6 @@ Integration packages
294
294
  | `@optique/temporal` | Temporal date and time parsers | <https://optique.dev/integrations/temporal.md> |
295
295
  | `@optique/git` | Async Git reference validation | <https://optique.dev/integrations/git.md> |
296
296
  | `@optique/logtape` | LogTape levels and formatter/output options | <https://optique.dev/integrations/logtape.md> |
297
- | `@optique/man` | Man page generation | <https://optique.dev/concepts/man.md> |
297
+ | `@optique/man` | Man pages and Markdown references | <https://optique.dev/concepts/man.md> |
298
298
  | `@optique/discover` | Command discovery; opt-in `help.bareGroup` | <https://optique.dev/concepts/discover.md> |
299
299
  | `@optique/testing` | Layered CLI testing at a chosen boundary | <https://optique.dev/concepts/testing.md> |