Skip to content

Commit 5a01594

Browse files
committed
feat: add verbose help mode, universal man page generation, and documentation URL support
1 parent da86a90 commit 5a01594

8 files changed

Lines changed: 313 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,18 @@ All notable changes to apcore-cli (TypeScript SDK) will be documented in this fi
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.4.0] - 2026-03-29
9+
10+
### Added
11+
- **Verbose help mode** — Built-in apcore options (`--input`, `--yes`, `--large-input`, `--format`, `--sandbox`) are now hidden from `--help` output by default. Pass `--help --verbose` to display the full option list including built-in options.
12+
- **Universal man page generation**`buildProgramManPage()` generates a complete roff man page covering all registered commands. `configureManHelp()` adds `--help --man` support to any Commander program, enabling downstream projects to get man pages for free.
13+
- **Documentation URL support**`setDocsUrl()` sets a base URL for online docs. Per-command help shows `Docs: {url}/commands/{name}`, man page SEE ALSO includes `Full documentation at {url}`. No default — disabled when not set.
14+
15+
### Changed
16+
- `buildModuleCommand()` accepts optional `verboseHelp` parameter to control built-in option visibility in help.
17+
- `--sandbox` is now always hidden from help (not yet implemented). Only four built-in options (`--input`, `--yes`, `--large-input`, `--format`) toggle with `--verbose`.
18+
- Improved built-in option descriptions for clarity (e.g., `--input` now reads "Read JSON input from a file path, or use '-' to read from stdin pipe").
19+
820
## [0.3.2] - 2026-03-28
921

1022
### Fixed

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
148148
| `--log-level` | `WARNING` | Logging: `DEBUG`, `INFO`, `WARNING`, `ERROR` |
149149
| `--version` | | Show version and exit |
150150
| `--help` | | Show help and exit |
151+
| `--verbose` | | Show all options in help (including built-in apcore options) |
152+
| `--man` | | Output man page in roff format (use with `--help`) |
151153

152154
### Built-in Commands
153155

@@ -161,15 +163,15 @@ apcore-cli [OPTIONS] COMMAND [ARGS]
161163

162164
### Module Execution Options
163165

164-
When executing a module (e.g. `apcore-cli math.add`), these built-in options are always available:
166+
When executing a module (e.g. `apcore-cli math.add`), these built-in options are available (hidden by default; use `--verbose` to show in `--help`):
165167

166168
| Option | Description |
167169
|--------|-------------|
168170
| `--input -` | Read JSON input from STDIN |
169171
| `--yes` / `-y` | Bypass approval prompts |
170172
| `--large-input` | Allow STDIN input larger than 10MB |
171173
| `--format` | Output format: `json` or `table` |
172-
| `--sandbox` | Run module in subprocess sandbox |
174+
| `--sandbox` | Run module in subprocess sandbox (not yet implemented — always hidden) |
173175

174176
Schema-generated flags (e.g. `--a`, `--b`) are added automatically from the module's `input_schema`.
175177

@@ -234,7 +236,8 @@ cli:
234236
- **Schema validation** -- inputs validated against JSON Schema before execution, with `$ref`/`allOf`/`anyOf`/`oneOf` resolution
235237
- **Security** -- API key auth (keyring + AES-256-GCM), append-only audit logging, subprocess sandboxing
236238
- **Shell completions** -- `apcore-cli completion bash|zsh|fish` generates completion scripts with dynamic module ID completion
237-
- **Man pages** -- `apcore-cli man <command>` generates roff-formatted man pages
239+
- **Man pages** -- `apcore-cli man <command>` for single commands, or `--help --man` for a complete program man page. `configureManHelp()` provides one-line integration for downstream projects
240+
- **Documentation URL** -- `setDocsUrl()` adds doc links to help footers and man pages
238241
- **Audit logging** -- all executions logged to `~/.apcore-cli/audit.jsonl` with SHA-256 input hashing
239242

