@descent-vtt/spec-guard 0.20.1 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +108 -0
  2. package/README.md +74 -16
  3. package/dist/cli.d.ts +4 -3
  4. package/dist/cli.d.ts.map +1 -1
  5. package/dist/cli.js +155 -21
  6. package/dist/cli.js.map +1 -1
  7. package/dist/config.d.ts.map +1 -1
  8. package/dist/config.js +9 -2
  9. package/dist/config.js.map +1 -1
  10. package/dist/engine.d.ts +9 -1
  11. package/dist/engine.d.ts.map +1 -1
  12. package/dist/engine.js +32 -13
  13. package/dist/engine.js.map +1 -1
  14. package/dist/glob.d.ts +19 -1
  15. package/dist/glob.d.ts.map +1 -1
  16. package/dist/glob.js +40 -12
  17. package/dist/glob.js.map +1 -1
  18. package/dist/impact.d.ts +2 -0
  19. package/dist/impact.d.ts.map +1 -1
  20. package/dist/impact.js +1 -0
  21. package/dist/impact.js.map +1 -1
  22. package/dist/index.d.ts +1 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +1 -1
  25. package/dist/index.js.map +1 -1
  26. package/dist/mcp.d.ts.map +1 -1
  27. package/dist/mcp.js +22 -4
  28. package/dist/mcp.js.map +1 -1
  29. package/dist/prove.d.ts.map +1 -1
  30. package/dist/prove.js +1 -0
  31. package/dist/prove.js.map +1 -1
  32. package/dist/query.d.ts +4 -0
  33. package/dist/query.d.ts.map +1 -1
  34. package/dist/query.js +3 -1
  35. package/dist/query.js.map +1 -1
  36. package/dist/reporter.d.ts +16 -1
  37. package/dist/reporter.d.ts.map +1 -1
  38. package/dist/reporter.js +27 -3
  39. package/dist/reporter.js.map +1 -1
  40. package/dist/runner.d.ts +2 -0
  41. package/dist/runner.d.ts.map +1 -1
  42. package/dist/runner.js +20 -3
  43. package/dist/runner.js.map +1 -1
  44. package/dist/specs.d.ts +14 -0
  45. package/dist/specs.d.ts.map +1 -1
  46. package/dist/specs.js +14 -5
  47. package/dist/specs.js.map +1 -1
  48. package/dist/types.d.ts +10 -0
  49. package/dist/types.d.ts.map +1 -1
  50. package/dist/watch.d.ts.map +1 -1
  51. package/dist/watch.js +7 -2
  52. package/dist/watch.js.map +1 -1
  53. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -10,6 +10,114 @@ what to do when upgrading; the family's policy, including how a flag or field
10
10
  is deprecated before it goes, is spec-core's
