mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-10-02 05:24:34 +08:00
docs(man): move the manual-page entry to the canonical CLI page
alfred-openspec on #1784: docs-lab/README.md makes docs-lab/ canonical and the old docs/ tree legacy, and 'man openspec' was documented only in docs/cli.md. The entry now lives in docs-lab/reference/cli.md, listed under Utilities, and docs/cli.md is back to its state on main. Two things changed in the move rather than being copied across: - CodeRabbit's finding on the fallback command was valid. The only fallback called 'pnpm root -g', which a contributor who installed with another manager may not have. The command is now derived from the installed CLI itself, so it works whichever manager installed it, with the pnpm form kept as the fallback for an older readlink with no -f. Verified against the packed tarball, whose layout really is bin/../dist/man/openspec.1. - The exit-code and environment-variable parity tests read docs/cli.md, so this PR was adding a hard test dependency on the tree that is being retired, and it forced the '130' row into the legacy table to keep the test green. The parity read backwards anyway: the legacy table omits 130, which the CLI really does exit with, and which docs-lab records per command. The exit-code test now pins the three codes directly, and the environment test holds a property no prose table can: every documented variable is one src/ actually reads. Verified it bites by adding an invented variable. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f68487cdce
commit
f993469dcd
@@ -52,6 +52,7 @@ Your agent runs most of these during the workflow.
|
||||
|---|---|
|
||||
| [`openspec feedback`](#openspec-feedback) | Submit feedback about OpenSpec. |
|
||||
| [`openspec completion`](#openspec-completion) | Install or generate shell completions. |
|
||||
| [`man openspec`](#man-openspec) | Read the full command reference offline. |
|
||||
|
||||
**Deprecated**
|
||||
|
||||
@@ -2182,6 +2183,26 @@ Removes the script and the marked config block. It asks before touching your con
|
||||
- `0`: script generated, installed, or removed. A cancelled uninstall also exits 0.
|
||||
- `1`: shell not supported or not detected, or an install or uninstall step failed.
|
||||
|
||||
## man openspec
|
||||
|
||||
The full command reference, offline.
|
||||
|
||||
```bash
|
||||
man openspec
|
||||
```
|
||||
|
||||
The page lists every command, its arguments, and its flags, plus exit codes, environment variables, the files OpenSpec reads, and examples. It is generated from the CLI itself at build time, so it always matches `openspec --help` for the version you have installed.
|
||||
|
||||
`npm install -g` links the page into your man path, so `man openspec` works with no extra step. Other package managers ship the file without linking it. Point `man` at the copy next to the installed CLI:
|
||||
|
||||
```bash
|
||||
man "$(dirname "$(readlink -f "$(command -v openspec)")")/../dist/man/openspec.1"
|
||||
```
|
||||
|
||||
That resolves the `openspec` on your `PATH` back to the package it came from, so it works whichever manager installed it. If your `readlink` has no `-f` (older macOS), ask the manager for its global package directory instead, for example `man "$(pnpm root -g)/@fission-ai/openspec/dist/man/openspec.1"`.
|
||||
|
||||
Windows has no `man`. Use `openspec --help` there.
|
||||
|
||||
## openspec change
|
||||
|
||||
Deprecated noun form of `show`, `list`, and `validate`. Every run warns and points to the verb-first commands, then runs anyway:
|
||||
|
||||
-26
@@ -1307,31 +1307,6 @@ run a command in an interactive terminal, and never again — it also stays quie
|
||||
if you already have completions installed. Set `OPENSPEC_NO_COMPLETIONS=1` to
|
||||
suppress that tip entirely.
|
||||
|
||||
### `man openspec`
|
||||
|
||||
A global install ships a manual page, so the full command reference is available
|
||||
offline:
|
||||
|
||||
```bash
|
||||
man openspec
|
||||
```
|
||||
|
||||
The page lists every command, its arguments, and its flags, plus exit codes,
|
||||
environment variables, the files OpenSpec reads, and examples. It is generated
|
||||
from the CLI itself at build time, so it always matches `openspec --help` for
|
||||
the version you have installed.
|
||||
|
||||
`man openspec` works out of the box after `npm install -g`, which links the page
|
||||
into your man path. Other package managers ship the file but don't link it, so
|
||||
point `man` at the copy in their global package directory — with pnpm, for
|
||||
example:
|
||||
|
||||
```bash
|
||||
man "$(pnpm root -g)/@fission-ai/openspec/dist/man/openspec.1"
|
||||
```
|
||||
|
||||
Windows has no `man` — use `openspec --help` there.
|
||||
|
||||
---
|
||||
|
||||
## Exit Codes
|
||||
@@ -1340,7 +1315,6 @@ Windows has no `man` — use `openspec --help` there.
|
||||
|------|---------|
|
||||
| `0` | Success |
|
||||
| `1` | Error (validation failure, missing files, etc.) |
|
||||
| `130` | Cancelled at a prompt |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import { Command } from 'commander';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import fg from 'fast-glob';
|
||||
|
||||
import {
|
||||
ENVIRONMENT,
|
||||
@@ -201,19 +202,6 @@ describe('the manual rendered from the real CLI', () => {
|
||||
|
||||
describe('the sections a command tree cannot supply', () => {
|
||||
const page = render(program);
|
||||
const docs = fs.readFileSync(path.join(repoRoot, 'docs', 'cli.md'), 'utf-8');
|
||||
|
||||
/**
|
||||
* Every term in the first column of a docs table, so a row that names two
|
||||
* (`EDITOR` or `VISUAL`) contributes both.
|
||||
*/
|
||||
function tableTerms(heading: string): string[] {
|
||||
const section = docs.split(`## ${heading}`)[1] ?? '';
|
||||
const table = section.split('\n---')[0];
|
||||
return [...table.matchAll(/^\|([^|]+)\|/gm)].flatMap((row) =>
|
||||
[...row[1].matchAll(/`([^`]+)`/g)].map((term) => term[1])
|
||||
);
|
||||
}
|
||||
|
||||
it('places its sections in the order a manual is read in', () => {
|
||||
const sections = [...page.matchAll(/^\.SH (.+)$/gm)].map((match) => match[1]);
|
||||
@@ -232,14 +220,34 @@ describe('the sections a command tree cannot supply', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
it('documents the same exit codes as the CLI reference', () => {
|
||||
expect(EXIT_STATUS.map(([code]) => code)).toEqual(tableTerms('Exit Codes'));
|
||||
it('names an environment variable the CLI actually reads', () => {
|
||||
// These lists are not pinned to a prose page. Parity against `docs/cli.md`
|
||||
// would tie the manual to the tree `docs-lab/README.md` retires, and it
|
||||
// read backwards: the legacy table omits 130, which the CLI really does
|
||||
// exit with. The canonical home for the variables is
|
||||
// `docs-lab/reference/configuration/environment-variables.md`, still a
|
||||
// skeleton; re-anchor here once it is written. Until then this holds the
|
||||
// one property a prose table cannot: every documented variable is one the
|
||||
// code reads, so the manual cannot advertise a variable that does nothing.
|
||||
const sources = fg
|
||||
.sync('src/**/*.ts', { cwd: repoRoot, absolute: true })
|
||||
.flatMap((file) => [
|
||||
...fs
|
||||
.readFileSync(file, 'utf-8')
|
||||
.matchAll(/process\.env(?:\.([A-Za-z_][A-Za-z0-9_]*)|\[['"]([A-Za-z_][A-Za-z0-9_]*)['"]\])/g),
|
||||
])
|
||||
.map((match) => match[1] ?? match[2]);
|
||||
const readByCode = new Set(sources);
|
||||
|
||||
const documented = ENVIRONMENT.flatMap(([term]) => term.split(', '));
|
||||
expect(documented.filter((name) => !readByCode.has(name))).toEqual([]);
|
||||
});
|
||||
|
||||
it('documents the same environment variables as the CLI reference', () => {
|
||||
const documented = ENVIRONMENT.flatMap(([term]) => term.split(', '));
|
||||
|
||||
expect(documented).toEqual(tableTerms('Environment Variables'));
|
||||
it('documents the exit codes the CLI can produce', () => {
|
||||
// 0 and 1 are universal; 130 is the Ctrl-C code the prompts exit with, and
|
||||
// `docs-lab/reference/cli.md` records it per command (openspec init,
|
||||
// openspec config profile, openspec workset open).
|
||||
expect(EXIT_STATUS.map(([code]) => code)).toEqual(['0', '1', '130']);
|
||||
});
|
||||
|
||||
it('renders every entry into the page', () => {
|
||||
|
||||
Reference in New Issue
Block a user