240243
## How It Works
@@ -274,7 +277,7 @@ apcore Registry + Executor (your modules, unchanged)
274277
275278
**Classes:** `LazyModuleGroup`, `ConfigResolver`, `AuthProvider`, `ConfigEncryptor`, `AuditLogger`, `Sandbox`
276279
277-
**Functions:** `createCli`, `main`, `buildModuleCommand`, `validateModuleId`, `collectInput`, `schemaToCliOptions`, `reconvertEnumValues`, `resolveRefs`, `checkApproval`, `resolveFormat`, `formatModuleList`, `formatModuleDetail`, `formatExecResult`, `registerDiscoveryCommands`, `registerShellCommands`, `setAuditLogger`, `getAuditLogger`, `exitCodeForError`, `mapType`, `extractHelp`, `truncate`
280+
**Functions:** `createCli`, `main`, `buildModuleCommand`, `validateModuleId`, `collectInput`, `schemaToCliOptions`, `reconvertEnumValues`, `resolveRefs`, `checkApproval`, `resolveFormat`, `formatModuleList`, `formatModuleDetail`, `formatExecResult`, `registerDiscoveryCommands`, `registerShellCommands`, `setAuditLogger`, `getAuditLogger`, `setVerboseHelp`, `setDocsUrl`, `buildProgramManPage`, `configureManHelp`, `exitCodeForError`, `mapType`, `extractHelp`, `truncate`
278281
279282
**Errors:** `ApprovalTimeoutError`, `ApprovalDeniedError`, `AuthenticationError`, `ConfigDecryptionError`, `ModuleExecutionError`, `ModuleNotFoundError`, `SchemaValidationError`
280283

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "apcore-cli",
3-
"version": "0.3.2",
3+
"version": "0.4.0",
44
"description": "CLI wrapper for the apcore core SDK — exposes apcore modules as CLI commands",
55
"type": "module",
66
"main": "./dist/index.js",

src/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
*/
66

77
// Core CLI
8-
export { createCli, main, buildModuleCommand, validateModuleId, collectInput, reconvertEnumValues, applyToolkitIntegration } from "./main.js";
8+
export { createCli, main, buildModuleCommand, validateModuleId, collectInput, reconvertEnumValues, applyToolkitIntegration, verboseHelp, setVerboseHelp, docsUrl, setDocsUrl } from "./main.js";
99
export type { OptionConfig } from "./main.js";
1010

1111
// Lazy module loading
@@ -35,7 +35,7 @@ export { schemaToCliOptions, mapType, extractHelp } from "./schema-parser.js";
3535
export { checkApproval } from "./approval.js";
3636

3737
// Shell integration
38-
export { registerShellCommands } from "./shell.js";
38+
export { registerShellCommands, buildProgramManPage, configureManHelp } from "./shell.js";
3939

