goke 6.8.0 → 6.10.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 (42) hide show
  1. package/dist/__test__/completions.test.d.ts +9 -0
  2. package/dist/__test__/completions.test.d.ts.map +1 -0
  3. package/dist/__test__/completions.test.js +774 -0
  4. package/dist/__test__/index.test.js +188 -0
  5. package/dist/__test__/just-bash.test.js +19 -0
  6. package/dist/__test__/readme-examples.test.js +141 -5
  7. package/dist/__test__/types.test-d.js +64 -0
  8. package/dist/agents.d.ts +38 -0
  9. package/dist/agents.d.ts.map +1 -0
  10. package/dist/agents.js +63 -0
  11. package/dist/completions.d.ts +88 -0
  12. package/dist/completions.d.ts.map +1 -0
  13. package/dist/completions.js +315 -0
  14. package/dist/goke.d.ts +115 -2
  15. package/dist/goke.d.ts.map +1 -1
  16. package/dist/goke.js +487 -25
  17. package/dist/index.d.ts +9 -2
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +8 -1
  20. package/dist/just-bash.d.ts +1 -1
  21. package/dist/just-bash.d.ts.map +1 -1
  22. package/dist/just-bash.js +80 -15
  23. package/dist/runtime-browser.d.ts +1 -1
  24. package/dist/runtime-browser.d.ts.map +1 -1
  25. package/dist/runtime-browser.js +1 -1
  26. package/dist/runtime-node.d.ts +1 -1
  27. package/dist/runtime-node.d.ts.map +1 -1
  28. package/dist/runtime-node.js +22 -13
  29. package/package.json +1 -1
  30. package/src/__test__/completions.test.ts +902 -0
  31. package/src/__test__/index.test.ts +241 -0
  32. package/src/__test__/just-bash.test.ts +24 -0
  33. package/src/__test__/readme-examples.test.ts +153 -5
  34. package/src/__test__/types.test-d.ts +68 -0
  35. package/src/agents.ts +101 -0
  36. package/src/completions.ts +363 -0
  37. package/src/goke.ts +564 -3
  38. package/src/index.ts +11 -2
  39. package/src/just-bash.ts +92 -18
  40. package/src/runtime-browser.ts +1 -1
  41. package/src/runtime-node.ts +19 -11
  42. package/README.md +0 -1254
package/dist/goke.js CHANGED
@@ -13,6 +13,7 @@ import pc from './picocolors.js';
13
13
  import mri from "./mri.js";
14
14
  import { GokeError, coerceBySchema, extractJsonSchema, extractSchemaMetadata, isStandardSchema } from "./coerce.js";
15
15
  import { createJustBashCommand as createJustBashCommandBridge } from './just-bash.js';
16
+ import { COMPLETION_FLAG, generateCompletionScript, installCompletions, uninstallCompletions, detectShell, detectCompletionShell, validateShell } from './completions.js';
16
17
  import { EventEmitter, fs as runtimeFs, openInBrowser, process } from '#runtime';
17
18
  // ─── Node.js platform constants ───
18
19
  const processArgs = process.argv;