11
11
  [ADR-0009](https://github.com/DescentVTT/spec-core/blob/main/docs/adr/0009-versions-before-1-0.md).
12
12
 
13
+ ## 0.22.0
14
+
15
+ A spec pattern that matches no file, beside others that do, is refused by
16
+ name, exit 2, where it was dropped without a word and the run reported on
17
+ the rest: a directory of specs that was renamed no longer leaves a green
18
+ check behind. `--allow-empty` lets such a pattern through, and `--watch` and
19
+ the server carry on and say which it is.
20
+
21
+ ### Changed
22
+
23
+ - **A spec pattern that matches no file beside others that do is refused,
24
+ where it was dropped without a word.** A directory of specs that was
25
+ renamed left its pattern behind: its rules stopped running and the check
26
+ stayed green. A run, `prove`, `impact` and `query` now exit 2 with a line
27
+ that names each such pattern, from the command line or `specs`; documents a
28
+ script reads are still written, the JSON ones with the patterns as
29
+ `unmatchedSpecs`, and `--watch` and the server carry on and say which
30
+ pattern it is
31
+ ([ADR-0014](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0014-configuration-and-watch.md)).
32
+ Upgrading: remove the pattern, or give `--allow-empty` to a run, `prove` or
33
+ `impact` where a pattern is meant to match nothing yet. `query` takes no
34
+ such option, so until the pattern is removed spec-harness's `context`
35
+ packet lists no rules and says why; its guard and its hooks ask spec-guard
36
+ nothing and are as they were.
37
+
38
+ ### Fixed
39
+
40
+ - A ripgrep that failed is said once in a report. Under `--engine auto`, with
41
+ `SPEC_GUARD_RG` naming a program that is not ripgrep, the warning that the
42
+ scanner had answered was repeated for every group of searches, four times
43
+ for four rules over four targets; failures that differ are still each said.
44
+ - `FORCE_COLOR=false` turns colour off, as Node and `supports-color` read it,
45
+ where it forced colour on. Exactly `0` and `false` are off, on a terminal
46
+ too, and any other value still forces colour.
47
+
48
+ ## 0.21.0
49
+
50
+ An input that is set and names nothing is refused, exit 2, by a line that
51
+ names it, where it was read as if it had not been given: an empty spec
52
+ pattern or `--root`, a root that is no directory, `--exclude ","`,
53
+ `--concurrency 0`, a value given to an option that takes none, and an
54
+ `--engine rg` whose binary is not ripgrep. A wrong command line is answered
55
+ in that one line, and no longer with the whole help after it.
56
+
57
+ ### Changed
58
+
59
+ - **An empty spec pattern is refused, where it was every spec under the
60
+ root.** `--spec ""`, or `""` for a pattern, was read as the root and ran
61
+ every Markdown file under it, for a run, `query`, `prove`, `cites`,
62
+ `impact` and the server; it is exit 2 and `invalid spec pattern "": it is
63
+ empty`, and `runSpecGuard` and the rest of the package throw the same
64
+ ([ADR-0014](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0014-configuration-and-watch.md)).
65
+ Upgrading: write `.` where every Markdown file under the root is meant, and
66
+ have the shell refuse a variable that is not set, as `"${DOCS:?}"` does.
67
+ - **An empty `--root`, and a root that is not a directory, are refused by
68
+ every command.** `--root ""` ran in the working directory. A root that was
69
+ a file or was not there was "no spec files matched", which `--allow-empty`
70
+ made exit 0, `cites` exited 0 with nothing to look for, and the server
71
+ started; each is exit 2 and a line that names the root. Upgrading: name a
72
+ directory that exists, or leave the option out.
73
+ - **`--concurrency 0` is refused**, as `"concurrency": 0` in a configuration
74
+ always was; it ran as 1. Upgrading: write 1 or more, or leave it out for 8.
75
+ - **`--exclude` given separators and nothing else, as in `","`, is
76
+ refused**, where it cleared the configuration's list. Upgrading: `--exclude=`
77
+ with nothing still clears it.
78
+ - **An option that takes no value is refused with one.** What followed the
79
+ `=` was never read, so `--strict=false` was strict, `--default-skips=false`
80
+ kept the default skips, `--allow-empty=false` let an empty run through and
81
+ `--json=false` wrote JSON. Upgrading: give the option alone or leave it
82
+ out; `--no-strict`, `--no-default-skips` and the other opposites turn one
83
+ off.
84
+ - **`--engine rg` is refused where `SPEC_GUARD_RG` names no ripgrep that
85
+ runs, in a line that names the variable.** A program that answers
86
+ `--version` and is not ripgrep was taken for one: every search failed, the
87
+ scanner answered, and the run exited 0 with a warning for each. A path that
88
+ was not there was blamed on `PATH`, and on Windows a file that is no
89
+ program ended the run with `spawn EFTYPE`. Each is exit 2 before anything
90
+ is searched, and `findRipgrep()` from the package answers `null` for each
91
+ ([ADR-0004](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0004-adaptive-engine.md)).
92
+ Upgrading: set the variable to the path of an `rg` that runs, unset it to
93
+ use the one on `PATH`, or leave `--engine rg` out: `auto` still leaves a
94
+ binary it cannot use to the scanner.
95
+ - **`FORCE_COLOR=0` and `TERM=dumb` turn colour off on a terminal**, as the
96
+ family reads them (spec-core's
97
+ [ADR-0005](https://github.com/DescentVTT/spec-core/blob/main/docs/adr/0005-the-family-contract.md)).
98
+ `FORCE_COLOR=0` only declined to force colour, and `TERM` was not read for
99
+ colour, so a terminal that says it draws none was written escapes. Any
100
+ other `FORCE_COLOR` and `--color` still force it, over `TERM=dumb` too.
101
+ Upgrading: unset the variable, or pass `--color`.
102
+
103
+ ### Fixed
104
+
105
+ - A wrong command line is answered in one line, `spec-guard:` and what was
106
+ wrong, as every other refusal is. The whole help followed it, a hundred
107
+ lines, so the reason was the first line of a log that is read from its
108
+ end. Exit 2, as before; `spec-guard --help` prints the help.
109
+ - A `package.json` or `.spec-guard.json` that is not JSON is refused in one
110
+ line. The parser quotes a short file whole, line breaks and all, so a file
111
+ of three lines made a refusal of three; each run of line breaks and spaces
112
+ in what it says is now one space. Exit 2, as before.
113
+
114
+ ### Documentation
115
+
116
+ - `--help` and the README's options table name `SPEC_GUARD_RG` beside
117
+ `--engine`, and `FORCE_COLOR` and `TERM=dumb` beside `--color`, and say
118
+ that `NO_COLOR`, `FORCE_COLOR` or `SPEC_GUARD_RG` set to nothing is as
119
+ unset.
120
+
13
121
  ## 0.20.1
14
122
 
15
123
  A reader that closed the output, as `| head` does, was answered with
package/README.md CHANGED
@@ -1064,6 +1064,13 @@ instructions tell an agent when to call each: the rules before creating or
1064
1064
  changing a file, the dependents before changing a file others import, and the
1065
1065
  check after changing files.
1066
1066
 
1067
+ The server starts whatever its patterns match, since a spec may be written
1068
+ while it runs. Where no spec file matches, the first two tools answer with an
1069
+ error that names the patterns. Where one pattern matches none beside others
1070
+ that do, each answer is given from the rest and ends by naming the pattern it
1071
+ holds no rule from, with `unmatchedSpecs` in its structured content and in
1072
+ `spec://rules`.
1073
+
1067
1074
  | Resource | What it holds |
1068
1075
  | --- | --- |
1069
1076
  | `spec://rules` | Every rule in force, as JSON |
@@ -1251,7 +1258,9 @@ src/db/client.ts
1251
1258
  - **Exit 2** for a path that does not exist, and for a run of `impact` in which
1252
1259
  no spec matched, as `query` does: the dependents are still written, but "no
1253
1260
  rules" over no specs is nothing measured. `--allow-empty` asks for the
1254
- dependents alone, and exits 0.
1261
+ dependents alone, and exits 0. A spec pattern that matched no file beside
1262
+ others that did is the same: the dependents and the rules that were read
1263
+ are written, and it is exit 2 unless `--allow-empty`.
1255
1264
  - **The MCP server** answers the same question as `get_dependents`, with the
1256
1265
  same document.
1257
1266
  [ADR-0018](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0018-impact.md) has the table and what it leaves out.
@@ -1270,27 +1279,27 @@ spec-guard impact <paths...> # the files that depend on each path, and
1270
1279
 
1271
1280
  | Option | Description |
1272
1281
  | --- | --- |
1273
- | `-r, --root <path>` | Codebase root that assertions resolve against (default: cwd) |
1274
- | `--spec <pattern>` | A spec glob or path, repeatable. `query` and `mcp` take their specs only from here |
1282
+ | `-r, --root <path>` | Codebase root that assertions resolve against, a directory (default: cwd). One that is not there, or is a file, is exit 2 from every command |
1283
+ | `--spec <pattern>` | A spec glob or path, repeatable. `query` and `mcp` take their specs only from here. An empty one is refused: `.` is every Markdown file under the root. So is one that matches no file beside others that do (below) |
1275
1284
  | `-v, --verbose` | Print passing assertions too |
1276
1285
  | `--watch` | Report, then report again as the tree changes, until Ctrl+C |
1277
1286
  | `--fail-fast` | Stop at the first failing assertion |
1278
1287
  | `--json` | Machine-readable report on stdout (same as `--format json`), versioned by `formatVersion` |
1279
1288
  | `--format <human\|json\|sarif\|github\|gitlab>` | Output format. `sarif` uploads to GitHub code scanning, `github` annotates a pull request from the job's log, `gitlab` is a GitLab Code Quality report |
1280
- | `--engine <auto\|rg\|js>` | Search engine (default `auto`: scanner for small trees, ripgrep for big ones) |
1289
+ | `--engine <auto\|rg\|js>` | Search engine (default `auto`: scanner for small trees, ripgrep for big ones). ripgrep is `rg` on `PATH`, or the program `SPEC_GUARD_RG` names; `rg` with no ripgrep that runs is exit 2 ([below](#ci-integration)) |
1281
1290
  | `--strict` / `--no-strict` | Treat analysis that could not be completed as a failure, and a run that executed no assertion: its specs matched and state no rule in force |
1282
1291
  | `--allow-missing-targets` / `--no-allow-missing-targets` | Warn instead of failing when a `target` path does not exist |
1283
1292
  | `--allow-empty-scope` / `--no-allow-empty-scope` | Warn instead of failing when an assertion inspects no files |
1284
1293
  | `--print-baseline` | Print the `baseline="..."` that would exempt today's violations, and exit |
1285
1294
  | `--no-default-skips` / `--default-skips` | Search `.git`, `.hg`, `.svn` and `node_modules` too |
1286
- | `--exclude <globs>` | Paths no assertion looks at, beside each directive's own `exclude`. Repeatable; replaces the configuration's list, and `--exclude=` clears it |
1295
+ | `--exclude <globs>` | Paths no assertion looks at, beside each directive's own `exclude`. Repeatable; replaces the configuration's list, and `--exclude=` clears it. Separators with nothing between them, `","`, are refused |
1287
1296
  | `--ignore-status` / `--no-ignore-status` | Execute directives in documents not in force too: draft, proposed, rejected, deprecated, superseded or archived |
1288
1297
  | `--include-specs` / `--no-include-specs` | Also count matches inside the spec files themselves |
1289
1298
  | `--max-snippets <n>` | Failure snippets per assertion (default 5) |
1290
- | `--concurrency <n>` | Search passes in flight at once (default 8) |
1299
+ | `--concurrency <n>` | Search passes in flight at once, 1 or more (default 8) |
1291
1300
  | `--depth <n>` | `impact` only: follow dependents at most `n` imports away (default: all) |
1292
- | `--allow-empty` | Exit 0 when no spec file matched the patterns, for a run, `prove` or `impact` (about the run, not an assertion) |
1293
- | `--color` / `--no-color` | Force colour on or off (`NO_COLOR` honoured) |
1301
+ | `--allow-empty` | Let a run, `prove` or `impact` through when no spec file matched the patterns, or when one pattern matched none beside others that did, each of which is exit 2 without it; such a pattern is still named on stderr (about the run, not an assertion) |
1302
+ | `--color` / `--no-color` | Force colour on or off. Without either flag, `NO_COLOR` set to anything turns it off; otherwise `FORCE_COLOR` turns it on, and `FORCE_COLOR=0` or `FORCE_COLOR=false` off; otherwise `TERM=dumb` turns it off; otherwise a terminal has colour. `NO_COLOR` or `FORCE_COLOR` set to nothing is as unset |
1294
1303
 
1295
1304
  Patterns are expanded by spec-guard itself, so quoted globs behave identically
1296
1305
  on Windows, macOS and Linux. A directory expands to the Markdown files in it.
@@ -1301,6 +1310,41 @@ An option that means nothing to a command is refused rather than ignored, so
1301
1310
  `--no-color`: its output never has colour, and scripts pass the flag to every
1302
1311
  command they run.
1303
1312
 
1313
+ So is an option that is set and names nothing, which is exit 2 and a line
1314
+ that names it, never a run as if it had not been given. `--root "$DIR"` and
1315
+ `--spec "$DOCS"` with the variable unset were the working directory and every
1316
+ Markdown file under the root; `--concurrency 0` was 1; and an option that is
1317
+ on when it is given was on whatever followed its `=`, so `--strict=false` was
1318
+ strict and `--allow-empty=false` let an empty run through. An option a
1319
+ configuration can set has an opposite to turn it off, `--no-strict`; any
1320
+ other is left out.
1321
+
1322
+ And so is a spec pattern that matches no file beside others that do. After
1323
+ `old-docs` was renamed, `spec-guard docs old-docs` ran the specs under `docs`
1324
+ and said nothing of the rest: every rule in the renamed directory had stopped
1325
+ running, and the check was green. A run, `prove`, `impact` and `query` now
1326
+ exit 2 with one line that names each such pattern, wherever it was written, on
1327
+ the command line or in `specs`:
1328
+
1329
+ ```text
1330
+ spec-guard: the pattern "old-docs" matched no spec file. Remove it from "specs" in package.json, or give --allow-empty where a pattern is meant to match nothing.
1331
+ ```
1332
+
1333
+ - **`--allow-empty`** lets a run, `prove` or `impact` through, for a place
1334
+ nothing is written in yet, and the pattern is still named on stderr.
1335
+ `query` takes no such option.
1336
+ - **A document a script reads is still written**, by `--json`,
1337
+ `--format sarif` and `--format gitlab`, and the JSON ones carry the
1338
+ patterns as `unmatchedSpecs`. The key is there only when a pattern matched
1339
+ no file, and `formatVersion` stays 1: a field added does not move it.
1340
+ - **`--watch` and `mcp` carry on**, since the file may yet appear. A session
1341
+ says which pattern it is waiting on under each report, and each answer the
1342
+ server gives names the pattern it holds no rule from, with `unmatchedSpecs`
1343
+ in its structured content.
1344
+ - **No spec at all is as it was**: `no spec files matched ...`, exit 2, or 0
1345
+ under `--allow-empty`. So is `docs/**/*.md`, the pattern read when none is
1346
+ given, which is alone and so matches or leaves no spec at all.
1347
+
1304
1348
  ### Configuration
1305
1349
 
1306
1350
  The options that are a project's policy can live in `package.json`, under
@@ -1415,7 +1459,9 @@ goes.
1415
1459
 
1416
1460
  `--watch` scans in-process, where it can see what each rule reads, so it refuses
1417
1461
  `--engine`, and `--json`, `--format json|sarif`, `--print-baseline`,
1418
- `--fail-fast` and `--allow-empty`, which each describe a single run. It exits
1462
+ `--fail-fast` and `--allow-empty`, which each describe a single run. A spec
1463
+ pattern that matches no file does not stop a session: it waits for one to
1464
+ appear, and says which pattern that is under each report. It exits
1419
1465
  130 when stopped, since a session is neither a pass nor a failure. What it
1420
1466
  cannot see: a change the operating system never reports, a change behind a
1421
1467
  symbolic link, and anything outside the root. CI stays the authority.
@@ -1431,7 +1477,7 @@ should be able to tell them apart, so they are 1 and 2:
1431
1477
  | `1` | An assertion failed, or a directive was malformed; under `--strict`, the specs matched state no rule in force, so no assertion was executed |
1432
1478
  | `1`, `prove` | A rule survived, or under `--strict` none was proved |
1433
1479
  | `1`, `cites` | A comment cites a document that does not exist, or under `--strict` a citation is stale, a file was read in part, or no source file was read |
1434
- | `2` | spec-guard could not run, or its answer did not arrive: bad usage, a malformed configuration, no spec files matched, `--engine rg` with no ripgrep, a watch that could not start, a `cites` family whose files match no document, a stdout its reader closed before all of the output was written, as a pipeline into `head` does, said in one line on stderr, or an error spec-guard did not expect, reported on stderr as `spec-guard: unexpected error:` with its stack |
1480
+ | `2` | spec-guard could not run, or its answer did not arrive: bad usage, an option set to something that names nothing, a root that is not a directory, a malformed configuration, no spec files matched, a spec pattern that matched none beside others that did, `--engine rg` with no ripgrep, a watch that could not start, a `cites` family whose files match no document, a stdout its reader closed before all of the output was written, as a pipeline into `head` does, said in one line on stderr, or an error spec-guard did not expect, reported on stderr as `spec-guard: unexpected error:` with its stack |
1435
1481
  | `130` | A `--watch` session was stopped |
1436
1482
 
1437
1483
  ## CI integration
@@ -1491,10 +1537,22 @@ runs:
1491
1537
  | Windows | `winget install BurntSushi.ripgrep.MSVC` |
1492
1538
 
1493
1539
  spec-guard looks for `rg` on `PATH`. `SPEC_GUARD_RG=/path/to/rg` names the
1494
- binary instead, for one that is not on `PATH` or not called `rg`. Under the
1495
- default `--engine auto`, a binary that cannot be run leaves the built-in
1496
- scanner to do the work, and the report's engine says `javascript`; under
1497
- `--engine rg` it is exit 2.
1540
+ binary instead, for one that is not on `PATH` or not called `rg`; set to
1541
+ nothing, it is as unset. Under the default `--engine auto`, a binary that
1542
+ cannot be run leaves the built-in scanner to do the work, and the report's
1543
+ engine says `javascript`. `--engine rg`, or `"engine": "rg"` in the
1544
+ configuration, is ripgrep asked for, and a run that cannot have it is exit 2
1545
+ before anything is searched, never a run on the scanner: where the variable
1546
+ names a path that is not there, a file that is no program, or a program that
1547
+ does not answer `--version` as ripgrep does, the line names the variable and
1548
+ what it holds:
1549
+
1550
+ ```text
1551
+ spec-guard: SPEC_GUARD_RG is "/opt/tools/rg", which did not answer --version as ripgrep does, and the rg engine was asked for. Set it to the path of an rg that runs, or unset it to use the rg on PATH.
1552
+ ```
1553
+
1554
+ A program that runs and is not ripgrep fails each search `auto` hands it; the
1555
+ scanner answers that search, and the report carries a warning for it.
1498
1556
 
1499
1557
  As a pre-commit hook, in a project that installed it:
1500
1558
 
@@ -1840,8 +1898,8 @@ build fails if they stop being true. CI proves every one of those rules can
1840
1898
  fail, and `spec-guard cites` holds every ADR a comment in the code cites to
1841
1899
  existing and being in force.
1842
1900
 
1843
- Line coverage is 100%, CI's full mutation sweep kills 99.16% of 12,346 mutants
1844
- (0.20.1), and every survivor a release adds is replayed before it ships
1901
+ Line coverage is 100%, CI's full mutation sweep kills 99.23% of 12,580 mutants
1902
+ (0.22.0), and every survivor a release adds is replayed before it ships
1845
1903
  ([ADR-0003](https://github.com/DescentVTT/spec-guard/blob/main/docs/adr/0003-mutation-testing.md));
1846
1904
  [CONTRIBUTING.md](https://github.com/DescentVTT/spec-guard/blob/main/CONTRIBUTING.md)
1847
1905
  has how to build, test and release, and
package/dist/cli.d.ts CHANGED
@@ -5,8 +5,9 @@
5
5
  * 0 - every assertion held
6
6
  * 1 - an assertion failed, a directive was invalid, or under --strict no
7
7
  * assertion was executed
8
- * 2 - spec-guard could not run (bad usage, no spec files, missing engine),
9
- * or met an error it did not expect, whose stack is on stderr
8
+ * 2 - spec-guard could not run (bad usage, no spec files, a spec pattern
9
+ * that matched none beside others that did, missing engine), or met an
10
+ * error it did not expect, whose stack is on stderr
10
11
  */
11
12
  import type { Readable } from 'node:stream';
12
13
  import { type ConfigKey, type ProjectConfig } from './config.js';
@@ -94,7 +95,7 @@ export declare class UsageError extends Error {
94
95
  * fallback nobody knows still works.
95
96
  */
96
97
  export declare function version(manifest?: string): string;
97
- export declare const HELP = "spec-guard - Executable architecture assertions for Markdown specs & ADRs\n\nUsage\n spec-guard [patterns...] [options] execute the directives in the specs\n spec-guard query <paths...> [options] list the rules in force for files or directories\n spec-guard mcp [options] serve the rules to an AI agent over MCP on stdio\n spec-guard prove [patterns...] [options]\n show each rule a violation of itself, in memory, and report any that pass\n spec-guard cites [paths...] [options] check that each spec a code comment cites exists and is in force\n spec-guard impact <paths...> [options] the files that depend on each path, and the rules in play there\n\nPatterns\n Globs or paths to the Markdown specs to execute. A directory expands to the\n Markdown files inside it. Defaults to \"docs/**/*.md\" when omitted. query,\n mcp, cites and impact take theirs from --spec.\n\nOptions\n -r, --root <path> Codebase root that assertions are resolved against (default: cwd)\n --spec <pattern> A spec glob or path; repeatable (default: \"docs/**/*.md\")\n -v, --verbose Print passing assertions too\n --watch Run again as the tree changes, until Ctrl+C (human output only)\n --fail-fast Stop at the first failing assertion\n --json Emit a machine-readable JSON report (same as --format json)\n --format <name> human | json | sarif | github | gitlab (sarif uploads to code scanning,\n github annotates a pull request from the log, gitlab is Code Quality)\n --engine <name> auto | rg | js (default: auto - scanner for small trees, ripgrep for big ones)\n --strict Treat analysis that could not be completed, and a run that\n executed no assertion, as a failure\n --allow-missing-targets\n Tolerate target paths that do not exist (they fail by default)\n --allow-empty-scope Tolerate assertions whose scope holds no files (they fail by default)\n --print-baseline Print the baseline=\"...\" that would exempt today's violations, and exit\n --no-default-skips Search .git, .hg, .svn and node_modules too\n --exclude <globs> Paths no assertion looks at, beside each directive's exclude; repeatable\n --ignore-status Execute directives in documents not in force too (see Directives)\n --include-specs Also count matches inside the spec files themselves\n --max-snippets <n> Failure snippets per assertion (default: 5)\n --concurrency <n> Assertions executed in parallel (default: 8)\n --depth <n> impact: follow dependents at most n imports away (default: all)\n --allow-empty Exit 0 when no spec files matched (a run, prove or impact; not an assertion)\n --color/--no-color Force colour on or off (NO_COLOR is honoured)\n -h, --help Show this help\n --version Print the version\n\nConfiguration\n package.json in the root can hold specs, exclude, engine, strict,\n allowMissingTargets, allowEmptyScope, ignoreStatus, includeSpecs, defaultSkips,\n maxSnippets and concurrency under \"specGuard\"; a root with no package.json can\n keep them in .spec-guard.json instead, but not in both. A flag wins over the\n file, every on/off option there also takes its opposite (--no-strict,\n --default-skips, ...), and --exclude= with nothing clears exclude.\n \"cites\": [{ \"id\": \"ADR-{n}\", \"files\": \"docs/adr/{n}-*.md\" }] names the\n documents code comments cite; without it, cites reads them off the titles of\n numbered specs.\n\nDirectives\n <!-- @assert-absence target=\"src/\" symbol=\"LegacyGateway\" exclude=\"src/legacy/**\" -->\n <!-- @assert-count target=\"src/\" symbol=\"SessionManager\" expected=\"1\" -->\n <!-- @assert-present file=\"SECURITY.md\" -->\n <!-- @assert-import-cycle target=\"src/\" types=\"ignore\" -->\n <!-- @assert-layers target=\"src/\" order=\"domain, application, infrastructure\" -->\n <!-- @assert-structure target=\"src/\" exclude=\"*.test.ts\" partner=\"[name].test.ts\" -->\n\n Matches inside comments do not count; add comments=\"include\" to count them.\n An assertion whose scope holds no files fails; add allow-empty=\"true\" to allow it.\n A document whose status is draft, proposed, rejected, deprecated, superseded\n or archived is reported and not executed; --ignore-status runs it anyway.\n The status is read in English, whatever language the document is written in:\n status: superseded.\n\nprove adds, changes or removes files in memory only - never on disk - to make\nthe violation each rule forbids, and runs the rule over that tree. A rule that\nstill passes survived, and exits 1; one no violation could be made for is\nunprovable, and exits 1 under --strict.\n\ncites reads comment text only - not strings, not Markdown - for ids such as\nADR-0007, ADR-7 and ADR-007, which are one document. A ghost citation names no\ndocument, and exits 1; a stale one names a document superseded, deprecated,\nrejected or archived, and exits 1 under --strict. A family whose files match no\ndocument exits 2.\n\nimpact follows the import graph backwards: relative JavaScript, TypeScript and\nPython imports that name a file. Go, Rust, C# and absolute Python imports name\nmodules, and are counted rather than followed; an import that names no file is\nlisted, never guessed at.\n\nquery answers from the specs alone, without reading the codebase, so it works\nfor a file that does not exist yet. mcp offers the same answer, a check and\nimpact's, as the tools get_architectural_rules, check_architecture and\nget_dependents.\n\nExit codes\n 0 all assertions passed 1 an assertion failed, or none ran under --strict\n 2 spec-guard could not run 130 a --watch session was stopped";
98
+ export declare const HELP = "spec-guard - Executable architecture assertions for Markdown specs & ADRs\n\nUsage\n spec-guard [patterns...] [options] execute the directives in the specs\n spec-guard query <paths...> [options] list the rules in force for files or directories\n spec-guard mcp [options] serve the rules to an AI agent over MCP on stdio\n spec-guard prove [patterns...] [options]\n show each rule a violation of itself, in memory, and report any that pass\n spec-guard cites [paths...] [options] check that each spec a code comment cites exists and is in force\n spec-guard impact <paths...> [options] the files that depend on each path, and the rules in play there\n\nPatterns\n Globs or paths to the Markdown specs to execute. A directory expands to the\n Markdown files inside it. Defaults to \"docs/**/*.md\" when omitted. query,\n mcp, cites and impact take theirs from --spec.\n\nOptions\n -r, --root <path> Codebase root that assertions are resolved against, a directory (default: cwd)\n --spec <pattern> A spec glob or path; repeatable (default: \"docs/**/*.md\")\n -v, --verbose Print passing assertions too\n --watch Run again as the tree changes, until Ctrl+C (human output only)\n --fail-fast Stop at the first failing assertion\n --json Emit a machine-readable JSON report (same as --format json)\n --format <name> human | json | sarif | github | gitlab (sarif uploads to code scanning,\n github annotates a pull request from the log, gitlab is Code Quality)\n --engine <name> auto | rg | js (default: auto - scanner for small trees, ripgrep for big ones)\n ripgrep is rg on PATH, or the program SPEC_GUARD_RG names; where that\n is no ripgrep that runs, rg is exit 2 and auto leaves it to the scanner\n --strict Treat analysis that could not be completed, and a run that\n executed no assertion, as a failure\n --allow-missing-targets\n Tolerate target paths that do not exist (they fail by default)\n --allow-empty-scope Tolerate assertions whose scope holds no files (they fail by default)\n --print-baseline Print the baseline=\"...\" that would exempt today's violations, and exit\n --no-default-skips Search .git, .hg, .svn and node_modules too\n --exclude <globs> Paths no assertion looks at, beside each directive's exclude; repeatable\n --ignore-status Execute directives in documents not in force too (see Directives)\n --include-specs Also count matches inside the spec files themselves\n --max-snippets <n> Failure snippets per assertion (default: 5)\n --concurrency <n> Assertions executed in parallel, 1 or more (default: 8)\n --depth <n> impact: follow dependents at most n imports away (default: all)\n --allow-empty Let a run, prove or impact through when no spec file matched, or when\n one pattern matched none beside others that did (not an assertion)\n --color/--no-color Force colour on or off (NO_COLOR, FORCE_COLOR and TERM=dumb are honoured)\n -h, --help Show this help\n --version Print the version\n\n An option that is set and names nothing is refused, exit 2, never read as if\n it were not there: an empty --root or spec pattern, --exclude \",\" (--exclude=\n with nothing clears the list), --concurrency 0, a root that is no directory,\n and a value given to an option that takes none, as in --strict=false.\n\n A spec pattern that matches no file, beside others that do, is refused the\n same way and by name, since none of its rules was read: a run, prove, impact\n and query exit 2. --allow-empty lets the first three through and still names\n it; --watch and mcp carry on, and say which pattern it is.\n\nConfiguration\n package.json in the root can hold specs, exclude, engine, strict,\n allowMissingTargets, allowEmptyScope, ignoreStatus, includeSpecs, defaultSkips,\n maxSnippets and concurrency under \"specGuard\"; a root with no package.json can\n keep them in .spec-guard.json instead, but not in both. A flag wins over the\n file, every on/off option there also takes its opposite (--no-strict,\n --default-skips, ...), and --exclude= with nothing clears exclude.\n \"cites\": [{ \"id\": \"ADR-{n}\", \"files\": \"docs/adr/{n}-*.md\" }] names the\n documents code comments cite; without it, cites reads them off the titles of\n numbered specs.\n\nDirectives\n <!-- @assert-absence target=\"src/\" symbol=\"LegacyGateway\" exclude=\"src/legacy/**\" -->\n <!-- @assert-count target=\"src/\" symbol=\"SessionManager\" expected=\"1\" -->\n <!-- @assert-present file=\"SECURITY.md\" -->\n <!-- @assert-import-cycle target=\"src/\" types=\"ignore\" -->\n <!-- @assert-layers target=\"src/\" order=\"domain, application, infrastructure\" -->\n <!-- @assert-structure target=\"src/\" exclude=\"*.test.ts\" partner=\"[name].test.ts\" -->\n\n Matches inside comments do not count; add comments=\"include\" to count them.\n An assertion whose scope holds no files fails; add allow-empty=\"true\" to allow it.\n A document whose status is draft, proposed, rejected, deprecated, superseded\n or archived is reported and not executed; --ignore-status runs it anyway.\n The status is read in English, whatever language the document is written in:\n status: superseded.\n\nprove adds, changes or removes files in memory only - never on disk - to make\nthe violation each rule forbids, and runs the rule over that tree. A rule that\nstill passes survived, and exits 1; one no violation could be made for is\nunprovable, and exits 1 under --strict.\n\ncites reads comment text only - not strings, not Markdown - for ids such as\nADR-0007, ADR-7 and ADR-007, which are one document. A ghost citation names no\ndocument, and exits 1; a stale one names a document superseded, deprecated,\nrejected or archived, and exits 1 under --strict. A family whose files match no\ndocument exits 2.\n\nimpact follows the import graph backwards: relative JavaScript, TypeScript and\nPython imports that name a file. Go, Rust, C# and absolute Python imports name\nmodules, and are counted rather than followed; an import that names no file is\nlisted, never guessed at.\n\nquery answers from the specs alone, without reading the codebase, so it works\nfor a file that does not exist yet. mcp offers the same answer, a check and\nimpact's, as the tools get_architectural_rules, check_architecture and\nget_dependents.\n\nExit codes\n 0 all assertions passed 1 an assertion failed, or none ran under --strict\n 2 spec-guard could not run 130 a --watch session was stopped";
98
99
  /** Minimal, dependency-free argv parser. Supports `--flag value` and `--flag=value`. */
99
100
  export declare function parseArgs(argv: readonly string[], cwd: string): CliOptions;
100
101
  /**
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAIH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EAAqD,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAGpH,OAAO,EAAoB,SAAS,EAAE,MAAM,SAAS,CAAC;AA0BtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACpD,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAExD,eAAO,MAAM,OAAO,IAAI,CAAC;AACzB,eAAO,MAAM,WAAW,IAAI,CAAC;AAC7B,eAAO,MAAM,UAAU,IAAI,CAAC;AAE5B,MAAM,WAAW,KAAK;IACpB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC,UAAU,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,OAAO,CAAC;IACf;;;OAGG;IACH,KAAK,CAAC,EAAE,QAAQ,CAAC;IACjB,iFAAiF;IACjF,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,mFAAmF;IACnF,KAAK,CAAC,EAAE,OAAO,SAAS,CAAC;IACzB,8EAA8E;IAC9E,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;CACnD;AAED;;;;GAIG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,KAAK,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,CAAC;AAK/E,yCAAyC;AACzC,MAAM,MAAM,YAAY,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAuB5E,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,6GAA6G;IAC7G,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,6FAA6F;IAC7F,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6FAA6F;IAC7F,KAAK,CAAC,EAAE,UAAU,EAAE,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,YAAY,CAAC;IACrB,4EAA4E;IAC5E,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,gBAAgB,CAAC;IACzB,mBAAmB,EAAE,OAAO,CAAC;IAC7B,YAAY,EAAE,OAAO,CAAC;IACtB,wEAAwE;IACxE,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,OAAO,CAAC;IACvB,eAAe,EAAE,OAAO,CAAC;IACzB,aAAa,EAAE,OAAO,CAAC;IACvB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,OAAO,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,2DAA2D;IAC3D,KAAK,EAAE,OAAO,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB;;;;;;OAMG;IACH,eAAe,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC;CACjC;AAED,qBAAa,UAAW,SAAQ,KAAK;CAAG;AAExC;;;;;;GAMG;AACH,wBAAgB,OAAO,CAAC,QAAQ,SAAoB,GAAG,MAAM,CAQ5D;AAED,eAAO,MAAM,IAAI,wxLA6FgD,CAAC;AAmHlE,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,UAAU,CAgN1E;AA4DD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,SAAiB,GAAG,SAAS,GAAG,SAAS,CAuBpH;AAiCD,iEAAiE;AACjE,wBAAgB,SAAS,IAAI,KAAK,CAqBjC;AAyPD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,IAAI,CAAC,IAAI,GAAE,SAAS,MAAM,EAA0B,EAAE,EAAE,GAAE,KAAmB,GAAG,OAAO,CAAC,MAAM,CAAC,CASpH"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}