4040
// Errors
4141
export {

src/main.ts

Lines changed: 68 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import { readFileSync } from "node:fs";
88
import { fileURLToPath } from "node:url";
99
import * as path from "node:path";
10-
import { Command, CommanderError } from "commander";
10+
import { Command, CommanderError, Option } from "commander";
1111
import { EXIT_CODES, exitCodeForError } from "./errors.js";
1212
import { resolveRefs } from "./ref-resolver.js";
1313
import { schemaToCliOptions } from "./schema-parser.js";
@@ -19,6 +19,33 @@ import { getDisplay } from "./display-helpers.js";
1919
import type { Executor, ModuleDescriptor } from "./cli.js";
2020

2121
const __dirname = path.dirname(fileURLToPath(import.meta.url));
22+
23+
/** Whether --verbose was passed (controls help detail level). */
24+
export let verboseHelp = false;
25+
26+
/** Set the verbose help flag. When false, built-in options are hidden from help. */
27+
export function setVerboseHelp(verbose: boolean): void {
28+
verboseHelp = verbose;
29+
}
30+
31+
/** Base URL for online documentation. Null means no docs link shown. */
32+
export let docsUrl: string | null = null;
33+
34+
/**
35+
* Set the base URL for online documentation links shown in help and man pages.
36+
* Pass null to disable. Command-level help appends `/commands/{name}` automatically.
37+
*
38+
* @example setDocsUrl("https://docs.apcore.dev/cli");
39+
*/
40+
export function setDocsUrl(url: string | null): void {
41+
docsUrl = url;
42+
}
43+
44+
/** Check if --verbose is present in process.argv (pre-parse, before Commander). */
45+
function hasVerboseFlag(): boolean {
46+
return process.argv.includes("--verbose");
47+
}
48+
2249
let VERSION = "0.0.0";
2350
try {
2451
const pkg = JSON.parse(readFileSync(path.resolve(__dirname, "../package.json"), "utf-8"));
@@ -66,7 +93,9 @@ export interface OptionConfig {
6693
export function createCli(
6794
extensionsDir?: string,
6895
progName?: string,
96+
verbose = false,
6997
): Command {
98+
verboseHelp = verbose;
7099
// Resolve program name
71100
const resolvedProgName = progName ?? path.basename(process.argv[1] ?? "apcore-cli") ?? "apcore-cli";
72101

@@ -81,7 +110,8 @@ export function createCli(
81110
.option("--extensions-dir <path>", "Path to extensions directory")
82111
.option("--commands-dir <path>", "Path to convention-based commands directory")
83112
.option("--binding <path>", "Path to binding.yaml for display overlay")
84-
.option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING");
113+
.option("--log-level <level>", "Logging level (DEBUG|INFO|WARNING|ERROR)", "WARNING")
114+
.option("--verbose", "Show all options in help output (including built-in apcore options)");
85115

86116
// NOTE: Full registry/executor wiring requires apcore-js to be available.
87117
// For now, extensions-dir is accepted but not wired to a real registry.
@@ -156,7 +186,8 @@ export async function applyToolkitIntegration(
156186
* Parse argv and run the CLI. Handles top-level error catching and exit codes.
157187
*/
158188
export function main(progName?: string): void {
159-
const program = createCli(undefined, progName);
189+
verboseHelp = hasVerboseFlag();
190+
const program = createCli(undefined, progName, verboseHelp);
160191

161192
try {
162193
program.parse(process.argv);
@@ -185,6 +216,7 @@ export function buildModuleCommand(
185216
executor: Executor,
186217
helpTextMaxLength = 1000,
187218
cmdName?: string,
219+
verbose = verboseHelp,
188220
): Command {
189221
const moduleId = moduleDef.id;
190222
let resolvedSchema: Record<string, unknown> = {};
@@ -211,12 +243,38 @@ export function buildModuleCommand(
211243

212244
const cmd = new Command(effectiveCmdName).description(cmdHelp);
213245

214-
// Built-in options
215-
cmd.option("--input <source>", "Read input from STDIN ('-')");
216-
cmd.option("-y, --yes", "Bypass approval prompts", false);
217-
cmd.option("--large-input", "Allow STDIN input larger than 10MB", false);
218-
cmd.option("--format <format>", "Output format (json|table)");
219-
cmd.option("--sandbox", "Run module in subprocess sandbox", false);
246+
// Built-in options (hidden unless --verbose)
247+
const inputOpt = new Option("--input <source>", "Read JSON input from a file path, or use '-' to read from stdin pipe");
248+
const yesOpt = new Option("-y, --yes", "Skip interactive approval prompts (for scripts and CI)").default(false);
249+
const largeInputOpt = new Option("--large-input", "Allow stdin input larger than 10MB (default limit protects against accidental pipes)").default(false);
250+
const formatOpt = new Option("--format <format>", "Set output format: 'json' for machine-readable, 'table' for human-readable");
251+
// --sandbox is always hidden (not yet implemented)
252+
const sandboxOpt = new Option("--sandbox", "Run module in an isolated subprocess with restricted filesystem and env access").default(false).hideHelp();
253+
254+
if (!verbose) {
255+
inputOpt.hideHelp();
256+
yesOpt.hideHelp();
257+
largeInputOpt.hideHelp();
258+
formatOpt.hideHelp();
259+
}
260+
261+
cmd.addOption(inputOpt);
262+
cmd.addOption(yesOpt);
263+
cmd.addOption(largeInputOpt);
264+
cmd.addOption(formatOpt);
265+
cmd.addOption(sandboxOpt);
266+
267+
// Help footer: verbose hint + optional docs link
268+
const footerParts: string[] = [];
269+
if (!verbose) {
270+
footerParts.push("Use --verbose to show all options (including built-in apcore options).");
271+
}
272+
if (docsUrl) {
273+
footerParts.push(`Docs: ${docsUrl}/commands/${effectiveCmdName}`);
274+
}
275+
if (footerParts.length > 0) {
276+
cmd.addHelpText("after", "\n" + footerParts.join("\n") + "\n");
277+
}
220278

221279
// Schema-generated options
222280
for (const opt of schemaOptions) {
@@ -238,7 +296,7 @@ export function buildModuleCommand(
238296

239297
// Remove built-in keys from options to get schema kwargs
240298
const schemaKwargs: Record<string, unknown> = {};
241-
const builtinKeys = new Set(["input", "yes", "largeInput", "format", "sandbox"]);
299+
const builtinKeys = new Set(["input", "yes", "largeInput", "format", "sandbox", "verbose"]);
242300
for (const [k, v] of Object.entries(options)) {
243301
if (!builtinKeys.has(k)) {
244302
schemaKwargs[k] = v;

0 commit comments

Comments
 (0)