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
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
schema: spec-driven
created: 2026-08-26
skip_specs: true
34 changes: 34 additions & 0 deletions openspec/changes/archive/2026-08-26-add-cli-help-flag/proposal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
## Why

After the first real `npm publish` attempt of `@openspec-ui/cli`
(`2026-08-26-prepare-cli-npm-publish`), the user tried
`openspec-ui-cli --help` to see the available options and got only
`openspec-ui-cli: unknown command '--help' (supported: validate)` — no
usage text at all. The options (`--cwd`, `--format`) were documented
only in `packages/cli/README.md` on the npm registry page, not
discoverable from the CLI itself. Any real external consumer running
this CLI for the first time hits the same gap.

## What Changes

- Add a `USAGE` string and `--help`/`-h` handling to
`packages/cli/src/main.ts`: printed to stdout, exits `0`.
- Also print `USAGE` to stderr alongside the existing "unknown command"
and argument-parsing error messages, so a mistake (not just an
explicit `--help`) also surfaces the available options immediately.
- Update `packages/cli/README.md` to mention `--help`/`-h`.

## Capabilities

### Modified Capabilities

(none in the specified-behavior sense — `validate`'s own behavior and
exit-code contract are unchanged; this only adds a `--help` path and
richer error output. `.openspec.yaml` sets `skip_specs: true`.)

## Impact

- `packages/cli/src/main.ts`
- `packages/cli/src/main.test.ts`
- `packages/cli/README.md`
- `.changeset/*.md` (new changeset file)
24 changes: 24 additions & 0 deletions openspec/changes/archive/2026-08-26-add-cli-help-flag/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
## 1. Add --help

- [x] 1.1 Add a `USAGE` string (command syntax, `--cwd`/`--format`
options, exit-code contract) and `--help`/`-h` handling to
`runMain()` in `packages/cli/src/main.ts`, checked before argument
parsing; prints to stdout and exits `0`.
- [x] 1.2 Print `USAGE` to stderr alongside the existing unknown-command
and argument-parsing error messages.
- [x] 1.3 Update `packages/cli/README.md` to mention `--help`/`-h`.

## 2. Verification

- [x] 2.1 Add tests to `packages/cli/src/main.test.ts`: `--help` and
`-h` print usage and exit `0`; an unknown command still exits `2` and
now also prints usage as a second stderr line (existing assertions on
the first stderr line are unaffected).
- [x] 2.2 `npm run typecheck`, `npm run lint`, and `npm run test`
(workspace) pass for `@openspec-ui/cli`.
- [x] 2.3 Rebuild (`npm run build --workspace @openspec-ui/cli`) and
smoke-test `node packages/cli/dist/cli.js --help` directly.
- [x] 2.4 Propose a changeset (`npx changeset`) for `@openspec-ui/cli`
(patch: new flag, no breaking change) instead of hand-editing
`version`/`CHANGELOG.md`; apply it via `npx changeset version`.
- [x] 2.5 Run `openspec change validate --strict add-cli-help-flag`.
10 changes: 10 additions & 0 deletions packages/cli/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# @openspec-ui/cli

## 0.1.2

### Patch Changes

- Add `--help`/`-h` to `openspec-ui-cli`, printing usage (command syntax,
options, exit codes) and exiting `0`. Usage is now also printed
alongside argument-parsing and unknown-command errors, so a mistake
surfaces the available options immediately instead of only an error
message.

## 0.1.1

### Patch Changes
Expand Down
5 changes: 5 additions & 0 deletions packages/cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ npx @openspec-ui/cli validate --cwd . --format text
to the current directory).
- `--format json|text`: `json` (the default) prints an aggregated report
suitable for further processing; `text` prints a human-readable table.
- `--help` / `-h`: print this usage summary and exit `0`.

```sh
npx @openspec-ui/cli --help
```

## Exit codes

Expand Down
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@openspec-ui/cli",
"version": "0.1.1",
"version": "0.1.2",
"description": "Non-interactive OpenSpec change validation for CI merge gates (see docs/adr/0007-ci-cli-third-delivery-target.md).",
"license": "MIT",
"repository": {
Expand Down
30 changes: 30 additions & 0 deletions packages/cli/src/main.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,4 +97,34 @@ describe("runMain", () => {
expect(code).toBe(2);
expect(io.errLines[0]).toContain("--format must be");
});

it("prints usage and exits 0 for --help", async () => {
const io = collectingIo();

const code = await runMain(["--help"], { validateAll: vi.fn(), ...io });

expect(code).toBe(0);
expect(io.outLines[0]).toContain("Usage:");
expect(io.outLines[0]).toContain("--cwd");
expect(io.outLines[0]).toContain("--format");
});

it("prints usage and exits 0 for -h", async () => {
const io = collectingIo();

const code = await runMain(["-h"], { validateAll: vi.fn(), ...io });

expect(code).toBe(0);
expect(io.outLines[0]).toContain("Usage:");
});

it("prints usage alongside an unknown-command error", async () => {
const io = collectingIo();

const code = await runMain(["bogus"], { validateAll: vi.fn(), ...io });

expect(code).toBe(2);
expect(io.errLines[0]).toContain("unknown command");
expect(io.errLines[1]).toContain("Usage:");
});
});
23 changes: 23 additions & 0 deletions packages/cli/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@

import { runValidateAll, type ValidateAllResult } from "./openspec-validate.js";

const USAGE = `openspec-ui-cli — non-interactive OpenSpec change validation for CI merge gates.

Usage:
openspec-ui-cli validate [--cwd <path>] [--format json|text]

Options:
--cwd <path> Repository root containing openspec/changes/ (default: current directory)
--format json|text Output format (default: json)
--help, -h Show this help and exit

Exit codes:
0 every active change passed strict validation
1 at least one active change failed strict validation
2 the CLI itself could not complete the check (bad arguments, the
openspec CLI missing, a filesystem error)`;

export interface MainOptions {
cwd?: string;
format?: "json" | "text";
Expand Down Expand Up @@ -58,13 +74,20 @@ export async function runMain(argv: string[], deps: MainDeps = {}): Promise<numb
const stdout = deps.stdout ?? console.log;
const stderr = deps.stderr ?? console.error;

if (argv.includes("--help") || argv.includes("-h")) {
stdout(USAGE);
return 0;
}

const { command, options, error } = parseArgs(argv);
if (error) {
stderr(`openspec-ui-cli: ${error}`);
stderr(USAGE);
return 2;
}
if (command !== "validate") {
stderr(`openspec-ui-cli: unknown command '${command ?? ""}' (supported: validate)`);
stderr(USAGE);
return 2;
}

Expand Down
Loading