@descent-vtt/spec-graph 0.12.1 → 0.13.1
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.
- package/CHANGELOG.md +32 -0
- package/README.md +6 -3
- package/bin/spec-graph.js +15 -0
- package/dist/cli.d.ts +19 -2
- package/dist/cli.js +35 -3
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -11,6 +11,38 @@ the family's
|
|
|
11
11
|
[versioning policy](https://github.com/DescentVTT/spec-core/blob/main/docs/adr/0009-versions-before-1-0.md)
|
|
12
12
|
records.
|
|
13
13
|
|
|
14
|
+
## 0.13.1
|
|
15
|
+
|
|
16
|
+
A reader that closed the output, as `| head` does, was answered with
|
|
17
|
+
`unexpected error:` and a stack; it is one line now, and still exit 2.
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- **A reader that closes the output is answered in one line, not a stack.**
|
|
22
|
+
`spec-graph graph | head` ended with `spec-graph: unexpected error: Error:
|
|
23
|
+
EPIPE: broken pipe, write` and a stack once `head` had left before the
|
|
24
|
+
graph was written; it now prints `spec-graph: stdout was closed before all
|
|
25
|
+
of the output was written` on stderr, from every command. The exit code is
|
|
26
|
+
2, as it was
|
|
27
|
+
([ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md)).
|
|
28
|
+
|
|
29
|
+
## 0.13.0
|
|
30
|
+
|
|
31
|
+
An error spec-graph did not expect ended the run with exit 1, which reads as
|
|
32
|
+
findings; it is exit 2 now, the run that could not be trusted.
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- **An error spec-graph did not expect ends the run with exit 2, not 1.** It
|
|
37
|
+
left the process as Node's uncaught error, and exit 1 reads as findings;
|
|
38
|
+
every command now reports it as `spec-graph: unexpected error:` with its
|
|
39
|
+
stack on stderr, nothing on stdout, and exit 2, as does an error thrown
|
|
40
|
+
where nothing waits for it, such as a pipe its reader closed
|
|
41
|
+
([ADR-0010](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0010-configuration-belongs-to-the-repository.md)).
|
|
42
|
+
Upgrading: a script that took exit 1 after a crash for findings now sees
|
|
43
|
+
2, "the tool could not run"; `main()` from the package resolves to 2 where
|
|
44
|
+
its promise rejected.
|
|
45
|
+
|
|
14
46
|
## 0.12.1
|
|
15
47
|
|
|
16
48
|
The README's commands gave `npx` the command's bare name, which on npm is
|
package/README.md
CHANGED
|
@@ -1048,7 +1048,10 @@ spec-graph diff <before> <after> Compare two JSON graph exports.
|
|
|
1048
1048
|
Exit codes: `0` clean, `1` findings, `2` the tool could not run. A mistyped flag
|
|
1049
1049
|
never masquerades as a passing build, and neither does a pattern that matches
|
|
1050
1050
|
nothing, a configuration file that did not load, or a `--baseline` that is not
|
|
1051
|
-
there.
|
|
1051
|
+
there. An error spec-graph did not expect is `2` as well, never findings: it is
|
|
1052
|
+
reported on stderr as `spec-graph: unexpected error:` with its stack. So is a
|
|
1053
|
+
stdout its reader closed before all of the output was written, as a pipeline
|
|
1054
|
+
into `head` does: the answer did not arrive, and one line on stderr says so.
|
|
1052
1055
|
|
|
1053
1056
|
**There is no `--watch`.** A full run on this repository takes 60 ms, so the
|
|
1054
1057
|
loop that would justify a resident process is one line of shell, and it belongs
|
|
@@ -1317,9 +1320,9 @@ tools share, whose code is copied into `src/vendor/` and checked by hash rather
|
|
|
1317
1320
|
than installed. So the whole package is auditable in an afternoon — and nothing
|
|
1318
1321
|
in the pipeline can take longer than its input.
|
|
1319
1322
|
|
|
1320
|
-
**Verified, not just covered.**
|
|
1323
|
+
**Verified, not just covered.** 2,084 tests; 99.4% statement and 99.8% line
|
|
1321
1324
|
coverage. Coverage says a line ran, so the suite is also held to a mutation
|
|
1322
|
-
score - 96.
|
|
1325
|
+
score - 96.39% over 8,553 mutants, measured by CI's full sweep, and a build
|
|
1323
1326
|
fails under 93 - because a vocabulary entry or a boundary
|
|
1324
1327
|
condition can be weakened by an ordinary-looking refactor without a single test
|
|
1325
1328
|
going red. [ADR-0007](https://github.com/DescentVTT/spec-graph/blob/main/docs/adr/0007-mutation-testing.md) is straight about what
|
package/bin/spec-graph.js
CHANGED
|
@@ -5,6 +5,21 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { pathToFileURL } from 'node:url';
|
|
7
7
|
|
|
8
|
+
// main answers the errors it awaits with exit 2. What nothing awaits - a
|
|
9
|
+
// stream's error, a timer's, a promise nobody holds - Node ends with exit 1,
|
|
10
|
+
// which a script reads as findings: the same answer for those.
|
|
11
|
+
//
|
|
12
|
+
// A reader that closed stdout, as `| head` does, is no defect, and it arrives
|
|
13
|
+
// here, as the stream's error: one line, with no stack to send a person
|
|
14
|
+
// looking for a bug. The code alone names it, since stdout and stderr are the
|
|
15
|
+
// only pipes spec-graph writes to, and a line about a closed stderr reaches
|
|
16
|
+
// nobody.
|
|
17
|
+
process.on('uncaughtException', (error) => {
|
|
18
|
+
const said = error?.code === 'EPIPE' ? 'stdout was closed before all of the output was written' : `unexpected error: ${error?.stack ?? error}`;
|
|
19
|
+
process.stderr.write(`spec-graph: ${said}\n`);
|
|
20
|
+
process.exit(2);
|
|
21
|
+
});
|
|
22
|
+
|
|
8
23
|
const entry = new URL('../dist/cli.js', import.meta.url);
|
|
9
24
|
|
|
10
25
|
let cli;
|
package/dist/cli.d.ts
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* change do to it (`diff`).
|
|
8
8
|
*
|
|
9
9
|
* Exit codes are the contract with CI: `0` clean, `1` findings, `2` the tool
|
|
10
|
-
* itself could not run. A usage mistake never masquerades as a passing build
|
|
10
|
+
* itself could not run. A usage mistake never masquerades as a passing build,
|
|
11
|
+
* and an error nothing here expected never as findings.
|
|
11
12
|
*/
|
|
12
13
|
import { type GraphFormat } from './report.js';
|
|
13
14
|
import { type AnyRuleId, type Severity } from './types.js';
|
|
@@ -80,5 +81,21 @@ export interface CliIO {
|
|
|
80
81
|
export declare const HELP = "spec-graph - turn Markdown specifications into a verifiable graph\n\nUSAGE\n spec-graph [check] [patterns...] [options]\n spec-graph query <selector|project:rule> [patterns...] [options]\n spec-graph graph [patterns...] [--graph-format dot|mermaid|json]\n spec-graph rules [rule-id] [--explain]\n spec-graph diff <before.json> <after.json> [--format human|json|markdown]\n\nCOMMANDS\n check Validate the specification graph. The default.\n query Run a selector - or a registered project rule, by its id - and\n print the matching paths.\n graph Export the graph for Graphviz, Mermaid, or another tool.\n rules List the diagnostics that will run, built in and project. Name one\n to see only that one; --explain adds its selector and the ADR that\n decided it.\n diff Compare two graph exports: documents added, removed, moved or\n accepted; relations added or removed; and obligations resolved or\n reopened where they can be told apart. Make both exports with the\n same spec-graph. Exits 0 whether anything changed or not.\n\nOPTIONS\n --root <dir> Directory the patterns resolve against. Without it,\n .spec-graph.json is looked for in the working\n directory and then upward as far as the repository,\n and the directory holding it becomes the root - so a\n run from a subdirectory reports what a run from the\n top reports. Paths typed on the command line stay\n relative to where they were typed.\n --ignore <glob> Skip paths. Repeatable.\n --ignore-ref <glob> Do not report these reference targets when they fail\n to resolve, for repositories where [[...]] tags a\n concept rather than naming a file. Repeatable.\n Suppresses findings only, never edges.\n --family <name> Families a bare identifier in prose may name. When\n given, everything else stays prose. Repeatable.\n --ignore-family <name> Families that are never citations - RFC when the repo\n cites RFC 2119 and keeps its own RFCs. Repeatable.\n --history <glob> Files that log what was decided rather than deciding\n it - journals, changelogs, minutes. Their links are\n still checked; their obligations are not. Repeatable.\n --baseline <file> Accept the findings recorded in this file and report\n only what is new since. The file has to be there: a\n path that reads nothing accepts nothing and says so\n nowhere. A \"baseline\" in the configuration may name a\n file not recorded yet; a path typed here may not.\n --record-baseline <f> Write today's findings to this file as accepted debt,\n and exit 0 without judging them.\n --ratchet Also fail when a baseline entry no longer occurs, so\n a paid-off exemption cannot outlive the defect.\n --no-config Ignore .spec-graph.json and the package.json key.\n --format <fmt> human, json, sarif, github, gitlab or markdown. sarif\n is the interchange format GitHub code scanning and\n editors already read, github the workflow commands a\n GitHub Actions log turns into annotations, and gitlab\n the Code Quality report a GitLab merge request reads,\n all three for check; markdown is a table for a\n pull-request comment or $GITHUB_STEP_SUMMARY, for\n check or diff (default: human)\n --graph-format <fmt> dot, mermaid or json (default: dot)\n --documents-only Leave items out of the exported graph\n --rule <id>=<severity> Override one rule: error, warn, info or off. A project\n rule is named in full: project:no-draft-dependency.\n Repeatable.\n --max <n> Show at most n findings (0 = no limit)\n --max-warnings <n> Fail when warnings exceed n (default: no limit)\n --strict Raise every warning to an error. An explicit --rule\n still wins, so --strict --rule x=warn exempts x.\n --color / --no-color Force colour on or off\n --ascii Use ASCII glyphs only\n --verbose Include parse problems, per-file detail, and every\n reference --ignore-ref or --ignore-family silenced\n -h, --help Show this help\n -v, --version Show the version\n\nSELECTORS\n A selector is a path through the graph.\n\n item[openness=open] -delegates-to-> document[phase=retired]\n document[phase=active] -assumes-> document[phase=retired]\n document =supersedes=> document\n *[path^=docs/adr] -contains-> item[state=accepted-debt]\n\n Node types: document, item, * (any)\n Attributes: id, kind, title, path, file, line, phase, status, receptivity,\n alias, document, state (or disposition), openness, section,\n text, body, evidence, conflicted, fm.<front-matter-key>\n Operators: = != ^= $= *= ~= and [attr] for \"is present\"\n ~= is a regular expression, matched by an automaton that cannot\n backtrack: linear in the subject, whatever the pattern. It reads\n the usual syntax minus backreferences and lookaround, which are\n not regular - both are refused when the selector is read, with\n the character pointed at\n Relations: -kind-> <-kind- =kind=> <=kind= (= forms are transitive)\n\nEXIT CODES\n 0 clean\n 1 findings at error severity (or over --max-warnings)\n 2 the tool could not run\n\nCONFIGURATION\n Anything repeated on every run belongs in the repository rather than in the\n command. spec-graph reads the first of these that exists:\n\n .spec-graph.json, spec-graph.config.json, or a \"spec-graph\" key in\n package.json\n\n { \"patterns\": [\"docs/**/*.md\"],\n \"ignoreReferences\": [\"trap *\"],\n \"ignoreFamilies\": [\"RFC\"],\n \"historyPatterns\": [\"**/JOURNAL_*.md\", \"archive/**\"],\n \"baseline\": \".spec-graph-baseline.json\",\n \"severities\": { \"self-reference\": \"off\" },\n \"strict\": true,\n \"maxRelated\": 3 }\n\n A flag always wins over the file, and list flags add to it rather than\n replacing it. maxRelated has no flag: it caps how many related locations one\n finding lists, 8 unless set, and 0 lists none.\n\n A problem in that file stops the run with exit 2, named on stderr: invalid\n JSON, an unknown key, a rule that does not compile. A configuration that did\n not load checks a different repository than the one configured, and would\n report that one as consistent. --no-config runs on defaults instead.\n\nPROJECT RULES\n A convention spec-graph never anticipated is a selector plus a sentence, and\n belongs in the same file. Every rule declared here runs beside the built-ins\n and is reported, baselined, escalated by --strict and silenced by --rule in\n exactly the same way.\n\n { \"rules\": {\n \"no-draft-dependency\": {\n \"query\": \"document[phase=active] -depends-on-> document[phase=draft]\",\n \"message\": \"{0} depends on {1}, which is still a draft\",\n \"hint\": \"wait for {1} to be accepted, or drop it from {0.path}\",\n \"severity\": \"error\" } } }\n\n query One selector, or a list of them read as a union.\n message The headline. {0} is the first node on the path, {1} the next;\n {1.phase} and {0.fm.owner} read any selector attribute.\n hint The next action. Optional, and templated the same way.\n severity error, warn, info or off. Defaults to warn - a rule a team has\n just written has not yet earned the right to fail their build.\n\n The id is the name with project: in front, which is why it can never collide\n with a built-in. A selector that does not parse, or a {2} the query can never\n reach, is reported when the file is read rather than found missing later.\n\n To see what one matches without copying its selector out of the file:\n\n spec-graph query project:no-draft-dependency --verbose\n\nADOPTING THIS ON AN OLD REPOSITORY\n Record what is already wrong, then report only what happens next:\n\n spec-graph check --record-baseline .spec-graph-baseline.json\n git add .spec-graph-baseline.json\n\n The file is keyed by specification and citation, not by line number, so it\n survives edits, moves and renames. Findings that stop occurring are reported\n so the file can be tightened; nothing new gets in.\n\nEXAMPLES\n spec-graph \"docs/**/*.md\"\n spec-graph check --rule self-reference=off --format json\n spec-graph query 'item[openness=open] -delegates-to-> document[phase=retired]'\n spec-graph check --ignore-ref \"trap *\" # [[trap 55]] tags a concept, not a file\n spec-graph graph --documents-only --graph-format mermaid > graph.mmd\n spec-graph check --history \"**/JOURNAL_*.md\" # a log is not a specification\n spec-graph check --baseline .spec-graph-baseline.json\n spec-graph rules --explain # including this repository's own\n spec-graph rules ghost-handover --explain # and why it exists\n spec-graph check --rule project:no-draft-dependency=off\n spec-graph query project:no-draft-dependency # what does that rule match?\n spec-graph check --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n spec-graph check --format github # annotations from an Actions log\n spec-graph check --format gitlab > gl-code-quality-report.json\n spec-graph diff base.json head.json --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n";
|
|
81
82
|
/** Parses argv into options. Throws {@link UsageError} on anything malformed. */
|
|
82
83
|
export declare function parseArgs(argv: readonly string[], cwd: string): CliOptions;
|
|
83
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* Runs the CLI and returns the process exit code. Never throws.
|
|
86
|
+
*
|
|
87
|
+
* An error nothing below expected is neither a finding nor a usage mistake:
|
|
88
|
+
* the answer cannot be trusted, which is the family contract's 2 (spec-core's
|
|
89
|
+
* ADR-0005). Rejected instead, it reached the launcher as Node's uncaught
|
|
90
|
+
* error, exit 1, which CI reads as findings. The stack is what makes a report
|
|
91
|
+
* of it something to act on, and it goes to stderr alone: a script that reads
|
|
92
|
+
* a document from stdout is handed no part of one.
|
|
93
|
+
*
|
|
94
|
+
* A write its reader had closed the pipe for is not such an error: the answer
|
|
95
|
+
* was not delivered, which is still 2, and nothing in spec-graph is at fault,
|
|
96
|
+
* so it is said in a line and no stack sends a person looking for a defect.
|
|
97
|
+
* The process's own streams report it as an event, which the launcher answers
|
|
98
|
+
* in the same words; here it is a caller's stream that throws it. It is read
|
|
99
|
+
* by its code: stdout and stderr are the only pipes spec-graph writes to.
|
|
100
|
+
*/
|
|
84
101
|
export declare function main(io?: CliIO): Promise<number>;
|
package/dist/cli.js
CHANGED
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
* change do to it (`diff`).
|
|
8
8
|
*
|
|
9
9
|
* Exit codes are the contract with CI: `0` clean, `1` findings, `2` the tool
|
|
10
|
-
* itself could not run. A usage mistake never masquerades as a passing build
|
|
10
|
+
* itself could not run. A usage mistake never masquerades as a passing build,
|
|
11
|
+
* and an error nothing here expected never as findings.
|
|
11
12
|
*/
|
|
12
13
|
import { applyBaseline, EMPTY_BASELINE, formatBaseline, parseBaseline, } from './baseline.js';
|
|
13
14
|
import { discoverConfig, loadConfig } from './config.js';
|
|
@@ -476,12 +477,43 @@ function parseCount(flag, value) {
|
|
|
476
477
|
/* -------------------------------------------------------------------------- */
|
|
477
478
|
/* Entry point */
|
|
478
479
|
/* -------------------------------------------------------------------------- */
|
|
479
|
-
/**
|
|
480
|
+
/**
|
|
481
|
+
* Runs the CLI and returns the process exit code. Never throws.
|
|
482
|
+
*
|
|
483
|
+
* An error nothing below expected is neither a finding nor a usage mistake:
|
|
484
|
+
* the answer cannot be trusted, which is the family contract's 2 (spec-core's
|
|
485
|
+
* ADR-0005). Rejected instead, it reached the launcher as Node's uncaught
|
|
486
|
+
* error, exit 1, which CI reads as findings. The stack is what makes a report
|
|
487
|
+
* of it something to act on, and it goes to stderr alone: a script that reads
|
|
488
|
+
* a document from stdout is handed no part of one.
|
|
489
|
+
*
|
|
490
|
+
* A write its reader had closed the pipe for is not such an error: the answer
|
|
491
|
+
* was not delivered, which is still 2, and nothing in spec-graph is at fault,
|
|
492
|
+
* so it is said in a line and no stack sends a person looking for a defect.
|
|
493
|
+
* The process's own streams report it as an event, which the launcher answers
|
|
494
|
+
* in the same words; here it is a caller's stream that throws it. It is read
|
|
495
|
+
* by its code: stdout and stderr are the only pipes spec-graph writes to.
|
|
496
|
+
*/
|
|
480
497
|
export async function main(io = {}) {
|
|
498
|
+
const err = io.stderr ?? ((text) => process.stderr.write(text));
|
|
499
|
+
try {
|
|
500
|
+
return await run(io, err);
|
|
501
|
+
}
|
|
502
|
+
catch (error) {
|
|
503
|
+
if (!(error instanceof Error))
|
|
504
|
+
err(`spec-graph: unexpected error: ${String(error)}\n`);
|
|
505
|
+
else if (error.code === 'EPIPE')
|
|
506
|
+
err('spec-graph: stdout was closed before all of the output was written\n');
|
|
507
|
+
else
|
|
508
|
+
err(`spec-graph: unexpected error: ${error.stack ?? error.message}\n`);
|
|
509
|
+
return EXIT_ERROR;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
/** One run of the command line, which rejects on an error it did not expect. */
|
|
513
|
+
async function run(io, err) {
|
|
481
514
|
const argv = io.argv ?? process.argv.slice(2);
|
|
482
515
|
const cwd = io.cwd ?? process.cwd();
|
|
483
516
|
const out = io.stdout ?? ((text) => process.stdout.write(text));
|
|
484
|
-
const err = io.stderr ?? ((text) => process.stderr.write(text));
|
|
485
517
|
const env = io.env ?? process.env;
|
|
486
518
|
let options;
|
|
487
519
|
try {
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The command line.\n *\n * Five verbs, because there are five questions worth asking of a specification\n * graph: is it consistent (`check`), what does it contain (`query`), what does\n * it look like (`graph`), what will you check for me (`rules`), and what did a\n * change do to it (`diff`).\n *\n * Exit codes are the contract with CI: `0` clean, `1` findings, `2` the tool\n * itself could not run. A usage mistake never masquerades as a passing build.\n */\n\nimport {\n applyBaseline,\n EMPTY_BASELINE,\n formatBaseline,\n parseBaseline,\n type Baseline,\n type StaleEntry,\n} from './baseline.js';\nimport { discoverConfig, loadConfig, type SpecGraphConfig } from './config.js';\nimport {\n DiffInputError,\n diffExports,\n formatDiffJson,\n formatDiffMarkdown,\n formatDiffText,\n parseGraphExport,\n type GraphExport,\n} from './diff.js';\nimport { isGlob, rebasePattern, underRoot } from './glob.js';\nimport { analyse, DEFAULT_PATTERNS, withDiagnostics, type AnalyseOptions, type AnalysisResult } from './runner.js';\nimport {\n formatGithub,\n formatGitlab,\n formatGraph,\n formatJson,\n formatMarkdown,\n formatReport,\n formatSarif,\n shouldUseAscii,\n shouldUseColor,\n type GraphFormat,\n} from './report.js';\nimport type { ProjectRule } from './project-rules.js';\nimport {\n DEFAULT_SEVERITIES,\n resolveStrict,\n RULE_DECISIONS,\n decisionUrl,\n RULE_DESCRIPTIONS,\n RULE_IDS,\n RULE_QUERIES,\n} from './rules.js';\nimport { isAbsolutePath, joinPosix, toPosix } from './paths.js';\nimport { formatRef } from './source.js';\nimport { execute, parseQuery, QueryError, renderMatch, type Match, type QuerySpec } from './select.js';\nimport { isProjectRule, type AnyRuleId, type RuleId, type Severity, type SpecNode } from './types.js';\nimport { displayWidth } from './vendor/spec-core/text/index.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_ERROR = 2;\n\nexport class UsageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UsageError';\n }\n}\n\nexport type Command = 'check' | 'query' | 'graph' | 'rules' | 'diff';\n\nexport interface CliOptions {\n readonly command: Command;\n readonly patterns: readonly string[];\n readonly root: string;\n /**\n * Whether `--root` was given.\n *\n * When it was, the caller has named the root and configuration is read from\n * exactly there. When it was not, the root is discovered - which is a\n * different question from what the root currently is. See ADR-0018.\n */\n readonly rootExplicit: boolean;\n readonly ignore: readonly string[];\n /** Reference targets to leave unreported when they do not resolve. */\n readonly ignoreReferences: readonly string[];\n /** Families a bare identifier may name. Empty means any family in the corpus. */\n readonly families: readonly string[];\n /** Families that are never citations. */\n readonly ignoreFamilies: readonly string[];\n /** Files that log what was decided rather than deciding it. */\n readonly historyPatterns: readonly string[];\n /** Accepted-debt file to read. `null` means none was asked for. */\n readonly baseline: string | null;\n /** Where to write the current findings as accepted debt. */\n readonly recordBaseline: string | null;\n /**\n * Fail when the baseline allows something that no longer happens.\n *\n * The other side of the ratchet, and off by default for the reason ADR-0012\n * gives: failing a build because somebody fixed something is a strange way to\n * encourage them. A team that has decided its debt only goes one way asks for\n * it explicitly, and then a dead exemption cannot outlive the defect.\n */\n readonly ratchet: boolean;\n /** Skip the repository configuration file entirely. */\n readonly noConfig: boolean;\n readonly format: 'human' | 'json' | 'sarif' | 'markdown' | 'gitlab' | 'github';\n readonly graphFormat: GraphFormat;\n readonly severities: Partial<Record<AnyRuleId, Severity>>;\n readonly color: boolean | null;\n readonly ascii: boolean | null;\n readonly verbose: boolean;\n readonly documentsOnly: boolean;\n readonly max: number;\n readonly maxWarnings: number;\n /** Raise every warning to an error. Explicit `--rule` overrides still win. */\n readonly strict: boolean;\n readonly selector: string | null;\n readonly help: boolean;\n readonly version: boolean;\n}\n\nexport interface CliIO {\n readonly argv?: readonly string[] | undefined;\n readonly cwd?: string | undefined;\n readonly stdout?: ((text: string) => void) | undefined;\n readonly stderr?: ((text: string) => void) | undefined;\n readonly env?: Readonly<Record<string, string | undefined>> | undefined;\n readonly isTTY?: boolean | undefined;\n}\n\nexport const HELP = `spec-graph - turn Markdown specifications into a verifiable graph\n\nUSAGE\n spec-graph [check] [patterns...] [options]\n spec-graph query <selector|project:rule> [patterns...] [options]\n spec-graph graph [patterns...] [--graph-format dot|mermaid|json]\n spec-graph rules [rule-id] [--explain]\n spec-graph diff <before.json> <after.json> [--format human|json|markdown]\n\nCOMMANDS\n check Validate the specification graph. The default.\n query Run a selector - or a registered project rule, by its id - and\n print the matching paths.\n graph Export the graph for Graphviz, Mermaid, or another tool.\n rules List the diagnostics that will run, built in and project. Name one\n to see only that one; --explain adds its selector and the ADR that\n decided it.\n diff Compare two graph exports: documents added, removed, moved or\n accepted; relations added or removed; and obligations resolved or\n reopened where they can be told apart. Make both exports with the\n same spec-graph. Exits 0 whether anything changed or not.\n\nOPTIONS\n --root <dir> Directory the patterns resolve against. Without it,\n .spec-graph.json is looked for in the working\n directory and then upward as far as the repository,\n and the directory holding it becomes the root - so a\n run from a subdirectory reports what a run from the\n top reports. Paths typed on the command line stay\n relative to where they were typed.\n --ignore <glob> Skip paths. Repeatable.\n --ignore-ref <glob> Do not report these reference targets when they fail\n to resolve, for repositories where [[...]] tags a\n concept rather than naming a file. Repeatable.\n Suppresses findings only, never edges.\n --family <name> Families a bare identifier in prose may name. When\n given, everything else stays prose. Repeatable.\n --ignore-family <name> Families that are never citations - RFC when the repo\n cites RFC 2119 and keeps its own RFCs. Repeatable.\n --history <glob> Files that log what was decided rather than deciding\n it - journals, changelogs, minutes. Their links are\n still checked; their obligations are not. Repeatable.\n --baseline <file> Accept the findings recorded in this file and report\n only what is new since. The file has to be there: a\n path that reads nothing accepts nothing and says so\n nowhere. A \"baseline\" in the configuration may name a\n file not recorded yet; a path typed here may not.\n --record-baseline <f> Write today's findings to this file as accepted debt,\n and exit 0 without judging them.\n --ratchet Also fail when a baseline entry no longer occurs, so\n a paid-off exemption cannot outlive the defect.\n --no-config Ignore .spec-graph.json and the package.json key.\n --format <fmt> human, json, sarif, github, gitlab or markdown. sarif\n is the interchange format GitHub code scanning and\n editors already read, github the workflow commands a\n GitHub Actions log turns into annotations, and gitlab\n the Code Quality report a GitLab merge request reads,\n all three for check; markdown is a table for a\n pull-request comment or $GITHUB_STEP_SUMMARY, for\n check or diff (default: human)\n --graph-format <fmt> dot, mermaid or json (default: dot)\n --documents-only Leave items out of the exported graph\n --rule <id>=<severity> Override one rule: error, warn, info or off. A project\n rule is named in full: project:no-draft-dependency.\n Repeatable.\n --max <n> Show at most n findings (0 = no limit)\n --max-warnings <n> Fail when warnings exceed n (default: no limit)\n --strict Raise every warning to an error. An explicit --rule\n still wins, so --strict --rule x=warn exempts x.\n --color / --no-color Force colour on or off\n --ascii Use ASCII glyphs only\n --verbose Include parse problems, per-file detail, and every\n reference --ignore-ref or --ignore-family silenced\n -h, --help Show this help\n -v, --version Show the version\n\nSELECTORS\n A selector is a path through the graph.\n\n item[openness=open] -delegates-to-> document[phase=retired]\n document[phase=active] -assumes-> document[phase=retired]\n document =supersedes=> document\n *[path^=docs/adr] -contains-> item[state=accepted-debt]\n\n Node types: document, item, * (any)\n Attributes: id, kind, title, path, file, line, phase, status, receptivity,\n alias, document, state (or disposition), openness, section,\n text, body, evidence, conflicted, fm.<front-matter-key>\n Operators: = != ^= $= *= ~= and [attr] for \"is present\"\n ~= is a regular expression, matched by an automaton that cannot\n backtrack: linear in the subject, whatever the pattern. It reads\n the usual syntax minus backreferences and lookaround, which are\n not regular - both are refused when the selector is read, with\n the character pointed at\n Relations: -kind-> <-kind- =kind=> <=kind= (= forms are transitive)\n\nEXIT CODES\n 0 clean\n 1 findings at error severity (or over --max-warnings)\n 2 the tool could not run\n\nCONFIGURATION\n Anything repeated on every run belongs in the repository rather than in the\n command. spec-graph reads the first of these that exists:\n\n .spec-graph.json, spec-graph.config.json, or a \"spec-graph\" key in\n package.json\n\n { \"patterns\": [\"docs/**/*.md\"],\n \"ignoreReferences\": [\"trap *\"],\n \"ignoreFamilies\": [\"RFC\"],\n \"historyPatterns\": [\"**/JOURNAL_*.md\", \"archive/**\"],\n \"baseline\": \".spec-graph-baseline.json\",\n \"severities\": { \"self-reference\": \"off\" },\n \"strict\": true,\n \"maxRelated\": 3 }\n\n A flag always wins over the file, and list flags add to it rather than\n replacing it. maxRelated has no flag: it caps how many related locations one\n finding lists, 8 unless set, and 0 lists none.\n\n A problem in that file stops the run with exit 2, named on stderr: invalid\n JSON, an unknown key, a rule that does not compile. A configuration that did\n not load checks a different repository than the one configured, and would\n report that one as consistent. --no-config runs on defaults instead.\n\nPROJECT RULES\n A convention spec-graph never anticipated is a selector plus a sentence, and\n belongs in the same file. Every rule declared here runs beside the built-ins\n and is reported, baselined, escalated by --strict and silenced by --rule in\n exactly the same way.\n\n { \"rules\": {\n \"no-draft-dependency\": {\n \"query\": \"document[phase=active] -depends-on-> document[phase=draft]\",\n \"message\": \"{0} depends on {1}, which is still a draft\",\n \"hint\": \"wait for {1} to be accepted, or drop it from {0.path}\",\n \"severity\": \"error\" } } }\n\n query One selector, or a list of them read as a union.\n message The headline. {0} is the first node on the path, {1} the next;\n {1.phase} and {0.fm.owner} read any selector attribute.\n hint The next action. Optional, and templated the same way.\n severity error, warn, info or off. Defaults to warn - a rule a team has\n just written has not yet earned the right to fail their build.\n\n The id is the name with project: in front, which is why it can never collide\n with a built-in. A selector that does not parse, or a {2} the query can never\n reach, is reported when the file is read rather than found missing later.\n\n To see what one matches without copying its selector out of the file:\n\n spec-graph query project:no-draft-dependency --verbose\n\nADOPTING THIS ON AN OLD REPOSITORY\n Record what is already wrong, then report only what happens next:\n\n spec-graph check --record-baseline .spec-graph-baseline.json\n git add .spec-graph-baseline.json\n\n The file is keyed by specification and citation, not by line number, so it\n survives edits, moves and renames. Findings that stop occurring are reported\n so the file can be tightened; nothing new gets in.\n\nEXAMPLES\n spec-graph \"docs/**/*.md\"\n spec-graph check --rule self-reference=off --format json\n spec-graph query 'item[openness=open] -delegates-to-> document[phase=retired]'\n spec-graph check --ignore-ref \"trap *\" # [[trap 55]] tags a concept, not a file\n spec-graph graph --documents-only --graph-format mermaid > graph.mmd\n spec-graph check --history \"**/JOURNAL_*.md\" # a log is not a specification\n spec-graph check --baseline .spec-graph-baseline.json\n spec-graph rules --explain # including this repository's own\n spec-graph rules ghost-handover --explain # and why it exists\n spec-graph check --rule project:no-draft-dependency=off\n spec-graph query project:no-draft-dependency # what does that rule match?\n spec-graph check --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n spec-graph check --format github # annotations from an Actions log\n spec-graph check --format gitlab > gl-code-quality-report.json\n spec-graph diff base.json head.json --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n`;\n\nconst SEVERITIES: readonly Severity[] = ['error', 'warn', 'info', 'off'];\n\n/** Parses argv into options. Throws {@link UsageError} on anything malformed. */\nexport function parseArgs(argv: readonly string[], cwd: string): CliOptions {\n const args = [...argv];\n let command: Command = 'check';\n\n const first = args[0];\n if (first === 'check' || first === 'query' || first === 'graph' || first === 'rules' || first === 'diff') {\n command = first;\n args.shift();\n }\n\n const patterns: string[] = [];\n const ignore: string[] = [];\n const ignoreReferences: string[] = [];\n const families: string[] = [];\n const ignoreFamilies: string[] = [];\n const historyPatterns: string[] = [];\n let noConfig = false;\n const severities: Partial<Record<AnyRuleId, Severity>> = {};\n let root = cwd;\n let rootExplicit = false;\n let format: CliOptions['format'] = 'human';\n let graphFormat: GraphFormat = 'dot';\n let color: boolean | null = null;\n let ascii: boolean | null = null;\n let verbose = false;\n let documentsOnly = false;\n let max = 0;\n let maxWarnings = -1;\n let strict = false;\n let baseline: string | null = null;\n let ratchet = false;\n let recordBaseline: string | null = null;\n let selector: string | null = null;\n let help = false;\n let version = false;\n\n const next = (flag: string, index: number): string => {\n const value = args[index + 1];\n if (value === undefined) throw new UsageError(`${flag} needs a value`);\n return value;\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i] as string;\n\n if (arg === '--') {\n patterns.push(...args.slice(i + 1));\n break;\n }\n\n if (!arg.startsWith('-')) {\n if (command === 'query' && selector === null) selector = arg;\n else patterns.push(arg);\n continue;\n }\n\n switch (arg) {\n case '-h':\n case '--help':\n help = true;\n break;\n case '-v':\n case '--version':\n version = true;\n break;\n case '--verbose':\n verbose = true;\n break;\n case '--color':\n color = true;\n break;\n case '--no-color':\n color = false;\n break;\n case '--ascii':\n ascii = true;\n break;\n case '--documents-only':\n documentsOnly = true;\n break;\n case '--strict':\n strict = true;\n break;\n case '--ratchet':\n ratchet = true;\n break;\n case '--explain':\n verbose = true;\n break;\n case '--root':\n root = next(arg, i);\n rootExplicit = true;\n i += 1;\n break;\n case '--ignore':\n ignore.push(next(arg, i));\n i += 1;\n break;\n case '--ignore-ref':\n ignoreReferences.push(next(arg, i));\n i += 1;\n break;\n case '--family':\n families.push(next(arg, i));\n i += 1;\n break;\n case '--ignore-family':\n ignoreFamilies.push(next(arg, i));\n i += 1;\n break;\n case '--history':\n historyPatterns.push(next(arg, i));\n i += 1;\n break;\n case '--baseline':\n baseline = next(arg, i);\n i += 1;\n break;\n case '--record-baseline':\n recordBaseline = next(arg, i);\n i += 1;\n break;\n case '--no-config':\n noConfig = true;\n break;\n case '--format': {\n const value = next(arg, i);\n if (\n value !== 'human' &&\n value !== 'json' &&\n value !== 'sarif' &&\n value !== 'markdown' &&\n value !== 'gitlab' &&\n value !== 'github'\n ) {\n throw new UsageError(`--format must be human, json, sarif, markdown, gitlab or github, got \"${value}\"`);\n }\n format = value;\n i += 1;\n break;\n }\n case '--graph-format': {\n const value = next(arg, i);\n if (value !== 'dot' && value !== 'mermaid' && value !== 'json') {\n throw new UsageError(`--graph-format must be dot, mermaid or json, got \"${value}\"`);\n }\n graphFormat = value;\n i += 1;\n break;\n }\n case '--rule': {\n const value = next(arg, i);\n const equals = value.indexOf('=');\n if (equals === -1) throw new UsageError(`--rule expects <id>=<severity>, got \"${value}\"`);\n const id = value.slice(0, equals) as AnyRuleId;\n const level = value.slice(equals + 1) as Severity;\n // A project rule is taken on its namespace here and checked for real\n // once the configuration defining it has been read: arguments are\n // parsed before any file is opened, and a flag has to be usable on that\n // first pass whatever the repository turns out to declare.\n if (!RULE_IDS.includes(id as RuleId) && !isProjectRule(id)) {\n throw new UsageError(`unknown rule \"${id}\"\\n known rules: ${RULE_IDS.join(', ')}`);\n }\n if (!SEVERITIES.includes(level)) {\n throw new UsageError(`unknown severity \"${level}\", expected one of: ${SEVERITIES.join(', ')}`);\n }\n severities[id] = level;\n i += 1;\n break;\n }\n case '--max': {\n max = parseCount(arg, next(arg, i));\n i += 1;\n break;\n }\n case '--max-warnings': {\n maxWarnings = parseCount(arg, next(arg, i));\n i += 1;\n break;\n }\n default:\n throw new UsageError(`unknown option \"${arg}\"\\n run \"spec-graph --help\" to see the available options`);\n }\n }\n\n // SARIF, GitHub's workflow commands and GitLab's Code Quality are reports\n // about findings, and only `check` produces those. Falling back to JSON would\n // hand a pipeline something its uploader rejects with a message about a\n // schema rather than about the command that was run. Markdown is for a pull\n // request, which reads a check or a diff.\n if ((format === 'sarif' || format === 'gitlab' || format === 'github') && command !== 'check' && !help && !version) {\n throw new UsageError(`--format ${format} reports findings, so it belongs to check, not to ${command}`);\n }\n if (format === 'markdown' && command !== 'check' && command !== 'diff' && !help && !version) {\n throw new UsageError(`--format markdown is a report for a pull request, so it belongs to check or diff, not to ${command}`);\n }\n\n if (command === 'diff' && patterns.length !== 2 && !help && !version) {\n throw new UsageError(\n 'diff compares two graph exports, for example:\\n spec-graph diff base.json head.json\\n (make each with spec-graph graph --graph-format json)',\n );\n }\n\n if (command === 'query' && selector === null && !help && !version) {\n throw new UsageError('query needs a selector, for example:\\n spec-graph query \\'item[openness=open]\\'');\n }\n\n return {\n command,\n patterns,\n root,\n rootExplicit,\n ignore,\n ignoreReferences,\n families,\n ignoreFamilies,\n historyPatterns,\n baseline,\n ratchet,\n recordBaseline,\n noConfig,\n format,\n graphFormat,\n severities,\n color,\n ascii,\n verbose,\n documentsOnly,\n max,\n maxWarnings,\n strict,\n selector,\n help,\n version,\n };\n}\n\nfunction count(n: number, word: string, many?: string): string {\n return `${n} ${n === 1 ? word : (many ?? `${word}s`)}`;\n}\n\ninterface HeldBaseline {\n readonly baseline: Baseline;\n readonly problems: readonly string[];\n /** What stopped the file being read, when something did. */\n readonly unread: { readonly missing: boolean; readonly message: string } | null;\n}\n\n/**\n * Reads a baseline file.\n *\n * Absent and unreadable are different answers, and the caller needs both. A\n * repository that has not recorded a baseline yet declares the path before the\n * file exists, and an empty baseline accepts nothing, which is what that run\n * should report (ADR-0012). Anything else - a directory, a permission, a path\n * that went somewhere unintended - is the tool failing to do what it was told.\n */\nasync function readBaseline(path: string, source: string): Promise<HeldBaseline> {\n let raw: string;\n try {\n const { readFile } = await import('node:fs/promises');\n raw = await readFile(path, 'utf8');\n } catch (error) {\n const failure = error as Error & { code?: string };\n return {\n baseline: EMPTY_BASELINE,\n problems: [],\n unread: { missing: failure.code === 'ENOENT', message: failure.message },\n };\n }\n return { ...parseBaseline(raw, source), unread: null };\n}\n\nfunction parseCount(flag: string, value: string): number {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0 || String(parsed) !== value.trim()) {\n throw new UsageError(`${flag} expects a non-negative whole number, got \"${value}\"`);\n }\n return parsed;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Entry point */\n/* -------------------------------------------------------------------------- */\n\n/** Runs the CLI and returns the process exit code. Never throws. */\nexport async function main(io: CliIO = {}): Promise<number> {\n const argv = io.argv ?? process.argv.slice(2);\n const cwd = io.cwd ?? process.cwd();\n const out = io.stdout ?? ((text) => process.stdout.write(text));\n const err = io.stderr ?? ((text) => process.stderr.write(text));\n const env = io.env ?? process.env;\n\n let options: CliOptions;\n try {\n options = parseArgs(argv, cwd);\n } catch (error) {\n // parseArgs throws nothing but a UsageError for any argv, so no test can\n // tell this check from `true`: the rethrow is for a bug in the parser.\n if (error instanceof UsageError) {\n err(`spec-graph: ${error.message}\\n`);\n return EXIT_ERROR;\n }\n throw error;\n }\n\n if (options.help) {\n out(HELP);\n return EXIT_OK;\n }\n if (options.version) {\n out(`${await readVersion()}\\n`);\n return EXIT_OK;\n }\n\n // A diff reads two files and nothing else: no configuration and no corpus, so\n // it can compare exports from a checkout it is not running in.\n if (options.command === 'diff') return runDiff(options.patterns as readonly [string, string], cwd, options.format, out, err);\n\n const color = options.color ?? shouldUseColor({ isTTY: io.isTTY, env });\n const ascii = options.ascii ?? shouldUseAscii({ env });\n\n // Configuration is what is true of the repository; a flag is somebody\n // overriding it for one run. So a flag always wins, and lists add rather than\n // replace - a `--ignore-ref` on the command line is one more exclusion, not a\n // decision to throw away the ones the repository already declared.\n //\n // Read before `rules` prints anything, so that command lists the conventions\n // this repository will actually check rather than the ones spec-graph ships.\n const loaded = options.noConfig\n ? { config: {} as SpecGraphConfig, source: null, problems: [], root: options.root }\n : options.rootExplicit\n ? { ...loadConfig(options.root), root: options.root }\n : discoverConfig(options.root);\n for (const problem of loaded.problems) err(`spec-graph: ${problem}\\n`);\n // A configuration that did not load means a run against a different\n // repository than the one configured - other patterns, other rules, other\n // severities - and the verdict printed is about that one. Reporting the\n // problem and carrying on gave a green build with `ok: true` from a file with\n // a typo in it, which is the one answer a checker must never reach by\n // accident, and the same mistake in a `--rule` has always exited 2. The\n // findings are one flag away, and the flag is named. Amends ADR-0010.\n if (loaded.problems.length > 0) {\n err(\n `spec-graph: ${count(loaded.problems.length, 'problem')} in the configuration, so nothing was checked\\n fix it, or run again with --no-config to check on defaults\\n`,\n );\n return EXIT_ERROR;\n }\n const file = loaded.config;\n const projectRules = file.rules ?? [];\n const root = loaded.root;\n // How far the root moved up. Everything a repository declares about itself is\n // relative to the root; everything typed on the command line is relative to\n // where it was typed, and this is what keeps those two readings apart.\n const here = below(root, options.root);\n\n // A `--rule` naming a project rule that does not exist is a flag that\n // silently does nothing, and nothing in the output would distinguish that\n // from a rule that ran and found none.\n for (const id of Object.keys(options.severities)) {\n if (!isProjectRule(id as AnyRuleId) || projectRules.some((rule) => rule.id === id)) continue;\n err(`spec-graph: unknown rule \"${id}\"\\n ${knownProjectRules(projectRules)}\\n`);\n return EXIT_ERROR;\n }\n\n if (options.command === 'rules') {\n // The one positional argument `rules` can take is a rule id: this command\n // reads no files, so a pattern here would be a word with nowhere to go.\n const wanted = options.patterns[0];\n if (wanted !== undefined && !RULE_IDS.includes(wanted as RuleId) && !projectRules.some((rule) => rule.id === wanted)) {\n err(\n `spec-graph: unknown rule \"${wanted}\"\\n known rules: ${RULE_IDS.join(', ')}\\n ${knownProjectRules(projectRules)}\\n`,\n );\n return EXIT_ERROR;\n }\n out(renderRules(options.verbose, await readVersion(), projectRules, wanted ?? null, loaded.source));\n return EXIT_OK;\n }\n\n // Named the way the reader would have to type it, because a discovered\n // configuration is often not the one in front of them.\n //\n // On stderr, with everything else this run says about itself. stdout is the\n // report, and in three of the four formats it is a document: a line above it\n // made `--verbose --format json` unparseable and handed `--format sarif` to a\n // code-scanning uploader that rejects it over a schema.\n if (options.verbose && loaded.source !== null) err(`configuration: ${fromHere(here, loaded.source)}\\n`);\n\n // A bare `--ignore` name prunes a directory of that name at any depth, the\n // way a .gitignore line does, so it means the same thing wherever it was\n // typed. Every other pattern is matched against the repository-relative\n // path, a bare `--history` name included, and leaving one alone would make\n // it silently match something else or nothing. A pattern that cannot be\n // rebased onto the root stops the run as one the dialect refuses does,\n // named as it was typed.\n let typed;\n try {\n typed = {\n patterns: options.patterns.map((pattern) => anchor(here, pattern)),\n ignore: options.ignore.map((pattern) => anchorPath(here, pattern)),\n history: options.historyPatterns.map((pattern) => anchor(here, pattern)),\n };\n } catch (error) {\n err(`spec-graph: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n const patterns = typed.patterns.length > 0 ? typed.patterns : (file.patterns ?? DEFAULT_PATTERNS);\n const severityOverrides = { ...(file.severities ?? {}), ...options.severities };\n const { severities, escalated } = resolveStrict(\n severityOverrides,\n options.strict || (file.strict ?? false),\n projectRules,\n );\n\n const analyseOptions: AnalyseOptions = {\n root,\n patterns,\n // A stray entry in one of the empty fallbacks below changes a run only if\n // it matches a path, a target or a family some corpus has, so the mutants\n // that plant one survive every corpus but one built for them - except in\n // `families`, where any entry is an allowlist.\n ignore: [...(file.ignore ?? []), ...typed.ignore],\n ignoreReferences: [...(file.ignoreReferences ?? []), ...options.ignoreReferences],\n families: [...(file.families ?? []), ...options.families],\n ignoreFamilies: [...(file.ignoreFamilies ?? []), ...options.ignoreFamilies],\n historyPatterns: [...(file.historyPatterns ?? []), ...typed.history],\n severities,\n projectRules,\n maxRelated: file.maxRelated,\n };\n\n let result;\n try {\n result = await analyse(analyseOptions);\n } catch (error) {\n err(`spec-graph: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n\n if (result.files.length === 0) {\n err(\n `spec-graph: no specifications matched ${\n patterns.map((p) => `\"${p}\"`).join(', ')\n }\\n looked under ${root}\\n`,\n );\n return EXIT_ERROR;\n }\n\n switch (options.command) {\n case 'graph':\n out(\n formatGraph(result.graph, options.graphFormat, {\n documentsOnly: options.documentsOnly,\n generator: { name: 'spec-graph', version: await readVersion() },\n }),\n );\n return EXIT_OK;\n\n case 'query': {\n const selector = options.selector as string;\n let queries: readonly QuerySpec[];\n\n // A registered rule is asked for by name, because by the time this runs\n // it is already compiled and the alternative is copying its selector out\n // of the configuration file by hand. The namespace cannot collide with\n // the grammar: no selector begins with a word and a colon. See ADR-0016.\n if (isProjectRule(selector as AnyRuleId)) {\n const rule = projectRules.find((candidate) => candidate.id === selector);\n if (rule === undefined) {\n err(`spec-graph: unknown rule \"${selector}\"\\n ${knownProjectRules(projectRules)}\\n`);\n return EXIT_ERROR;\n }\n queries = rule.queries;\n // Beside the report rather than inside it, for the reason the\n // configuration line above is: `--format json` is a document.\n if (options.verbose) for (const source of rule.sources) err(`${rule.id}: ${source}\\n`);\n } else {\n try {\n queries = [parseQuery(selector)];\n } catch (error) {\n // Every selector that fails to parse fails as a QueryError, so no\n // test can tell this check from `true`.\n if (error instanceof QueryError) {\n err(renderQueryError(selector, error));\n return EXIT_ERROR;\n }\n throw error;\n }\n }\n\n const matches = union(result.graph, queries);\n // renderMatches asks only whether this is 'json', so the other word is a\n // name for the reader and emptying it changes nothing.\n out(renderMatches(matches, options.format === 'json' ? 'json' : 'human', result.graph.nodes.size));\n return matches.length > 0 ? EXIT_OK : EXIT_FAILED;\n }\n\n default: {\n // Recording is not checking. It writes down what is wrong today so that\n // tomorrow can be compared against it, and says nothing about whether\n // today is acceptable - so it reports what it wrote and exits clean.\n if (options.recordBaseline !== null) {\n const text = formatBaseline(result.graph, result.diagnostics);\n try {\n const { writeFile } = await import('node:fs/promises');\n // Node writes a string as UTF-8 when the encoding is empty too, so\n // emptying this literal writes the same bytes.\n await writeFile(underRoot(root, anchorFile(here, options.recordBaseline)), text, 'utf8');\n } catch (error) {\n err(`spec-graph: cannot write ${options.recordBaseline}: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n const entries = (JSON.parse(text) as Baseline).findings.length;\n out(\n `recorded ${count(result.diagnostics.length, 'finding')} as ${count(entries, 'entry', 'entries')} in ${options.recordBaseline}\\n`,\n );\n return EXIT_OK;\n }\n\n const source = options.baseline === null ? (file.baseline ?? null) : anchorFile(here, options.baseline);\n const ratchet = options.ratchet || file.ratchet === true;\n let reported = result;\n let note:\n | {\n source: string;\n suppressed: number;\n stale: number;\n ratchet: boolean;\n entries: readonly StaleEntry[];\n }\n | undefined;\n let recordAs: string | undefined;\n if (source !== null) {\n const held = await readBaseline(underRoot(root, source), source);\n // A path typed on the command line says the file is there for this run.\n // Read as an empty baseline instead, a typo in it reports every accepted\n // finding as new, and passes --ratchet with nothing left to be stale.\n // A path in the configuration is the case ADR-0012 argued for, and only\n // where the file is absent: unreadable is unreadable wherever the path\n // was written.\n if (held.unread !== null && (options.baseline !== null || !held.unread.missing)) {\n err(`spec-graph: cannot read the baseline ${source}: ${held.unread.message}\\n`);\n return EXIT_ERROR;\n }\n // Quiet by default, because a configured path legitimately precedes the\n // first --record-baseline. Under --verbose, where parse problems already\n // go, a path that reads nothing says so rather than accepting nothing.\n if (held.unread !== null && options.verbose) {\n err(`spec-graph: baseline ${source} is not there, so nothing is accepted\\n`);\n }\n for (const problem of held.problems) err(`spec-graph: ${problem}\\n`);\n const outcome = applyBaseline(result.graph, result.diagnostics, held.baseline);\n reported = withDiagnostics(result, outcome.kept);\n note = {\n source,\n suppressed: outcome.suppressed,\n stale: outcome.stale.length,\n ratchet,\n entries: outcome.stale,\n };\n // The command that re-records it is typed where this run was: a path\n // typed on this command line is relative to there already, and the\n // configuration's is from the root.\n recordAs = options.baseline ?? fromHere(here, source);\n // Listed rather than counted when the run turns on them: a number is\n // enough to know the file has slack, and not enough to strike it. On\n // stdout only for a human - the structured formats carry the same rows\n // inside the document, where a stray line is the difference between\n // parsing and not.\n if (options.format === 'human' && (options.verbose || ratchet)) {\n for (const entry of outcome.stale) {\n const subject = entry.subject === '' ? '' : ` \"${entry.subject}\"`;\n const why = entry.reason === 'gone' ? ` - ${entry.document} is not in this corpus` : '';\n out(` ${entry.reason}: ${entry.rule} ${entry.document}${subject}${why}\\n`);\n }\n }\n }\n const looseBaseline = note !== undefined && note.ratchet && note.stale > 0;\n\n const reporterOptions = { verbose: options.verbose, max: options.max, escalated, baseline: note, recordAs };\n out(\n options.format === 'sarif'\n ? formatSarif(reported, reported.graph, { version: await readVersion(), escalated, projectRules })\n : options.format === 'gitlab'\n ? formatGitlab(reported, { escalated })\n : options.format === 'github'\n ? formatGithub(reported, { escalated })\n : options.format === 'json'\n ? formatJson(reported, { escalated, baseline: note })\n : options.format === 'markdown'\n ? formatMarkdown(reported, reporterOptions)\n : `${formatReport(reported, { color, ascii, ...reporterOptions })}\\n`,\n );\n if (!reported.ok || looseBaseline) return EXIT_FAILED;\n if (options.maxWarnings >= 0 && reported.summary.warnings > options.maxWarnings) return EXIT_FAILED;\n return EXIT_OK;\n }\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Rendering */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Where the working directory sits below the root, or `''` when it is the root.\n *\n * Discovery only ever walks upward, so the root is always a prefix of the\n * directory the command was typed in, and this is a slice rather than path\n * arithmetic.\n */\nfunction below(root: string, from: string): string {\n const start = toPosix(from).replace(/[/]+$/, '');\n // The prefix test only guards a root that is neither `start` nor above it,\n // which nothing returns; for `start` itself, slicing past its end is `''`\n // too, so emptying the prefix changes nothing a run can reach.\n return start.startsWith(`${root}/`) ? start.slice(root.length + 1) : '';\n}\n\n/**\n * A path from the root, as it is typed in `here`: one `../` for each directory\n * `here` is below the root. An absolute path reads the same from anywhere.\n */\nfunction fromHere(here: string, path: string): string {\n return here === '' || isAbsolutePath(path) ? path : `${'../'.repeat(here.split('/').length)}${path}`;\n}\n\n/**\n * Re-anchors a pattern typed in `prefix` so it reads from the root, brace\n * alternative by brace alternative, or throws naming it as typed.\n *\n * `../docs` typed one directory down is the root's `docs`. That is arithmetic\n * on two relative paths, and this is the one place both are known; left in the\n * pattern, it reads as a glob climbing out of the root, which is refused.\n *\n * An absolute path is left alone: it was not relative to anywhere, so moving\n * the root cannot change what it means. spec-core roots a leading `/` and\n * knows no drive letter, so `C:/docs` is told apart here.\n */\nfunction anchor(prefix: string, pattern: string): string {\n return isAbsolutePath(pattern) ? pattern : rebasePattern(pattern, prefix);\n}\n\n/**\n * The same, for an `--ignore`, where a bare name is a directory at any depth.\n * Only the walk reads a name that way; a `--history` pattern is matched as a\n * whole path, and goes to {@link anchor}.\n */\nfunction anchorPath(prefix: string, pattern: string): string {\n return isGlob(pattern) || pattern.includes('/') ? anchor(prefix, pattern) : pattern;\n}\n\n/**\n * Re-anchors a file typed in `prefix`, a baseline, so it reads from the root.\n *\n * A file name is not a pattern: a brace or a `!` in it is a character, which\n * {@link anchor} would read as syntax, and a baseline outside the repository\n * is as good as one inside, so a `..` past the root is a path like any other.\n * At the root the file is named as it was typed.\n */\nfunction anchorFile(prefix: string, file: string): string {\n return prefix === '' || isAbsolutePath(file) ? file : joinPosix(prefix, toPosix(file));\n}\n\nfunction knownProjectRules(projectRules: readonly ProjectRule[]): string {\n const known = projectRules.map((rule) => rule.id);\n return known.length > 0 ? `project rules here: ${known.join(', ')}` : 'this repository defines no project rules';\n}\n\n/**\n * Runs a list of selectors as one result set.\n *\n * A project rule is a union of its selectors, and a path that two of them both\n * find is one path - the same dedupe `projectFindings` does, so that asking\n * this command what a rule matches answers with the set `check` reports on.\n */\nfunction union(graph: AnalysisResult['graph'], queries: readonly QuerySpec[]): Match[] {\n // `execute` already returns each path of nodes once, keyed the way this is,\n // so one selector needs no second pass and sending it through one anyway\n // returns the same matches.\n if (queries.length === 1) return execute(graph, queries[0] as QuerySpec);\n const seen = new Set<string>();\n const matches: Match[] = [];\n for (const spec of queries) {\n for (const match of execute(graph, spec)) {\n const key = match.nodes.map((node) => node.id).join('>');\n if (seen.has(key)) continue;\n seen.add(key);\n matches.push(match);\n }\n }\n return matches;\n}\n\ninterface RuleRow {\n readonly id: string;\n readonly severity: Severity;\n readonly description: string;\n /** The selector, where the rule is one. */\n readonly query: string | undefined;\n /** Where the reasoning is: an ADR, or the file that declared the rule. */\n readonly decided: string;\n}\n\nfunction renderRules(\n explain: boolean,\n version: string,\n projectRules: readonly ProjectRule[],\n only: string | null,\n configSource: string | null,\n): string {\n // A project rule is described by the selector it is, because that is what it\n // is - its message is a template, and a template is not a description.\n const rows: RuleRow[] = [\n ...RULE_IDS.map((id): RuleRow => ({\n id,\n severity: DEFAULT_SEVERITIES[id],\n description: RULE_DESCRIPTIONS[id],\n query: RULE_QUERIES[id],\n decided: decisionUrl(RULE_DECISIONS[id], version),\n })),\n ...projectRules.map((rule): RuleRow => ({\n id: rule.id,\n severity: rule.severity,\n description: rule.sources[0] as string,\n query: rule.sources.slice(1).join(' | ') || undefined,\n // A project rule only exists because a file declared it, so the source\n // is never null here and the first branch is for the type alone.\n decided: configSource === null ? `rules.${rule.name}` : `${configSource}: rules.${rule.name}`,\n })),\n ];\n\n const shown = only === null ? rows : rows.filter((row) => row.id === only);\n const width = Math.max(...shown.map((row) => row.id.length));\n const lines: string[] = [];\n for (const row of shown) {\n lines.push(`${row.id.padEnd(width)} ${row.severity.padEnd(5)} ${row.description}`);\n if (!explain) continue;\n if (row.query) lines.push(`${' '.repeat(width)} ${row.query}`);\n // Where the decision is written down, rather than a second copy of it. A\n // reader who has just been told their document is inconsistent is entitled\n // to know who decided that and why, and the ADR is the answer.\n lines.push(`${' '.repeat(width)} ${row.decided}`);\n }\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction renderMatches(matches: readonly Match[], format: 'human' | 'json', total: number): string {\n if (format === 'json') {\n return `${JSON.stringify(\n {\n version: 1,\n count: matches.length,\n matches: matches.map((match) => ({\n nodes: match.nodes.map((node) => nodeSummary(node)),\n edges: match.edges.map((edge) => ({\n kind: edge.kind,\n from: edge.from,\n to: edge.to,\n file: edge.declaredAt.file,\n line: edge.declaredAt.span.start.line,\n })),\n })),\n },\n null,\n 2,\n )}\\n`;\n }\n\n if (matches.length === 0) return `no matches (searched ${total} nodes)\\n`;\n\n const lines: string[] = [];\n for (const match of matches) {\n const head = match.nodes[0] as SpecNode;\n const tail = match.nodes[match.nodes.length - 1] as SpecNode;\n lines.push(renderMatch(match));\n lines.push(` ${formatRef(head.at)}${match.nodes.length > 1 ? ` ${formatRef(tail.at)}` : ''}`);\n }\n lines.push(`${matches.length} ${matches.length === 1 ? 'match' : 'matches'}`);\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction nodeSummary(node: SpecNode): Record<string, unknown> {\n return {\n id: node.id,\n kind: node.kind,\n title: node.title,\n file: node.at.file,\n line: node.at.span.start.line,\n ...(node.kind === 'document' ? { phase: node.phase } : { state: node.disposition, openness: node.openness }),\n };\n}\n\n/**\n * Points at the exact character of a selector that failed to parse, in the\n * columns a terminal draws: a Han character before it takes two, so an offset\n * counted in UTF-16 units put the caret short of it. The offset is a position\n * the parser reached, never negative, so it needs no floor.\n */\nfunction renderQueryError(selector: string, error: QueryError): string {\n const caret = `${' '.repeat(displayWidth(selector.slice(0, error.offset)))}^`;\n return `spec-graph: ${error.message}\\n ${selector}\\n ${caret}\\n`;\n}\n\n/**\n * Compares two exports.\n *\n * Exit 0 whether anything changed or not: a diff describes, and `check` gates. A\n * diff that failed on a removed relation would fail every legitimate\n * supersession (ADR-0020).\n */\nasync function runDiff(\n paths: readonly [string, string],\n cwd: string,\n format: CliOptions['format'],\n out: (text: string) => void,\n err: (text: string) => void,\n): Promise<number> {\n const { readFile } = await import('node:fs/promises');\n const { resolve } = await import('node:path');\n const sides: GraphExport[] = [];\n for (const path of paths) {\n let raw: string;\n try {\n // Without the encoding this is a Buffer, which JSON.parse decodes as\n // UTF-8 all the same - so emptying the literal parses the same export.\n raw = await readFile(resolve(cwd, path), 'utf8');\n } catch (error) {\n err(`spec-graph: cannot read ${path}: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n try {\n sides.push(parseGraphExport(raw, path));\n } catch (error) {\n // Every input parseGraphExport refuses, it refuses as a DiffInputError;\n // the rethrow is for a bug in it, which no file reaches.\n if (!(error instanceof DiffInputError)) throw error;\n err(`spec-graph: ${error.message}\\n`);\n return EXIT_ERROR;\n }\n }\n const [before, after] = sides as [GraphExport, GraphExport];\n const diff = diffExports(before, after);\n out(format === 'json' ? formatDiffJson(diff) : format === 'markdown' ? formatDiffMarkdown(diff) : formatDiffText(diff));\n return EXIT_OK;\n}\n\n/**\n * The version in the package's own manifest.\n *\n * The guards and the placeholder are for a manifest that is missing or\n * malformed, and the one this reads always parses and always has a version, so\n * no test reaches them without replacing the file the package ships - and the\n * same parse without the encoding reads a Buffer, which JSON.parse decodes as\n * UTF-8 all the same.\n */\nasync function readVersion(): Promise<string> {\n try {\n const { readFile } = await import('node:fs/promises');\n const url = new URL('../package.json', import.meta.url);\n const parsed: unknown = JSON.parse(await readFile(url, 'utf8'));\n if (typeof parsed === 'object' && parsed !== null && 'version' in parsed) {\n return String((parsed as { version: unknown }).version);\n }\n } catch {\n // Fall through to the placeholder rather than failing `--version`.\n }\n return '0.0.0';\n}\n"]}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * The command line.\n *\n * Five verbs, because there are five questions worth asking of a specification\n * graph: is it consistent (`check`), what does it contain (`query`), what does\n * it look like (`graph`), what will you check for me (`rules`), and what did a\n * change do to it (`diff`).\n *\n * Exit codes are the contract with CI: `0` clean, `1` findings, `2` the tool\n * itself could not run. A usage mistake never masquerades as a passing build,\n * and an error nothing here expected never as findings.\n */\n\nimport {\n applyBaseline,\n EMPTY_BASELINE,\n formatBaseline,\n parseBaseline,\n type Baseline,\n type StaleEntry,\n} from './baseline.js';\nimport { discoverConfig, loadConfig, type SpecGraphConfig } from './config.js';\nimport {\n DiffInputError,\n diffExports,\n formatDiffJson,\n formatDiffMarkdown,\n formatDiffText,\n parseGraphExport,\n type GraphExport,\n} from './diff.js';\nimport { isGlob, rebasePattern, underRoot } from './glob.js';\nimport { analyse, DEFAULT_PATTERNS, withDiagnostics, type AnalyseOptions, type AnalysisResult } from './runner.js';\nimport {\n formatGithub,\n formatGitlab,\n formatGraph,\n formatJson,\n formatMarkdown,\n formatReport,\n formatSarif,\n shouldUseAscii,\n shouldUseColor,\n type GraphFormat,\n} from './report.js';\nimport type { ProjectRule } from './project-rules.js';\nimport {\n DEFAULT_SEVERITIES,\n resolveStrict,\n RULE_DECISIONS,\n decisionUrl,\n RULE_DESCRIPTIONS,\n RULE_IDS,\n RULE_QUERIES,\n} from './rules.js';\nimport { isAbsolutePath, joinPosix, toPosix } from './paths.js';\nimport { formatRef } from './source.js';\nimport { execute, parseQuery, QueryError, renderMatch, type Match, type QuerySpec } from './select.js';\nimport { isProjectRule, type AnyRuleId, type RuleId, type Severity, type SpecNode } from './types.js';\nimport { displayWidth } from './vendor/spec-core/text/index.js';\n\nexport const EXIT_OK = 0;\nexport const EXIT_FAILED = 1;\nexport const EXIT_ERROR = 2;\n\nexport class UsageError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'UsageError';\n }\n}\n\nexport type Command = 'check' | 'query' | 'graph' | 'rules' | 'diff';\n\nexport interface CliOptions {\n readonly command: Command;\n readonly patterns: readonly string[];\n readonly root: string;\n /**\n * Whether `--root` was given.\n *\n * When it was, the caller has named the root and configuration is read from\n * exactly there. When it was not, the root is discovered - which is a\n * different question from what the root currently is. See ADR-0018.\n */\n readonly rootExplicit: boolean;\n readonly ignore: readonly string[];\n /** Reference targets to leave unreported when they do not resolve. */\n readonly ignoreReferences: readonly string[];\n /** Families a bare identifier may name. Empty means any family in the corpus. */\n readonly families: readonly string[];\n /** Families that are never citations. */\n readonly ignoreFamilies: readonly string[];\n /** Files that log what was decided rather than deciding it. */\n readonly historyPatterns: readonly string[];\n /** Accepted-debt file to read. `null` means none was asked for. */\n readonly baseline: string | null;\n /** Where to write the current findings as accepted debt. */\n readonly recordBaseline: string | null;\n /**\n * Fail when the baseline allows something that no longer happens.\n *\n * The other side of the ratchet, and off by default for the reason ADR-0012\n * gives: failing a build because somebody fixed something is a strange way to\n * encourage them. A team that has decided its debt only goes one way asks for\n * it explicitly, and then a dead exemption cannot outlive the defect.\n */\n readonly ratchet: boolean;\n /** Skip the repository configuration file entirely. */\n readonly noConfig: boolean;\n readonly format: 'human' | 'json' | 'sarif' | 'markdown' | 'gitlab' | 'github';\n readonly graphFormat: GraphFormat;\n readonly severities: Partial<Record<AnyRuleId, Severity>>;\n readonly color: boolean | null;\n readonly ascii: boolean | null;\n readonly verbose: boolean;\n readonly documentsOnly: boolean;\n readonly max: number;\n readonly maxWarnings: number;\n /** Raise every warning to an error. Explicit `--rule` overrides still win. */\n readonly strict: boolean;\n readonly selector: string | null;\n readonly help: boolean;\n readonly version: boolean;\n}\n\nexport interface CliIO {\n readonly argv?: readonly string[] | undefined;\n readonly cwd?: string | undefined;\n readonly stdout?: ((text: string) => void) | undefined;\n readonly stderr?: ((text: string) => void) | undefined;\n readonly env?: Readonly<Record<string, string | undefined>> | undefined;\n readonly isTTY?: boolean | undefined;\n}\n\nexport const HELP = `spec-graph - turn Markdown specifications into a verifiable graph\n\nUSAGE\n spec-graph [check] [patterns...] [options]\n spec-graph query <selector|project:rule> [patterns...] [options]\n spec-graph graph [patterns...] [--graph-format dot|mermaid|json]\n spec-graph rules [rule-id] [--explain]\n spec-graph diff <before.json> <after.json> [--format human|json|markdown]\n\nCOMMANDS\n check Validate the specification graph. The default.\n query Run a selector - or a registered project rule, by its id - and\n print the matching paths.\n graph Export the graph for Graphviz, Mermaid, or another tool.\n rules List the diagnostics that will run, built in and project. Name one\n to see only that one; --explain adds its selector and the ADR that\n decided it.\n diff Compare two graph exports: documents added, removed, moved or\n accepted; relations added or removed; and obligations resolved or\n reopened where they can be told apart. Make both exports with the\n same spec-graph. Exits 0 whether anything changed or not.\n\nOPTIONS\n --root <dir> Directory the patterns resolve against. Without it,\n .spec-graph.json is looked for in the working\n directory and then upward as far as the repository,\n and the directory holding it becomes the root - so a\n run from a subdirectory reports what a run from the\n top reports. Paths typed on the command line stay\n relative to where they were typed.\n --ignore <glob> Skip paths. Repeatable.\n --ignore-ref <glob> Do not report these reference targets when they fail\n to resolve, for repositories where [[...]] tags a\n concept rather than naming a file. Repeatable.\n Suppresses findings only, never edges.\n --family <name> Families a bare identifier in prose may name. When\n given, everything else stays prose. Repeatable.\n --ignore-family <name> Families that are never citations - RFC when the repo\n cites RFC 2119 and keeps its own RFCs. Repeatable.\n --history <glob> Files that log what was decided rather than deciding\n it - journals, changelogs, minutes. Their links are\n still checked; their obligations are not. Repeatable.\n --baseline <file> Accept the findings recorded in this file and report\n only what is new since. The file has to be there: a\n path that reads nothing accepts nothing and says so\n nowhere. A \"baseline\" in the configuration may name a\n file not recorded yet; a path typed here may not.\n --record-baseline <f> Write today's findings to this file as accepted debt,\n and exit 0 without judging them.\n --ratchet Also fail when a baseline entry no longer occurs, so\n a paid-off exemption cannot outlive the defect.\n --no-config Ignore .spec-graph.json and the package.json key.\n --format <fmt> human, json, sarif, github, gitlab or markdown. sarif\n is the interchange format GitHub code scanning and\n editors already read, github the workflow commands a\n GitHub Actions log turns into annotations, and gitlab\n the Code Quality report a GitLab merge request reads,\n all three for check; markdown is a table for a\n pull-request comment or $GITHUB_STEP_SUMMARY, for\n check or diff (default: human)\n --graph-format <fmt> dot, mermaid or json (default: dot)\n --documents-only Leave items out of the exported graph\n --rule <id>=<severity> Override one rule: error, warn, info or off. A project\n rule is named in full: project:no-draft-dependency.\n Repeatable.\n --max <n> Show at most n findings (0 = no limit)\n --max-warnings <n> Fail when warnings exceed n (default: no limit)\n --strict Raise every warning to an error. An explicit --rule\n still wins, so --strict --rule x=warn exempts x.\n --color / --no-color Force colour on or off\n --ascii Use ASCII glyphs only\n --verbose Include parse problems, per-file detail, and every\n reference --ignore-ref or --ignore-family silenced\n -h, --help Show this help\n -v, --version Show the version\n\nSELECTORS\n A selector is a path through the graph.\n\n item[openness=open] -delegates-to-> document[phase=retired]\n document[phase=active] -assumes-> document[phase=retired]\n document =supersedes=> document\n *[path^=docs/adr] -contains-> item[state=accepted-debt]\n\n Node types: document, item, * (any)\n Attributes: id, kind, title, path, file, line, phase, status, receptivity,\n alias, document, state (or disposition), openness, section,\n text, body, evidence, conflicted, fm.<front-matter-key>\n Operators: = != ^= $= *= ~= and [attr] for \"is present\"\n ~= is a regular expression, matched by an automaton that cannot\n backtrack: linear in the subject, whatever the pattern. It reads\n the usual syntax minus backreferences and lookaround, which are\n not regular - both are refused when the selector is read, with\n the character pointed at\n Relations: -kind-> <-kind- =kind=> <=kind= (= forms are transitive)\n\nEXIT CODES\n 0 clean\n 1 findings at error severity (or over --max-warnings)\n 2 the tool could not run\n\nCONFIGURATION\n Anything repeated on every run belongs in the repository rather than in the\n command. spec-graph reads the first of these that exists:\n\n .spec-graph.json, spec-graph.config.json, or a \"spec-graph\" key in\n package.json\n\n { \"patterns\": [\"docs/**/*.md\"],\n \"ignoreReferences\": [\"trap *\"],\n \"ignoreFamilies\": [\"RFC\"],\n \"historyPatterns\": [\"**/JOURNAL_*.md\", \"archive/**\"],\n \"baseline\": \".spec-graph-baseline.json\",\n \"severities\": { \"self-reference\": \"off\" },\n \"strict\": true,\n \"maxRelated\": 3 }\n\n A flag always wins over the file, and list flags add to it rather than\n replacing it. maxRelated has no flag: it caps how many related locations one\n finding lists, 8 unless set, and 0 lists none.\n\n A problem in that file stops the run with exit 2, named on stderr: invalid\n JSON, an unknown key, a rule that does not compile. A configuration that did\n not load checks a different repository than the one configured, and would\n report that one as consistent. --no-config runs on defaults instead.\n\nPROJECT RULES\n A convention spec-graph never anticipated is a selector plus a sentence, and\n belongs in the same file. Every rule declared here runs beside the built-ins\n and is reported, baselined, escalated by --strict and silenced by --rule in\n exactly the same way.\n\n { \"rules\": {\n \"no-draft-dependency\": {\n \"query\": \"document[phase=active] -depends-on-> document[phase=draft]\",\n \"message\": \"{0} depends on {1}, which is still a draft\",\n \"hint\": \"wait for {1} to be accepted, or drop it from {0.path}\",\n \"severity\": \"error\" } } }\n\n query One selector, or a list of them read as a union.\n message The headline. {0} is the first node on the path, {1} the next;\n {1.phase} and {0.fm.owner} read any selector attribute.\n hint The next action. Optional, and templated the same way.\n severity error, warn, info or off. Defaults to warn - a rule a team has\n just written has not yet earned the right to fail their build.\n\n The id is the name with project: in front, which is why it can never collide\n with a built-in. A selector that does not parse, or a {2} the query can never\n reach, is reported when the file is read rather than found missing later.\n\n To see what one matches without copying its selector out of the file:\n\n spec-graph query project:no-draft-dependency --verbose\n\nADOPTING THIS ON AN OLD REPOSITORY\n Record what is already wrong, then report only what happens next:\n\n spec-graph check --record-baseline .spec-graph-baseline.json\n git add .spec-graph-baseline.json\n\n The file is keyed by specification and citation, not by line number, so it\n survives edits, moves and renames. Findings that stop occurring are reported\n so the file can be tightened; nothing new gets in.\n\nEXAMPLES\n spec-graph \"docs/**/*.md\"\n spec-graph check --rule self-reference=off --format json\n spec-graph query 'item[openness=open] -delegates-to-> document[phase=retired]'\n spec-graph check --ignore-ref \"trap *\" # [[trap 55]] tags a concept, not a file\n spec-graph graph --documents-only --graph-format mermaid > graph.mmd\n spec-graph check --history \"**/JOURNAL_*.md\" # a log is not a specification\n spec-graph check --baseline .spec-graph-baseline.json\n spec-graph rules --explain # including this repository's own\n spec-graph rules ghost-handover --explain # and why it exists\n spec-graph check --rule project:no-draft-dependency=off\n spec-graph query project:no-draft-dependency # what does that rule match?\n spec-graph check --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n spec-graph check --format github # annotations from an Actions log\n spec-graph check --format gitlab > gl-code-quality-report.json\n spec-graph diff base.json head.json --format markdown >> \"$GITHUB_STEP_SUMMARY\"\n`;\n\nconst SEVERITIES: readonly Severity[] = ['error', 'warn', 'info', 'off'];\n\n/** Parses argv into options. Throws {@link UsageError} on anything malformed. */\nexport function parseArgs(argv: readonly string[], cwd: string): CliOptions {\n const args = [...argv];\n let command: Command = 'check';\n\n const first = args[0];\n if (first === 'check' || first === 'query' || first === 'graph' || first === 'rules' || first === 'diff') {\n command = first;\n args.shift();\n }\n\n const patterns: string[] = [];\n const ignore: string[] = [];\n const ignoreReferences: string[] = [];\n const families: string[] = [];\n const ignoreFamilies: string[] = [];\n const historyPatterns: string[] = [];\n let noConfig = false;\n const severities: Partial<Record<AnyRuleId, Severity>> = {};\n let root = cwd;\n let rootExplicit = false;\n let format: CliOptions['format'] = 'human';\n let graphFormat: GraphFormat = 'dot';\n let color: boolean | null = null;\n let ascii: boolean | null = null;\n let verbose = false;\n let documentsOnly = false;\n let max = 0;\n let maxWarnings = -1;\n let strict = false;\n let baseline: string | null = null;\n let ratchet = false;\n let recordBaseline: string | null = null;\n let selector: string | null = null;\n let help = false;\n let version = false;\n\n const next = (flag: string, index: number): string => {\n const value = args[index + 1];\n if (value === undefined) throw new UsageError(`${flag} needs a value`);\n return value;\n };\n\n for (let i = 0; i < args.length; i += 1) {\n const arg = args[i] as string;\n\n if (arg === '--') {\n patterns.push(...args.slice(i + 1));\n break;\n }\n\n if (!arg.startsWith('-')) {\n if (command === 'query' && selector === null) selector = arg;\n else patterns.push(arg);\n continue;\n }\n\n switch (arg) {\n case '-h':\n case '--help':\n help = true;\n break;\n case '-v':\n case '--version':\n version = true;\n break;\n case '--verbose':\n verbose = true;\n break;\n case '--color':\n color = true;\n break;\n case '--no-color':\n color = false;\n break;\n case '--ascii':\n ascii = true;\n break;\n case '--documents-only':\n documentsOnly = true;\n break;\n case '--strict':\n strict = true;\n break;\n case '--ratchet':\n ratchet = true;\n break;\n case '--explain':\n verbose = true;\n break;\n case '--root':\n root = next(arg, i);\n rootExplicit = true;\n i += 1;\n break;\n case '--ignore':\n ignore.push(next(arg, i));\n i += 1;\n break;\n case '--ignore-ref':\n ignoreReferences.push(next(arg, i));\n i += 1;\n break;\n case '--family':\n families.push(next(arg, i));\n i += 1;\n break;\n case '--ignore-family':\n ignoreFamilies.push(next(arg, i));\n i += 1;\n break;\n case '--history':\n historyPatterns.push(next(arg, i));\n i += 1;\n break;\n case '--baseline':\n baseline = next(arg, i);\n i += 1;\n break;\n case '--record-baseline':\n recordBaseline = next(arg, i);\n i += 1;\n break;\n case '--no-config':\n noConfig = true;\n break;\n case '--format': {\n const value = next(arg, i);\n if (\n value !== 'human' &&\n value !== 'json' &&\n value !== 'sarif' &&\n value !== 'markdown' &&\n value !== 'gitlab' &&\n value !== 'github'\n ) {\n throw new UsageError(`--format must be human, json, sarif, markdown, gitlab or github, got \"${value}\"`);\n }\n format = value;\n i += 1;\n break;\n }\n case '--graph-format': {\n const value = next(arg, i);\n if (value !== 'dot' && value !== 'mermaid' && value !== 'json') {\n throw new UsageError(`--graph-format must be dot, mermaid or json, got \"${value}\"`);\n }\n graphFormat = value;\n i += 1;\n break;\n }\n case '--rule': {\n const value = next(arg, i);\n const equals = value.indexOf('=');\n if (equals === -1) throw new UsageError(`--rule expects <id>=<severity>, got \"${value}\"`);\n const id = value.slice(0, equals) as AnyRuleId;\n const level = value.slice(equals + 1) as Severity;\n // A project rule is taken on its namespace here and checked for real\n // once the configuration defining it has been read: arguments are\n // parsed before any file is opened, and a flag has to be usable on that\n // first pass whatever the repository turns out to declare.\n if (!RULE_IDS.includes(id as RuleId) && !isProjectRule(id)) {\n throw new UsageError(`unknown rule \"${id}\"\\n known rules: ${RULE_IDS.join(', ')}`);\n }\n if (!SEVERITIES.includes(level)) {\n throw new UsageError(`unknown severity \"${level}\", expected one of: ${SEVERITIES.join(', ')}`);\n }\n severities[id] = level;\n i += 1;\n break;\n }\n case '--max': {\n max = parseCount(arg, next(arg, i));\n i += 1;\n break;\n }\n case '--max-warnings': {\n maxWarnings = parseCount(arg, next(arg, i));\n i += 1;\n break;\n }\n default:\n throw new UsageError(`unknown option \"${arg}\"\\n run \"spec-graph --help\" to see the available options`);\n }\n }\n\n // SARIF, GitHub's workflow commands and GitLab's Code Quality are reports\n // about findings, and only `check` produces those. Falling back to JSON would\n // hand a pipeline something its uploader rejects with a message about a\n // schema rather than about the command that was run. Markdown is for a pull\n // request, which reads a check or a diff.\n if ((format === 'sarif' || format === 'gitlab' || format === 'github') && command !== 'check' && !help && !version) {\n throw new UsageError(`--format ${format} reports findings, so it belongs to check, not to ${command}`);\n }\n if (format === 'markdown' && command !== 'check' && command !== 'diff' && !help && !version) {\n throw new UsageError(`--format markdown is a report for a pull request, so it belongs to check or diff, not to ${command}`);\n }\n\n if (command === 'diff' && patterns.length !== 2 && !help && !version) {\n throw new UsageError(\n 'diff compares two graph exports, for example:\\n spec-graph diff base.json head.json\\n (make each with spec-graph graph --graph-format json)',\n );\n }\n\n if (command === 'query' && selector === null && !help && !version) {\n throw new UsageError('query needs a selector, for example:\\n spec-graph query \\'item[openness=open]\\'');\n }\n\n return {\n command,\n patterns,\n root,\n rootExplicit,\n ignore,\n ignoreReferences,\n families,\n ignoreFamilies,\n historyPatterns,\n baseline,\n ratchet,\n recordBaseline,\n noConfig,\n format,\n graphFormat,\n severities,\n color,\n ascii,\n verbose,\n documentsOnly,\n max,\n maxWarnings,\n strict,\n selector,\n help,\n version,\n };\n}\n\nfunction count(n: number, word: string, many?: string): string {\n return `${n} ${n === 1 ? word : (many ?? `${word}s`)}`;\n}\n\ninterface HeldBaseline {\n readonly baseline: Baseline;\n readonly problems: readonly string[];\n /** What stopped the file being read, when something did. */\n readonly unread: { readonly missing: boolean; readonly message: string } | null;\n}\n\n/**\n * Reads a baseline file.\n *\n * Absent and unreadable are different answers, and the caller needs both. A\n * repository that has not recorded a baseline yet declares the path before the\n * file exists, and an empty baseline accepts nothing, which is what that run\n * should report (ADR-0012). Anything else - a directory, a permission, a path\n * that went somewhere unintended - is the tool failing to do what it was told.\n */\nasync function readBaseline(path: string, source: string): Promise<HeldBaseline> {\n let raw: string;\n try {\n const { readFile } = await import('node:fs/promises');\n raw = await readFile(path, 'utf8');\n } catch (error) {\n const failure = error as Error & { code?: string };\n return {\n baseline: EMPTY_BASELINE,\n problems: [],\n unread: { missing: failure.code === 'ENOENT', message: failure.message },\n };\n }\n return { ...parseBaseline(raw, source), unread: null };\n}\n\nfunction parseCount(flag: string, value: string): number {\n const parsed = Number.parseInt(value, 10);\n if (!Number.isFinite(parsed) || parsed < 0 || String(parsed) !== value.trim()) {\n throw new UsageError(`${flag} expects a non-negative whole number, got \"${value}\"`);\n }\n return parsed;\n}\n\n/* -------------------------------------------------------------------------- */\n/* Entry point */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Runs the CLI and returns the process exit code. Never throws.\n *\n * An error nothing below expected is neither a finding nor a usage mistake:\n * the answer cannot be trusted, which is the family contract's 2 (spec-core's\n * ADR-0005). Rejected instead, it reached the launcher as Node's uncaught\n * error, exit 1, which CI reads as findings. The stack is what makes a report\n * of it something to act on, and it goes to stderr alone: a script that reads\n * a document from stdout is handed no part of one.\n *\n * A write its reader had closed the pipe for is not such an error: the answer\n * was not delivered, which is still 2, and nothing in spec-graph is at fault,\n * so it is said in a line and no stack sends a person looking for a defect.\n * The process's own streams report it as an event, which the launcher answers\n * in the same words; here it is a caller's stream that throws it. It is read\n * by its code: stdout and stderr are the only pipes spec-graph writes to.\n */\nexport async function main(io: CliIO = {}): Promise<number> {\n const err = io.stderr ?? ((text) => process.stderr.write(text));\n try {\n return await run(io, err);\n } catch (error) {\n if (!(error instanceof Error)) err(`spec-graph: unexpected error: ${String(error)}\\n`);\n else if ((error as { code?: unknown }).code === 'EPIPE') err('spec-graph: stdout was closed before all of the output was written\\n');\n else err(`spec-graph: unexpected error: ${error.stack ?? error.message}\\n`);\n return EXIT_ERROR;\n }\n}\n\n/** One run of the command line, which rejects on an error it did not expect. */\nasync function run(io: CliIO, err: (text: string) => void): Promise<number> {\n const argv = io.argv ?? process.argv.slice(2);\n const cwd = io.cwd ?? process.cwd();\n const out = io.stdout ?? ((text) => process.stdout.write(text));\n const env = io.env ?? process.env;\n\n let options: CliOptions;\n try {\n options = parseArgs(argv, cwd);\n } catch (error) {\n // parseArgs throws nothing but a UsageError for any argv, so no test can\n // tell this check from `true`: the rethrow is for a bug in the parser.\n if (error instanceof UsageError) {\n err(`spec-graph: ${error.message}\\n`);\n return EXIT_ERROR;\n }\n throw error;\n }\n\n if (options.help) {\n out(HELP);\n return EXIT_OK;\n }\n if (options.version) {\n out(`${await readVersion()}\\n`);\n return EXIT_OK;\n }\n\n // A diff reads two files and nothing else: no configuration and no corpus, so\n // it can compare exports from a checkout it is not running in.\n if (options.command === 'diff') return runDiff(options.patterns as readonly [string, string], cwd, options.format, out, err);\n\n const color = options.color ?? shouldUseColor({ isTTY: io.isTTY, env });\n const ascii = options.ascii ?? shouldUseAscii({ env });\n\n // Configuration is what is true of the repository; a flag is somebody\n // overriding it for one run. So a flag always wins, and lists add rather than\n // replace - a `--ignore-ref` on the command line is one more exclusion, not a\n // decision to throw away the ones the repository already declared.\n //\n // Read before `rules` prints anything, so that command lists the conventions\n // this repository will actually check rather than the ones spec-graph ships.\n const loaded = options.noConfig\n ? { config: {} as SpecGraphConfig, source: null, problems: [], root: options.root }\n : options.rootExplicit\n ? { ...loadConfig(options.root), root: options.root }\n : discoverConfig(options.root);\n for (const problem of loaded.problems) err(`spec-graph: ${problem}\\n`);\n // A configuration that did not load means a run against a different\n // repository than the one configured - other patterns, other rules, other\n // severities - and the verdict printed is about that one. Reporting the\n // problem and carrying on gave a green build with `ok: true` from a file with\n // a typo in it, which is the one answer a checker must never reach by\n // accident, and the same mistake in a `--rule` has always exited 2. The\n // findings are one flag away, and the flag is named. Amends ADR-0010.\n if (loaded.problems.length > 0) {\n err(\n `spec-graph: ${count(loaded.problems.length, 'problem')} in the configuration, so nothing was checked\\n fix it, or run again with --no-config to check on defaults\\n`,\n );\n return EXIT_ERROR;\n }\n const file = loaded.config;\n const projectRules = file.rules ?? [];\n const root = loaded.root;\n // How far the root moved up. Everything a repository declares about itself is\n // relative to the root; everything typed on the command line is relative to\n // where it was typed, and this is what keeps those two readings apart.\n const here = below(root, options.root);\n\n // A `--rule` naming a project rule that does not exist is a flag that\n // silently does nothing, and nothing in the output would distinguish that\n // from a rule that ran and found none.\n for (const id of Object.keys(options.severities)) {\n if (!isProjectRule(id as AnyRuleId) || projectRules.some((rule) => rule.id === id)) continue;\n err(`spec-graph: unknown rule \"${id}\"\\n ${knownProjectRules(projectRules)}\\n`);\n return EXIT_ERROR;\n }\n\n if (options.command === 'rules') {\n // The one positional argument `rules` can take is a rule id: this command\n // reads no files, so a pattern here would be a word with nowhere to go.\n const wanted = options.patterns[0];\n if (wanted !== undefined && !RULE_IDS.includes(wanted as RuleId) && !projectRules.some((rule) => rule.id === wanted)) {\n err(\n `spec-graph: unknown rule \"${wanted}\"\\n known rules: ${RULE_IDS.join(', ')}\\n ${knownProjectRules(projectRules)}\\n`,\n );\n return EXIT_ERROR;\n }\n out(renderRules(options.verbose, await readVersion(), projectRules, wanted ?? null, loaded.source));\n return EXIT_OK;\n }\n\n // Named the way the reader would have to type it, because a discovered\n // configuration is often not the one in front of them.\n //\n // On stderr, with everything else this run says about itself. stdout is the\n // report, and in three of the four formats it is a document: a line above it\n // made `--verbose --format json` unparseable and handed `--format sarif` to a\n // code-scanning uploader that rejects it over a schema.\n if (options.verbose && loaded.source !== null) err(`configuration: ${fromHere(here, loaded.source)}\\n`);\n\n // A bare `--ignore` name prunes a directory of that name at any depth, the\n // way a .gitignore line does, so it means the same thing wherever it was\n // typed. Every other pattern is matched against the repository-relative\n // path, a bare `--history` name included, and leaving one alone would make\n // it silently match something else or nothing. A pattern that cannot be\n // rebased onto the root stops the run as one the dialect refuses does,\n // named as it was typed.\n let typed;\n try {\n typed = {\n patterns: options.patterns.map((pattern) => anchor(here, pattern)),\n ignore: options.ignore.map((pattern) => anchorPath(here, pattern)),\n history: options.historyPatterns.map((pattern) => anchor(here, pattern)),\n };\n } catch (error) {\n err(`spec-graph: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n const patterns = typed.patterns.length > 0 ? typed.patterns : (file.patterns ?? DEFAULT_PATTERNS);\n const severityOverrides = { ...(file.severities ?? {}), ...options.severities };\n const { severities, escalated } = resolveStrict(\n severityOverrides,\n options.strict || (file.strict ?? false),\n projectRules,\n );\n\n const analyseOptions: AnalyseOptions = {\n root,\n patterns,\n // A stray entry in one of the empty fallbacks below changes a run only if\n // it matches a path, a target or a family some corpus has, so the mutants\n // that plant one survive every corpus but one built for them - except in\n // `families`, where any entry is an allowlist.\n ignore: [...(file.ignore ?? []), ...typed.ignore],\n ignoreReferences: [...(file.ignoreReferences ?? []), ...options.ignoreReferences],\n families: [...(file.families ?? []), ...options.families],\n ignoreFamilies: [...(file.ignoreFamilies ?? []), ...options.ignoreFamilies],\n historyPatterns: [...(file.historyPatterns ?? []), ...typed.history],\n severities,\n projectRules,\n maxRelated: file.maxRelated,\n };\n\n let result;\n try {\n result = await analyse(analyseOptions);\n } catch (error) {\n err(`spec-graph: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n\n if (result.files.length === 0) {\n err(\n `spec-graph: no specifications matched ${\n patterns.map((p) => `\"${p}\"`).join(', ')\n }\\n looked under ${root}\\n`,\n );\n return EXIT_ERROR;\n }\n\n switch (options.command) {\n case 'graph':\n out(\n formatGraph(result.graph, options.graphFormat, {\n documentsOnly: options.documentsOnly,\n generator: { name: 'spec-graph', version: await readVersion() },\n }),\n );\n return EXIT_OK;\n\n case 'query': {\n const selector = options.selector as string;\n let queries: readonly QuerySpec[];\n\n // A registered rule is asked for by name, because by the time this runs\n // it is already compiled and the alternative is copying its selector out\n // of the configuration file by hand. The namespace cannot collide with\n // the grammar: no selector begins with a word and a colon. See ADR-0016.\n if (isProjectRule(selector as AnyRuleId)) {\n const rule = projectRules.find((candidate) => candidate.id === selector);\n if (rule === undefined) {\n err(`spec-graph: unknown rule \"${selector}\"\\n ${knownProjectRules(projectRules)}\\n`);\n return EXIT_ERROR;\n }\n queries = rule.queries;\n // Beside the report rather than inside it, for the reason the\n // configuration line above is: `--format json` is a document.\n if (options.verbose) for (const source of rule.sources) err(`${rule.id}: ${source}\\n`);\n } else {\n try {\n queries = [parseQuery(selector)];\n } catch (error) {\n // Every selector that fails to parse fails as a QueryError, so no\n // test can tell this check from `true`.\n if (error instanceof QueryError) {\n err(renderQueryError(selector, error));\n return EXIT_ERROR;\n }\n throw error;\n }\n }\n\n const matches = union(result.graph, queries);\n // renderMatches asks only whether this is 'json', so the other word is a\n // name for the reader and emptying it changes nothing.\n out(renderMatches(matches, options.format === 'json' ? 'json' : 'human', result.graph.nodes.size));\n return matches.length > 0 ? EXIT_OK : EXIT_FAILED;\n }\n\n default: {\n // Recording is not checking. It writes down what is wrong today so that\n // tomorrow can be compared against it, and says nothing about whether\n // today is acceptable - so it reports what it wrote and exits clean.\n if (options.recordBaseline !== null) {\n const text = formatBaseline(result.graph, result.diagnostics);\n try {\n const { writeFile } = await import('node:fs/promises');\n // Node writes a string as UTF-8 when the encoding is empty too, so\n // emptying this literal writes the same bytes.\n await writeFile(underRoot(root, anchorFile(here, options.recordBaseline)), text, 'utf8');\n } catch (error) {\n err(`spec-graph: cannot write ${options.recordBaseline}: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n const entries = (JSON.parse(text) as Baseline).findings.length;\n out(\n `recorded ${count(result.diagnostics.length, 'finding')} as ${count(entries, 'entry', 'entries')} in ${options.recordBaseline}\\n`,\n );\n return EXIT_OK;\n }\n\n const source = options.baseline === null ? (file.baseline ?? null) : anchorFile(here, options.baseline);\n const ratchet = options.ratchet || file.ratchet === true;\n let reported = result;\n let note:\n | {\n source: string;\n suppressed: number;\n stale: number;\n ratchet: boolean;\n entries: readonly StaleEntry[];\n }\n | undefined;\n let recordAs: string | undefined;\n if (source !== null) {\n const held = await readBaseline(underRoot(root, source), source);\n // A path typed on the command line says the file is there for this run.\n // Read as an empty baseline instead, a typo in it reports every accepted\n // finding as new, and passes --ratchet with nothing left to be stale.\n // A path in the configuration is the case ADR-0012 argued for, and only\n // where the file is absent: unreadable is unreadable wherever the path\n // was written.\n if (held.unread !== null && (options.baseline !== null || !held.unread.missing)) {\n err(`spec-graph: cannot read the baseline ${source}: ${held.unread.message}\\n`);\n return EXIT_ERROR;\n }\n // Quiet by default, because a configured path legitimately precedes the\n // first --record-baseline. Under --verbose, where parse problems already\n // go, a path that reads nothing says so rather than accepting nothing.\n if (held.unread !== null && options.verbose) {\n err(`spec-graph: baseline ${source} is not there, so nothing is accepted\\n`);\n }\n for (const problem of held.problems) err(`spec-graph: ${problem}\\n`);\n const outcome = applyBaseline(result.graph, result.diagnostics, held.baseline);\n reported = withDiagnostics(result, outcome.kept);\n note = {\n source,\n suppressed: outcome.suppressed,\n stale: outcome.stale.length,\n ratchet,\n entries: outcome.stale,\n };\n // The command that re-records it is typed where this run was: a path\n // typed on this command line is relative to there already, and the\n // configuration's is from the root.\n recordAs = options.baseline ?? fromHere(here, source);\n // Listed rather than counted when the run turns on them: a number is\n // enough to know the file has slack, and not enough to strike it. On\n // stdout only for a human - the structured formats carry the same rows\n // inside the document, where a stray line is the difference between\n // parsing and not.\n if (options.format === 'human' && (options.verbose || ratchet)) {\n for (const entry of outcome.stale) {\n const subject = entry.subject === '' ? '' : ` \"${entry.subject}\"`;\n const why = entry.reason === 'gone' ? ` - ${entry.document} is not in this corpus` : '';\n out(` ${entry.reason}: ${entry.rule} ${entry.document}${subject}${why}\\n`);\n }\n }\n }\n const looseBaseline = note !== undefined && note.ratchet && note.stale > 0;\n\n const reporterOptions = { verbose: options.verbose, max: options.max, escalated, baseline: note, recordAs };\n out(\n options.format === 'sarif'\n ? formatSarif(reported, reported.graph, { version: await readVersion(), escalated, projectRules })\n : options.format === 'gitlab'\n ? formatGitlab(reported, { escalated })\n : options.format === 'github'\n ? formatGithub(reported, { escalated })\n : options.format === 'json'\n ? formatJson(reported, { escalated, baseline: note })\n : options.format === 'markdown'\n ? formatMarkdown(reported, reporterOptions)\n : `${formatReport(reported, { color, ascii, ...reporterOptions })}\\n`,\n );\n if (!reported.ok || looseBaseline) return EXIT_FAILED;\n if (options.maxWarnings >= 0 && reported.summary.warnings > options.maxWarnings) return EXIT_FAILED;\n return EXIT_OK;\n }\n }\n}\n\n/* -------------------------------------------------------------------------- */\n/* Rendering */\n/* -------------------------------------------------------------------------- */\n\n/**\n * Where the working directory sits below the root, or `''` when it is the root.\n *\n * Discovery only ever walks upward, so the root is always a prefix of the\n * directory the command was typed in, and this is a slice rather than path\n * arithmetic.\n */\nfunction below(root: string, from: string): string {\n const start = toPosix(from).replace(/[/]+$/, '');\n // The prefix test only guards a root that is neither `start` nor above it,\n // which nothing returns; for `start` itself, slicing past its end is `''`\n // too, so emptying the prefix changes nothing a run can reach.\n return start.startsWith(`${root}/`) ? start.slice(root.length + 1) : '';\n}\n\n/**\n * A path from the root, as it is typed in `here`: one `../` for each directory\n * `here` is below the root. An absolute path reads the same from anywhere.\n */\nfunction fromHere(here: string, path: string): string {\n return here === '' || isAbsolutePath(path) ? path : `${'../'.repeat(here.split('/').length)}${path}`;\n}\n\n/**\n * Re-anchors a pattern typed in `prefix` so it reads from the root, brace\n * alternative by brace alternative, or throws naming it as typed.\n *\n * `../docs` typed one directory down is the root's `docs`. That is arithmetic\n * on two relative paths, and this is the one place both are known; left in the\n * pattern, it reads as a glob climbing out of the root, which is refused.\n *\n * An absolute path is left alone: it was not relative to anywhere, so moving\n * the root cannot change what it means. spec-core roots a leading `/` and\n * knows no drive letter, so `C:/docs` is told apart here.\n */\nfunction anchor(prefix: string, pattern: string): string {\n return isAbsolutePath(pattern) ? pattern : rebasePattern(pattern, prefix);\n}\n\n/**\n * The same, for an `--ignore`, where a bare name is a directory at any depth.\n * Only the walk reads a name that way; a `--history` pattern is matched as a\n * whole path, and goes to {@link anchor}.\n */\nfunction anchorPath(prefix: string, pattern: string): string {\n return isGlob(pattern) || pattern.includes('/') ? anchor(prefix, pattern) : pattern;\n}\n\n/**\n * Re-anchors a file typed in `prefix`, a baseline, so it reads from the root.\n *\n * A file name is not a pattern: a brace or a `!` in it is a character, which\n * {@link anchor} would read as syntax, and a baseline outside the repository\n * is as good as one inside, so a `..` past the root is a path like any other.\n * At the root the file is named as it was typed.\n */\nfunction anchorFile(prefix: string, file: string): string {\n return prefix === '' || isAbsolutePath(file) ? file : joinPosix(prefix, toPosix(file));\n}\n\nfunction knownProjectRules(projectRules: readonly ProjectRule[]): string {\n const known = projectRules.map((rule) => rule.id);\n return known.length > 0 ? `project rules here: ${known.join(', ')}` : 'this repository defines no project rules';\n}\n\n/**\n * Runs a list of selectors as one result set.\n *\n * A project rule is a union of its selectors, and a path that two of them both\n * find is one path - the same dedupe `projectFindings` does, so that asking\n * this command what a rule matches answers with the set `check` reports on.\n */\nfunction union(graph: AnalysisResult['graph'], queries: readonly QuerySpec[]): Match[] {\n // `execute` already returns each path of nodes once, keyed the way this is,\n // so one selector needs no second pass and sending it through one anyway\n // returns the same matches.\n if (queries.length === 1) return execute(graph, queries[0] as QuerySpec);\n const seen = new Set<string>();\n const matches: Match[] = [];\n for (const spec of queries) {\n for (const match of execute(graph, spec)) {\n const key = match.nodes.map((node) => node.id).join('>');\n if (seen.has(key)) continue;\n seen.add(key);\n matches.push(match);\n }\n }\n return matches;\n}\n\ninterface RuleRow {\n readonly id: string;\n readonly severity: Severity;\n readonly description: string;\n /** The selector, where the rule is one. */\n readonly query: string | undefined;\n /** Where the reasoning is: an ADR, or the file that declared the rule. */\n readonly decided: string;\n}\n\nfunction renderRules(\n explain: boolean,\n version: string,\n projectRules: readonly ProjectRule[],\n only: string | null,\n configSource: string | null,\n): string {\n // A project rule is described by the selector it is, because that is what it\n // is - its message is a template, and a template is not a description.\n const rows: RuleRow[] = [\n ...RULE_IDS.map((id): RuleRow => ({\n id,\n severity: DEFAULT_SEVERITIES[id],\n description: RULE_DESCRIPTIONS[id],\n query: RULE_QUERIES[id],\n decided: decisionUrl(RULE_DECISIONS[id], version),\n })),\n ...projectRules.map((rule): RuleRow => ({\n id: rule.id,\n severity: rule.severity,\n description: rule.sources[0] as string,\n query: rule.sources.slice(1).join(' | ') || undefined,\n // A project rule only exists because a file declared it, so the source\n // is never null here and the first branch is for the type alone.\n decided: configSource === null ? `rules.${rule.name}` : `${configSource}: rules.${rule.name}`,\n })),\n ];\n\n const shown = only === null ? rows : rows.filter((row) => row.id === only);\n const width = Math.max(...shown.map((row) => row.id.length));\n const lines: string[] = [];\n for (const row of shown) {\n lines.push(`${row.id.padEnd(width)} ${row.severity.padEnd(5)} ${row.description}`);\n if (!explain) continue;\n if (row.query) lines.push(`${' '.repeat(width)} ${row.query}`);\n // Where the decision is written down, rather than a second copy of it. A\n // reader who has just been told their document is inconsistent is entitled\n // to know who decided that and why, and the ADR is the answer.\n lines.push(`${' '.repeat(width)} ${row.decided}`);\n }\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction renderMatches(matches: readonly Match[], format: 'human' | 'json', total: number): string {\n if (format === 'json') {\n return `${JSON.stringify(\n {\n version: 1,\n count: matches.length,\n matches: matches.map((match) => ({\n nodes: match.nodes.map((node) => nodeSummary(node)),\n edges: match.edges.map((edge) => ({\n kind: edge.kind,\n from: edge.from,\n to: edge.to,\n file: edge.declaredAt.file,\n line: edge.declaredAt.span.start.line,\n })),\n })),\n },\n null,\n 2,\n )}\\n`;\n }\n\n if (matches.length === 0) return `no matches (searched ${total} nodes)\\n`;\n\n const lines: string[] = [];\n for (const match of matches) {\n const head = match.nodes[0] as SpecNode;\n const tail = match.nodes[match.nodes.length - 1] as SpecNode;\n lines.push(renderMatch(match));\n lines.push(` ${formatRef(head.at)}${match.nodes.length > 1 ? ` ${formatRef(tail.at)}` : ''}`);\n }\n lines.push(`${matches.length} ${matches.length === 1 ? 'match' : 'matches'}`);\n return `${lines.join('\\n')}\\n`;\n}\n\nfunction nodeSummary(node: SpecNode): Record<string, unknown> {\n return {\n id: node.id,\n kind: node.kind,\n title: node.title,\n file: node.at.file,\n line: node.at.span.start.line,\n ...(node.kind === 'document' ? { phase: node.phase } : { state: node.disposition, openness: node.openness }),\n };\n}\n\n/**\n * Points at the exact character of a selector that failed to parse, in the\n * columns a terminal draws: a Han character before it takes two, so an offset\n * counted in UTF-16 units put the caret short of it. The offset is a position\n * the parser reached, never negative, so it needs no floor.\n */\nfunction renderQueryError(selector: string, error: QueryError): string {\n const caret = `${' '.repeat(displayWidth(selector.slice(0, error.offset)))}^`;\n return `spec-graph: ${error.message}\\n ${selector}\\n ${caret}\\n`;\n}\n\n/**\n * Compares two exports.\n *\n * Exit 0 whether anything changed or not: a diff describes, and `check` gates. A\n * diff that failed on a removed relation would fail every legitimate\n * supersession (ADR-0020).\n */\nasync function runDiff(\n paths: readonly [string, string],\n cwd: string,\n format: CliOptions['format'],\n out: (text: string) => void,\n err: (text: string) => void,\n): Promise<number> {\n const { readFile } = await import('node:fs/promises');\n const { resolve } = await import('node:path');\n const sides: GraphExport[] = [];\n for (const path of paths) {\n let raw: string;\n try {\n // Without the encoding this is a Buffer, which JSON.parse decodes as\n // UTF-8 all the same - so emptying the literal parses the same export.\n raw = await readFile(resolve(cwd, path), 'utf8');\n } catch (error) {\n err(`spec-graph: cannot read ${path}: ${(error as Error).message}\\n`);\n return EXIT_ERROR;\n }\n try {\n sides.push(parseGraphExport(raw, path));\n } catch (error) {\n // Every input parseGraphExport refuses, it refuses as a DiffInputError;\n // the rethrow is for a bug in it, which no file reaches.\n if (!(error instanceof DiffInputError)) throw error;\n err(`spec-graph: ${error.message}\\n`);\n return EXIT_ERROR;\n }\n }\n const [before, after] = sides as [GraphExport, GraphExport];\n const diff = diffExports(before, after);\n out(format === 'json' ? formatDiffJson(diff) : format === 'markdown' ? formatDiffMarkdown(diff) : formatDiffText(diff));\n return EXIT_OK;\n}\n\n/**\n * The version in the package's own manifest.\n *\n * The guards and the placeholder are for a manifest that is missing or\n * malformed, and the one this reads always parses and always has a version, so\n * no test reaches them without replacing the file the package ships - and the\n * same parse without the encoding reads a Buffer, which JSON.parse decodes as\n * UTF-8 all the same.\n */\nasync function readVersion(): Promise<string> {\n try {\n const { readFile } = await import('node:fs/promises');\n const url = new URL('../package.json', import.meta.url);\n const parsed: unknown = JSON.parse(await readFile(url, 'utf8'));\n if (typeof parsed === 'object' && parsed !== null && 'version' in parsed) {\n return String((parsed as { version: unknown }).version);\n }\n } catch {\n // Fall through to the placeholder rather than failing `--version`.\n }\n return '0.0.0';\n}\n"]}
|
package/package.json
CHANGED