@@ -322,9 +323,41 @@ class Command {
322
323
  * instance before `.command()` was called.
323
324
  */
324
325
  action(callback) {
326
+ // Give anonymous functions a name derived from the command so stack traces
327
+ // show e.g. "command:deploy" instead of "<anonymous>"
328
+ if (!callback.name) {
329
+ const label = this.name ? `command:${this.name}` : 'command:default';
330
+ Object.defineProperty(callback, 'name', { value: label });
331
+ }
325
332
  this.commandAction = callback;
326
333
  return this;
327
334
  }
335
+ /**
336
+ * Return the registered action callback with full type safety.
337
+ *
338
+ * Use this in tests to call the action directly without parsing argv.
339
+ * The returned function has the same typed signature as the `.action()` callback:
340
+ * `(..positionalArgs, options, executionContext) => unknown | Promise<unknown>`
341
+ *
342
+ * Throws if no action has been registered on this command.
343
+ *
344
+ * @example
345
+ * ```ts
346
+ * const cmd = cli
347
+ * .command('deploy', 'Deploy')
348
+ * .option('--env <env>', z.enum(['staging', 'production']))
349
+ * .action((options, { console }) => console.log(options.env))
350
+ *
351
+ * const action = cmd.getAction()
352
+ * action({ env: 'staging', '--': [] }, cli.createExecutionContext({ stdout }))
353
+ * ```
354
+ */
355
+ getAction() {
356
+ if (!this.commandAction) {
357
+ throw new GokeError(`No action registered on command "${this.name || '(default)'}"`);
358
+ }
359
+ return this.commandAction;
360
+ }
328
361
  isMatched(args) {
329
362
  const nameParts = this.name.split(' ').filter(Boolean);
330
363
  if (nameParts.length === 0) {
@@ -822,30 +855,14 @@ class Goke extends EventEmitter {
822
855
  this.globalCommand.options.push(option);
823
856
  return this;
824
857
  }
825
- /**
826
- * Register a middleware function that runs before the matched command action.
827
- *
828
- * Middleware runs in registration order, after option parsing and validation,
829
- * but before the command's `.action()` callback. Useful for reacting to global
830
- * options (e.g. setting up logging, initializing state).
831
- *
832
- * The callback receives the parsed options object, typed according to all
833
- * `.option()` calls that precede this `.use()` in the chain, plus an injected
834
- * execution context with `{ console, process }` for portable output and exits.
835
- *
836
- * @example
837
- * ```ts
838
- * cli
839
- * .option('--verbose', z.boolean().default(false).describe('Verbose'))
840
- * .use((options, { console }) => {
841
- * if (options.verbose) {
842
- * console.log('verbose mode enabled')
843
- * }
844
- * })
845
- * ```
846
- */
847
- use(callback) {
848
- this.middlewares.push({ action: callback });
858
+ use(callbackOrCli) {
859
+ if (callbackOrCli instanceof Goke) {
860
+ for (const command of callbackOrCli.commands) {
861
+ this.commands.push(cloneCommandInto(command, this));
862
+ }
863
+ return this;
864
+ }
865
+ this.middlewares.push({ action: callbackOrCli });
849
866
  return this;
850
867
  }
851
868
  /**
@@ -953,6 +970,294 @@ class Goke extends EventEmitter {
953
970
  this.console.error(`\nRun "${cmdName}" for usage information.`);
954
971
  }
955
972
  }
973
+ /**
974
+ * Register shell completion commands: `completions install` and `completions uninstall`.
975
+ *
976
+ * Also wires the hidden `--get-goke-completions` flag that shell scripts call
977
+ * on each Tab press. When this flag is detected during `parse()`, the CLI
978
+ * prints matching completions to stdout and exits immediately.
979
+ *
980
+ * @example
981
+ * ```ts
982
+ * goke('mycli')
983
+ * .help()
984
+ * .completions()
985
+ * .command('deploy', 'Deploy the app')
986
+ * .parse(process.argv)
987
+ *
988
+ * // Then the user runs:
989
+ * // mycli completions install
990
+ * // mycli dep<TAB> → mycli deploy
991
+ * ```
992
+ */
993
+ completions() {
994
+ this.command('completions install', 'Install shell completions')
995
+ .option('--shell [shell]', 'Target shell (zsh or bash). Auto-detected if omitted.')
996
+ .action(async (options, { console, process: proc }) => {
997
+ const shell = validateShell(options.shell);
998
+ const cliPath = proc.argv[1] ?? this.name;
999
+ const result = await installCompletions(this.name, cliPath, shell);
1000
+ console.log(`Wrote ${result.shell} completions to ${result.path}`);
1001
+ if (result.shell === 'zsh') {
1002
+ console.log('Restart your shell or run: autoload -Uz compinit && compinit');
1003
+ }
1004
+ else {
1005
+ console.log('Restart your shell to enable completions.');
1006
+ }
1007
+ });
1008
+ this.command('completions uninstall', 'Remove shell completions')
1009
+ .option('--shell [shell]', 'Target shell (zsh or bash). Auto-detected if omitted.')
1010
+ .action(async (options, { console }) => {
1011
+ const shell = validateShell(options.shell);
1012
+ const removed = await uninstallCompletions(this.name, shell);
1013
+ if (removed.length > 0) {
1014
+ for (const p of removed) {
1015
+ console.log(`Removed ${p}`);
1016
+ }
1017
+ }
1018
+ else {
1019
+ console.log('No completion files found to remove.');
1020
+ }
1021
+ });
1022
+ this.command('completions script', 'Print the completion script to stdout')
1023
+ .option('--shell [shell]', 'Target shell (zsh or bash). Auto-detected if omitted.')
1024
+ .action((options, { console, process: proc }) => {
1025
+ const shell = validateShell(options.shell) ?? detectShell();
1026
+ if (!shell) {
1027
+ throw new GokeError('Could not detect shell. Set the SHELL environment variable or pass --shell explicitly.');
1028
+ }
1029
+ const cliPath = proc.argv[1] ?? this.name;
1030
+ const script = generateCompletionScript(shell, this.name, cliPath);
1031
+ console.log(script);
1032
+ });
1033
+ return this;
1034
+ }
1035
+ /**
1036
+ * Compute completions for the given args (as received from the shell script).
1037
+ *
1038
+ * Returns an array of completion strings. For zsh, each entry is `name:description`.
1039
+ * For bash, each entry is just the name.
1040
+ *
1041
+ * @internal Used by parse() when --get-goke-completions is detected.
1042
+ */
1043
+ getCompletions(argv) {
1044
+ // argv comes from the shell: ["my-cli", "dep", ""] or ["my-cli", "deploy", "--"]
1045
+ // Strip the binary name (first element, which is the CLI name itself)
1046
+ const args = argv.slice(1);
1047
+ const current = args.length > 0 ? args[args.length - 1] : '';
1048
+ const previous = args.slice(0, -1);
1049
+ // Use GOKE_COMPLETION_SHELL (set by the shell shim) over $SHELL to avoid
1050
+ // format mismatch when e.g. a bash shim runs on a machine where $SHELL is zsh.
1051
+ const isZsh = detectCompletionShell() === 'zsh';
1052
+ const completions = [];
1053
+ const escapeColon = (s) => s.replace(/:/g, '\\:');
1054
+ // Extract the long --flag from an option's rawName string.
1055
+ // rawName is like "--dry-run", "-v, --verbose", "--port <port>"
1056
+ // Returns the original kebab-case flag including dashes.
1057
+ const getLongFlag = (option) => {
1058
+ const parts = removeBrackets(option.rawName).split(',').map((s) => s.trim());
1059
+ // Prefer the -- prefixed part; fall back to the last part (short-only flags like -x)
1060
+ const longPart = parts.find((p) => p.startsWith('--')) ?? parts[parts.length - 1];
1061
+ return longPart.startsWith('-') ? longPart : `--${longPart}`;
1062
+ };
1063
+ // Check if the previous token is a non-boolean option expecting a value.
1064
+ // In that case we should NOT suggest more flags or commands; let the shell
1065
+ // fall back to file completion or return nothing.
1066
+ const isAwaitingOptionValue = () => {
1067
+ if (previous.length === 0)
1068
+ return false;
1069
+ const lastToken = previous[previous.length - 1];
1070
+ if (!lastToken.startsWith('-'))
1071
+ return false;
1072
+ // Find the option matching this token across all registered options
1073
+ const allOptions = [
1074
+ ...this.globalCommand.options,
1075
+ ...this.commands.flatMap((c) => c.options),
1076
+ ];
1077
+ const tokenName = camelcaseOptionName(lastToken.replace(/^-{1,2}/, ''));
1078
+ for (const option of allOptions) {
1079
+ if (option.names.includes(tokenName)) {
1080
+ // If it takes a value (required or optional) and is not boolean, we're awaiting a value
1081
+ return !option.isBoolean && option.required !== undefined;
1082
+ }
1083
+ }
1084
+ return false;
1085
+ };
1086
+ // If the previous token is a non-boolean option, don't suggest anything.
1087
+ // Let the shell fall back to file completion.
1088
+ if (!current.startsWith('-') && isAwaitingOptionValue()) {
1089
+ return [];
1090
+ }
1091
+ // Helper to push an option as a completion entry
1092
+ const pushOption = (option) => {
1093
+ const flag = getLongFlag(option);
1094
+ if (isZsh && option.description) {
1095
+ completions.push(`${escapeColon(flag)}:${escapeColon(option.description)}`);
1096
+ }
1097
+ else {
1098
+ completions.push(flag);
1099
+ }
1100
+ };
1101
+ // Check if any alias of an option has already been used
1102
+ const isOptionUsed = (option, usedOptions) => {
1103
+ return option.names.some((name) => usedOptions.has(name));
1104
+ };
1105
+ // Try to match a command from the previous words
1106
+ let matchedCommand;
1107
+ let consumedArgs = 0;
1108
+ // Sort by name length (longest first) for greedy matching
1109
+ const sortedCommands = [...this.commands].sort((a, b) => {
1110
+ const aLen = a.name.split(' ').filter(Boolean).length;
1111
+ const bLen = b.name.split(' ').filter(Boolean).length;
1112
+ return bLen - aLen;
1113
+ });
1114
+ for (const command of sortedCommands) {
1115
+ const result = command.isMatched(previous);
1116
+ if (result.matched) {
1117
+ matchedCommand = command;
1118
+ consumedArgs = result.consumedArgs;
1119
+ break;
1120
+ }
1121
+ }
1122
+ if (matchedCommand) {
1123
+ // We matched a command, suggest its options
1124
+ const usedOptions = new Set(previous.slice(consumedArgs)
1125
+ .filter((a) => a.startsWith('-'))
1126
+ .map((a) => a.replace(/^-{1,2}/, ''))
1127
+ .map(camelcaseOptionName));
1128
+ const allOptions = [...(matchedCommand.globalCommand?.options ?? []), ...matchedCommand.options];
1129
+ for (const option of allOptions) {
1130
+ if (option.deprecated)
1131
+ continue;
1132
+ // Skip already-used options (check all aliases, not just the primary name)
1133
+ if (option.isBoolean && isOptionUsed(option, usedOptions))
1134
+ continue;
1135
+ const flag = getLongFlag(option);
1136
+ if (current.startsWith('-')) {
1137
+ if (!flag.startsWith(current))
1138
+ continue;
1139
+ }
1140
+ else if (current !== '') {
1141
+ continue;
1142
+ }
1143
+ pushOption(option);
1144
+ }
1145
+ // If current word doesn't start with -, also suggest subcommands that extend this one
1146
+ if (!current.startsWith('-')) {
1147
+ const prefix = matchedCommand.name ? matchedCommand.name + ' ' : '';
1148
+ for (const cmd of this.commands) {
1149
+ if (cmd._hidden)
1150
+ continue;
1151
+ if (cmd === matchedCommand)
1152
+ continue;
1153
+ if (cmd.name.startsWith(prefix) && cmd.name !== matchedCommand.name) {
1154
+ const sub = cmd.name.slice(prefix.length).split(' ')[0];
1155
+ if (sub.startsWith(current)) {
1156
+ if (isZsh) {
1157
+ const desc = cmd.description.split('\n')[0].trim();
1158
+ completions.push(desc ? `${escapeColon(sub)}:${escapeColon(desc)}` : sub);
1159
+ }
1160
+ else {
1161
+ completions.push(sub);
1162
+ }
1163
+ }
1164
+ }
1165
+ }
1166
+ }
1167
+ }
1168
+ else {
1169
+ // No command matched yet, suggest commands
1170
+ // Check if some previous words partially match a multi-word command prefix
1171
+ const prevJoined = previous.join(' ');
1172
+ for (const command of this.commands) {
1173
+ if (command._hidden)
1174
+ continue;
1175
+ if (command.isDefaultCommand)
1176
+ continue;
1177
+ const cmdName = command.name;
1178
+ const cmdParts = cmdName.split(' ').filter(Boolean);
1179
+ if (cmdParts.length === 0)
1180
+ continue;
1181
+ // For single-word commands, just check prefix against current
1182
+ if (cmdParts.length === 1) {
1183
+ if (previous.length === 0 && cmdParts[0].startsWith(current)) {
1184
+ if (isZsh) {
1185
+ const desc = command.description.split('\n')[0].trim();
1186
+ completions.push(desc ? `${escapeColon(cmdParts[0])}:${escapeColon(desc)}` : cmdParts[0]);
1187
+ }
1188
+ else {
1189
+ completions.push(cmdParts[0]);
1190
+ }
1191
+ }
1192
+ continue;
1193
+ }
1194
+ // Multi-word commands: check if previous matches the prefix parts
1195
+ const matchPrefix = cmdParts.slice(0, -1).join(' ');
1196
+ const lastPart = cmdParts[cmdParts.length - 1];
1197
+ if (prevJoined === matchPrefix && lastPart.startsWith(current)) {
1198
+ if (isZsh) {
1199
+ const desc = command.description.split('\n')[0].trim();
1200
+ completions.push(desc ? `${escapeColon(lastPart)}:${escapeColon(desc)}` : lastPart);
1201
+ }
1202
+ else {
1203
+ completions.push(lastPart);
1204
+ }
1205
+ }
1206
+ }
1207
+ // Also suggest first words of multi-word commands when at root level
1208
+ if (previous.length === 0) {
1209
+ const seenFirstWords = new Set();
1210
+ for (const command of this.commands) {
1211
+ if (command._hidden || command.isDefaultCommand)
1212
+ continue;
1213
+ const firstWord = command.name.split(' ')[0];
1214
+ if (!firstWord || seenFirstWords.has(firstWord))
1215
+ continue;
1216
+ // Skip if already added as a single-word command above
1217
+ if (completions.some((c) => {
1218
+ const name = c.split(':')[0].replace(/\\:/g, ':');
1219
+ return name === firstWord;
1220
+ }))
1221
+ continue;
1222
+ seenFirstWords.add(firstWord);
1223
+ if (firstWord.startsWith(current)) {
1224
+ // For first words of multi-word commands, no description (it's a prefix, not a full command)
1225
+ completions.push(firstWord);
1226
+ }
1227
+ }
1228
+ }
1229
+ // Also include default/root command options at root level
1230
+ // (commands with name '' that have their own options)
1231
+ const defaultCommands = this.commands.filter((c) => c.isDefaultCommand);
1232
+ const defaultOptions = defaultCommands.flatMap((c) => c.options);
1233
+ // Suggest global options + default command options when current starts with -
1234
+ if (current.startsWith('-') || current === '') {
1235
+ const globalAndDefaultOptions = [...this.globalCommand.options, ...defaultOptions];
1236
+ const seen = new Set();
1237
+ for (const option of globalAndDefaultOptions) {
1238
+ if (option.deprecated)
1239
+ continue;
1240
+ if (seen.has(option.name))
1241
+ continue;
1242
+ seen.add(option.name);
1243
+ const flag = getLongFlag(option);
1244
+ if (current.startsWith('-')) {
1245
+ if (!flag.startsWith(current))
1246
+ continue;
1247
+ }
1248
+ else if (current !== '') {
1249
+ continue;
1250
+ }
1251
+ // Only suggest options when current is - prefixed or empty and no commands matched
1252
+ if (current === '' && completions.length > 0 && !current.startsWith('-'))
1253
+ continue;
1254
+ pushOption(option);
1255
+ }
1256
+ }
1257
+ }
1258
+ // Deduplicate
1259
+ return [...new Set(completions)];
1260
+ }
956
1261
  /**
957
1262
  * Parse argv
958
1263
  */
@@ -963,6 +1268,19 @@ class Goke extends EventEmitter {
963
1268
  if (!this.name) {
964
1269
  this.name = argv[1] ? getFileName(argv[1]) : 'cli';
965
1270
  }
1271
+ // Intercept --get-goke-completions before any command matching/validation.
1272
+ // The shell completion script passes this flag on every Tab press.
1273
+ const completionFlagIndex = argv.indexOf(`--${COMPLETION_FLAG}`);
1274
+ if (completionFlagIndex !== -1) {
1275
+ // Everything after the flag is the words typed so far
1276
+ const completionArgs = argv.slice(completionFlagIndex + 1);
1277
+ const completions = this.getCompletions(completionArgs);
1278
+ for (const c of completions) {
1279
+ this.stdout.write(c + '\n');
1280
+ }
1281
+ this.exit(0);
1282
+ return { args: [], options: {} };
1283
+ }
966
1284
  let shouldParse = true;
967
1285
  // Sort by name length (longest first) so "mcp login" matches before "mcp"
968
1286
  const sortedCommands = [...this.commands].sort((a, b) => {
@@ -1310,5 +1628,149 @@ class Goke extends EventEmitter {
1310
1628
  }
1311
1629
  }
1312
1630
  }
1313
- export { createConsole, Command, GokeProcessExit, openInBrowser };
1631
+ /**
1632
+ * Generate markdown documentation pages for every command in a CLI.
1633
+ *
1634
+ * Returns one `DocPage` per non-hidden command, plus a root index page
1635
+ * that lists all available commands. Each page includes an arguments table,
1636
+ * options table, global options, and examples when available.
1637
+ *
1638
+ * @example
1639
+ * ```ts
1640
+ * import { goke, generateDocs } from 'goke'
1641
+ * import fs from 'node:fs'
1642
+ *
1643
+ * const cli = goke('mycli')
1644
+ * .command('deploy <env>', 'Deploy to an environment')
1645
+ * .option('--force', 'Skip confirmation')
1646
+ *
1647
+ * const pages = generateDocs({ cli })
1648
+ * for (const page of pages) {
1649
+ * fs.writeFileSync(`docs/${page.slug}.md`, page.content)
1650
+ * }
1651
+ * ```
1652
+ */
1653
+ function generateDocs({ cli }) {
1654
+ const pages = [];
1655
+ // Collect global options (from globalCommand), excluding deprecated
1656
+ const globalOptions = cli.globalCommand.options.filter((o) => !o.deprecated);
1657
+ // Root index page listing all commands
1658
+ const visibleCommands = cli.commands.filter((cmd) => !cmd._hidden);
1659
+ if (visibleCommands.length > 0) {
1660
+ const lines = [];
1661
+ lines.push(`# ${cli.name}`);
1662
+ lines.push('');
1663
+ const { versionNumber } = cli.globalCommand;
1664
+ if (versionNumber) {
1665
+ lines.push(`Version: ${versionNumber}`);
1666
+ lines.push('');
1667
+ }
1668
+ lines.push('## Commands');
1669
+ lines.push('');
1670
+ lines.push('| Command | Description |');
1671
+ lines.push('|---------|-------------|');
1672
+ for (const cmd of visibleCommands) {
1673
+ if (cmd.isDefaultCommand)
1674
+ continue;
1675
+ const desc = cmd.description.split('\n')[0].trim();
1676
+ const slug = cmd.name.replace(/\s+/g, '-');
1677
+ lines.push(`| [\`${cmd.name}\`](./${slug}.md) | ${desc} |`);
1678
+ }
1679
+ lines.push('');
1680
+ if (globalOptions.length > 0) {
1681
+ lines.push('## Global Options');
1682
+ lines.push('');
1683
+ lines.push(formatOptionsTable(globalOptions));
1684
+ lines.push('');
1685
+ }
1686
+ pages.push({ command: '', slug: 'index', content: lines.join('\n') });
1687
+ }
1688
+ // One page per command
1689
+ for (const cmd of visibleCommands) {
1690
+ if (cmd.isDefaultCommand)
1691
+ continue;
1692
+ const lines = [];
1693
+ const title = cmd.name;
1694
+ lines.push(`# ${title}`);
1695
+ lines.push('');
1696
+ if (cmd.description) {
1697
+ lines.push(cmd.description);
1698
+ lines.push('');
1699
+ }
1700
+ // Usage line
1701
+ const usage = cmd.usageText || cmd.rawName;
1702
+ lines.push('## Usage');
1703
+ lines.push('');
1704
+ lines.push('```sh');
1705
+ lines.push(`${cli.name} ${usage}`);
1706
+ lines.push('```');
1707
+ lines.push('');
1708
+ // Arguments table
1709
+ if (cmd.args.length > 0) {
1710
+ lines.push('## Arguments');
1711
+ lines.push('');
1712
+ lines.push('| Argument | Required | Description |');
1713
+ lines.push('|----------|----------|-------------|');
1714
+ for (const arg of cmd.args) {
1715
+ const bracket = arg.required
1716
+ ? `<${arg.variadic ? '...' : ''}${arg.value}>`
1717
+ : `[${arg.variadic ? '...' : ''}${arg.value}]`;
1718
+ const required = arg.required ? 'Yes' : 'No';
1719
+ const desc = arg.variadic ? `${arg.value} (variadic)` : arg.value;
1720
+ lines.push(`| \`${bracket}\` | ${required} | ${desc} |`);
1721
+ }
1722
+ lines.push('');
1723
+ }
1724
+ // Command-specific options
1725
+ const cmdOptions = cmd.options.filter((o) => !o.deprecated);
1726
+ if (cmdOptions.length > 0) {
1727
+ lines.push('## Options');
1728
+ lines.push('');
1729
+ lines.push(formatOptionsTable(cmdOptions));
1730
+ lines.push('');
1731
+ }
1732
+ // Global options section
1733
+ if (globalOptions.length > 0) {
1734
+ lines.push('## Global Options');
1735
+ lines.push('');
1736
+ lines.push(formatOptionsTable(globalOptions));
1737
+ lines.push('');
1738
+ }
1739
+ // Examples
1740
+ if (cmd.examples.length > 0) {
1741
+ lines.push('## Examples');
1742
+ lines.push('');
1743
+ for (const example of cmd.examples) {
1744
+ const text = typeof example === 'function' ? example(cli.name) : example;
1745
+ // Auto-wrap in ```sh if not already fenced
1746
+ if (text.trimStart().startsWith('```')) {
1747
+ lines.push(text);
1748
+ }
1749
+ else {
1750
+ lines.push('```sh');
1751
+ lines.push(text);
1752
+ lines.push('```');
1753
+ }
1754
+ lines.push('');
1755
+ }
1756
+ }
1757
+ const slug = cmd.name.replace(/\s+/g, '-');
1758
+ pages.push({ command: cmd.name, slug, content: lines.join('\n') });
1759
+ }
1760
+ return pages;
1761
+ }
1762
+ function formatOptionsTable(options) {
1763
+ const lines = [];
1764
+ lines.push('| Option | Default | Description |');
1765
+ lines.push('|--------|---------|-------------|');
1766
+ for (const opt of options) {
1767
+ const defaultVal = opt.default !== undefined ? `\`${String(opt.default)}\`` : '-';
1768
+ // Escape pipe characters in description for markdown tables
1769
+ const desc = opt.description.replace(/\|/g, '\\|').replace(/\n/g, ' ');
1770
+ lines.push(`| \`${opt.rawName}\` | ${defaultVal} | ${desc} |`);
1771
+ }
1772
+ return lines.join('\n');
1773
+ }
1774
+ export { createConsole, Command, GokeProcessExit, openInBrowser, generateDocs };
1775
+ export { generateCompletionScript, installCompletions, uninstallCompletions, detectShell, detectCompletionShell, validateShell };
1314
1776
  export default Goke;
package/dist/index.d.ts CHANGED
@@ -6,10 +6,17 @@ import { Command } from "./goke.js";
6
6
  * @param options Configuration for stdout, stderr, and argv
7
7
  */
8
8
  declare const goke: (name?: string, options?: GokeOptions) => Goke<{}>;
9
+ /**
10
+ * Vendored picocolors instance for terminal colors.
11
+ * Import this instead of installing picocolors, chalk, or any other color library.
12
+ */
13
+ export declare const colors: import("./picocolors.js").PicoColors;
9
14
  export default goke;
10
15
  export { goke, Goke, Command };
11
- export { createConsole, GokeProcessExit, openInBrowser } from "./goke.js";
12
- export type { GokeOutputStream, GokeConsole, GokeExecutionContext, GokeExecutionContextOverride, GokeFs, GokeOptions, GokeProcess } from "./goke.js";
16
+ export { createConsole, GokeProcessExit, openInBrowser, generateDocs, generateCompletionScript, installCompletions, uninstallCompletions, detectShell } from "./goke.js";
17
+ export type { GokeOutputStream, GokeConsole, GokeExecutionContext, GokeExecutionContextOverride, GokeFs, GokeOptions, GokeProcess, DocPage, GenerateDocsOptions, ShellType } from "./goke.js";
13
18
  export type { StandardTypedV1, StandardJSONSchemaV1, JsonSchema } from "./coerce.js";
14
19
  export { GokeError, coerceBySchema, extractJsonSchema, wrapJsonSchema, isStandardSchema, extractSchemaMetadata } from "./coerce.js";
20
+ export { detectAgent, agentInfo, agent, isAgent } from "./agents.js";
21
+ export type { AgentName, AgentInfo } from "./agents.js";
15
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAEnC;;;GAGG;AACH,QAAA,MAAM,IAAI,GAAI,aAAS,EAAE,UAAU,WAAW,aAA4B,CAAA;AAE1E,eAAe,IAAI,CAAA;AACnB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;AAC9B,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACpJ,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAGnC;;;GAGG;AACH,QAAA,MAAM,IAAI,GAAI,aAAS,EAAE,UAAU,WAAW,aAA4B,CAAA;AAE1E;;;GAGG;AACH,eAAO,MAAM,MAAM,sCAAK,CAAA;AAExB,eAAe,IAAI,CAAA;AACnB,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,CAAA;AAC9B,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAE,YAAY,EAAE,wBAAwB,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACxK,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,oBAAoB,EAAE,4BAA4B,EAAE,MAAM,EAAE,WAAW,EAAE,WAAW,EAAE,OAAO,EAAE,mBAAmB,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAC7L,YAAY,EAAE,eAAe,EAAE,oBAAoB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AACpF,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,iBAAiB,EAAE,cAAc,EAAE,gBAAgB,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAA;AACnI,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACpE,YAAY,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA"}
package/dist/index.js CHANGED
@@ -1,11 +1,18 @@
1
1
  import Goke from "./goke.js";
2
2
  import { Command } from "./goke.js";
3
+ import pc from "./picocolors.js";
3
4
  /**
4
5
  * @param name The program name to display in help and version message
5
6
  * @param options Configuration for stdout, stderr, and argv
6
7
  */
7
8
  const goke = (name = '', options) => new Goke(name, options);
9
+ /**
10
+ * Vendored picocolors instance for terminal colors.
11
+ * Import this instead of installing picocolors, chalk, or any other color library.
12
+ */
13
+ export const colors = pc;
8
14
  export default goke;
9
15
  export { goke, Goke, Command };
10
- export { createConsole, GokeProcessExit, openInBrowser } from "./goke.js";
16
+ export { createConsole, GokeProcessExit, openInBrowser, generateDocs, generateCompletionScript, installCompletions, uninstallCompletions, detectShell } from "./goke.js";
11
17
  export { GokeError, coerceBySchema, extractJsonSchema, wrapJsonSchema, isStandardSchema, extractSchemaMetadata } from "./coerce.js";
18
+ export { detectAgent, agentInfo, agent, isAgent } from "./agents.js";
@@ -17,7 +17,7 @@ interface JustBashCommand {
17
17
  trusted: true;
18
18
  execute(args: string[], context?: JustBashExecutionContext): Promise<JustBashExecResult>;
19
19
  }
20
- type JustBashExecutionContext = Pick<CommandContext, 'cwd' | 'env' | 'fs' | 'stdin'>;
20
+ type JustBashExecutionContext = Pick<CommandContext, 'cwd' | 'env' | 'fs' | 'stdin' | 'limits'>;
21
21
  export declare function createJustBashCommand(cli: Goke<any>, options?: {
22
22
  name?: string;
23
23
  }): JustBashCommand;
@@ -1 +1 @@
1
- {"version":3,"file":"just-bash.d.ts","sourceRoot":"","sources":["../src/just-bash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,WAAW,CAAA;AAC5D,OAAO,IAAyB,MAAM,WAAW,CAAA;AAIjD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,IAAI,CAAA;IACb,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CACzF;AAED,KAAK,wBAAwB,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,CAAC,CAAA;AA4LpF,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1B,eAAe,CAqDjB;AAED,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAA"}
1
+ {"version":3,"file":"just-bash.d.ts","sourceRoot":"","sources":["../src/just-bash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAKH,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,WAAW,CAAA;AAC5D,OAAO,IAAyB,MAAM,WAAW,CAAA;AAIjD,UAAU,kBAAkB;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,UAAU,eAAe;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,IAAI,CAAA;IACb,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;CACzF;AAED,KAAK,wBAAwB,GAAG,IAAI,CAAC,cAAc,EAAE,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,OAAO,GAAG,QAAQ,CAAC,CAAA;AAqQ/F,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GAC1B,eAAe,CAsDjB;AAED,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,CAAA"}