@cosyte/script-utils 0.1.5 → 0.1.7
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 +18 -0
- package/README.md +86 -54
- package/agent-notes.d.ts +60 -9
- package/agent-notes.js +51 -14
- package/bin/cosyte-agent-notes.js +16 -6
- package/bin/cosyte-attw.js +3 -2
- package/bin/cosyte-catalog.js +8 -6
- package/bin/cosyte-claude-card.js +3 -2
- package/bin/cosyte-internal-refs.js +3 -2
- package/bin/cosyte-phi-scan.js +3 -2
- package/bin/cosyte-readme.js +3 -2
- package/bin/help.js +41 -0
- package/bin/script-utils.js +9 -4
- package/catalog.d.ts +10 -3
- package/catalog.js +44 -15
- package/check-no-emdash.sh +180 -15
- package/claude-card.js +11 -2
- package/internal-refs.d.ts +31 -0
- package/internal-refs.js +461 -14
- package/package.json +1 -1
- package/phi-scan.d.ts +11 -0
- package/phi-scan.js +174 -23
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.1.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 9d0d69f: Deprecated: the agent-notes checker, `@cosyte/script-utils/agent-notes` and its bin `cosyte-agent-notes`. The agent notes, `documentation/agent-notes.md`, were removed from every Cosyte repository (D62), so remove the package script or CI step that runs it. Until a minor release removes the entry point and the bin, a repository with no notes file (not tracked, or deleted from the working tree) passes: the checker exits 0 with one line, `agent notes: OK, no agent notes (retired, D62)`, where it used to fail on a pointer left in the card, a source comment or a changelog. A repository that still has the file is checked as before, and a failing run ends with a line saying the notes are retired. `--help`, the `script-utils` command list, the declarations and the README say it is deprecated.
|
|
8
|
+
- 9d0d69f: The CLAUDE.md card checker no longer sends detail to `documentation/agent-notes.md`, which was removed from every repository (D62). A card over 5,120 bytes, or with a level-2 heading other than Gate, Hazard and Rules, is told to move the detail to where it is used: a test, a comment at the code it guards, an ADR, the README or docs, or a runbook. What the checker checks, and its exit codes, are unchanged.
|
|
9
|
+
|
|
10
|
+
## 0.1.6
|
|
11
|
+
|
|
12
|
+
### Patch Changes
|
|
13
|
+
|
|
14
|
+
- 239ac9a: The catalog check a library runs is now advisory. `cosyte-catalog` (and `catalog.js`) still compares the package.json description with the catalog this release carries, but a disagreement, or a package the catalog does not list, prints both values as a note and exits 0: a library that changes its description is no longer held red until the next script-utils release. `--strict` (or `{ strict: true }`) restores the gate, 1 for a disagreement and 2 for an unlisted package; 2 still means the check could not run. Keeping the catalog fresh moves to cosyte/config, which compares it with every library's latest release on npm (its description, release line and engines range) nightly and before each config release, and regenerates it from there.
|
|
15
|
+
- 4e30735: The dash gate reads a changelog's dated archive the way the repositories that keep one already do. A line in `scripts/check-no-emdash.exclude` of the form `CHANGELOG.md em-only below ## Released before this file was generated` reads the file from that exact line to its end for the em dash alone, in every form, so an en dash in an old range passes while an em dash still reds; `CHANGELOG.md unread below <line>` does not read the archive at all. Everything above the line is read for both dashes as before, the file's name is still scanned, and a boundary line the file no longer carries is refused rather than ignored. The test suite now spells out every encoded form terminology's own scanner carried (the braced escape, lower-case URL encoding, entities with no semicolon or with leading zeros, and the upper-case X), all of which 0.1.4 already refused. The internal-refs slice rule no longer counts `the` or `each` as a determiner: a fixed-width reader's field slice (`the field slices`, `the slice holding the concept code`) is the reader's vocabulary. `this`, `that`, `another`, `previous`, `next`, `final` and `current` still red, so `the final slice` and `the next slice` are still hits, but a bare `the slice` and `each slice` no longer are.
|
|
16
|
+
- 409ef6c: Under CI, every gate's `--help` and `-h` now exit non-zero instead of 0: the bins exit 2 and the dash gate exits 1, after printing the help to stderr with a line saying why. Help checks nothing, so a package script such as `"attw": "cosyte-attw --help"` used to pass a required check having run no gate. CI is read the way the common runners set it: `CI` set to anything but an empty string, `false` or `0`. At a terminal `--help` prints to stdout and exits 0 as before.
|
|
17
|
+
- 91f8d76: The internal-refs canonical rules take what generalizes from dicom's own gate. An umbrella spec id (`S0228-github-profile-ci-drift`, never a HCPCS code such as `S9083`) is an identifier. The phase rule also catches the adjective forms (`in subsequent phases`, `a later wave`, `waves 2 and 3`) while letting the clinical senses through (`a later phase of the disease`), and a citation of the roadmap (`tracked by the roadmap`). The ADR rule reads the plural, `#` and two-digit forms (`ADRs 0015`, `ADR #0018`, `ADR 23`). The jargon rule catches process commentary (`a later PR`, `this changeset`, `out of scope for this change`) but not `this spec`, `this session` or `this port`, which are reader words in a parser, and lets an image slice in its volume through. The repo-path rule catches the planning files a session writes (`CONTEXT.md`, `REQUIREMENTS.md`, `PITFALLS.md`), the meta-repo or umbrella repo named as a place, and its `work/` and `cards/` directories, and a `decisions/NNNN-` path wherever it sits. Measured on the pinned libraries, this adds 12 hits, all of them real (in hl7, x12 and ncpdp), and removes none. dicom's decision-number, issue-number, `WORD-N` and bare section rules stay dicom's: each collides with reference material another library publishes.
|
|
18
|
+
- d75579c: The internal-refs gate gains a source string-literal pass, `sourceStringLiterals: { enabled, paths, extraRules }`, off by default like the doc-comment pass. It reads the double-quoted, single-quoted and template literals in the tracked source under `paths`, decoded, plus the literals on each line joined, because a string literal reaches a consumer as error and warning text. It never reads a comment, so an item identifier in a `//` note or a `/* */` block stays allowed, and it skips regular-expression literals. In both source passes a NUL byte now reads as a space instead of refusing the file: a raw separator inside a literal is legitimate source, and a space keeps the word boundary beside it. The public-surface pass still refuses a NUL-bearing target.
|
|
19
|
+
- d30eca0: The PHI engine reads a JavaScript or TypeScript source as the document its string literals spell, as well as its bytes. A literal can spell a dashed identifier's separators as `-`, `\u{2D}` or `\x2D`, and the raw floor read straight past it. Each `.ts`, `.js`, `.mts`, `.cts`, `.mjs`, `.cjs`, `.tsx` and `.jsx` target is now also decoded (an escaped backslash first, so a doubled backslash stays text), and the floor and the caller's `detect` run over that view too, so a field-level detector can parse a record whose separators are escapes. The decoded view adds only what the raw view did not report, and says `escape-encoded in a source literal` on the hit. It is a view, not a parser, so it also decodes a `String.raw` template or a comment quoting an escape; an `ID <value>` declaration in the allow-list clears such a hit. `decodeSourceLiterals` and `isSourceLiteralContainer` are exported.
|
|
20
|
+
|
|
3
21
|
## 0.1.5
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -188,20 +188,6 @@ catalogEntry("@cosyte/hl7")?.docs; // => "https://docs.cosyte.com/hl7"
|
|
|
188
188
|
CATALOG.engine.displayName; // => "Pathways"
|
|
189
189
|
```
|
|
190
190
|
|
|
191
|
-
The agent-notes checker is the eighth. It resolves every pointer into a repository's one notes
|
|
192
|
-
file against that file's headings, slugged the way GitHub slugs them:
|
|
193
|
-
|
|
194
|
-
```ts runnable
|
|
195
|
-
import { checkAgentNotes, slugify } from "@cosyte/script-utils/agent-notes";
|
|
196
|
-
|
|
197
|
-
slugify("Why `attw` exits 0"); // => "why-attw-exits-0"
|
|
198
|
-
|
|
199
|
-
const claude = "# demo\n\nSee documentation/agent-notes.md#setup.\n";
|
|
200
|
-
const notes = "# Notes\n\n## Setup\n\nRun it.\n";
|
|
201
|
-
checkAgentNotes({ claude, notes, files: [] }); // => []
|
|
202
|
-
checkAgentNotes({ claude, notes: notes.replace("Setup", "Install"), files: [] })[0]?.rule; // => "dangling"
|
|
203
|
-
```
|
|
204
|
-
|
|
205
191
|
## Entry points
|
|
206
192
|
|
|
207
193
|
| entry point | what it is |
|
|
@@ -213,7 +199,7 @@ checkAgentNotes({ claude, notes: notes.replace("Setup", "Install"), files: [] })
|
|
|
213
199
|
| `@cosyte/script-utils/readme` | `runReadmeGate(options)` and `checkReadme(text, manifest)`, the README gate |
|
|
214
200
|
| `@cosyte/script-utils/claude-card` | `runClaudeCardGate(options)` and `checkClaudeCard(text)`, the CLAUDE.md card checker |
|
|
215
201
|
| `@cosyte/script-utils/catalog` | `CATALOG`, `catalogEntry(name)` and `runCatalogCheck(options)`, the catalog of our libraries |
|
|
216
|
-
| `@cosyte/script-utils/agent-notes` | `runAgentNotesCheck(options)` and `checkAgentNotes(input)
|
|
202
|
+
| `@cosyte/script-utils/agent-notes` | deprecated (D62): `runAgentNotesCheck(options)` and `checkAgentNotes(input)` |
|
|
217
203
|
|
|
218
204
|
They are separate subpaths because they are separately adoptable: a repo can take the entry-point
|
|
219
205
|
guard without taking a position on PHI scanning, a repo can take the internal-reference gate without
|
|
@@ -258,19 +244,22 @@ npx -y @cosyte/script-utils@0.1 no-emdash # the dispatcher: script-util
|
|
|
258
244
|
npx -y -p @cosyte/script-utils@0.1 cosyte-readme # or one gate's own bin
|
|
259
245
|
```
|
|
260
246
|
|
|
261
|
-
| bin | `script-utils` command | the gate
|
|
262
|
-
| ---------------------- | ---------------------- |
|
|
263
|
-
| `cosyte-no-emdash` | `no-emdash` | the em and en dash gate, over files and names
|
|
264
|
-
| `cosyte-attw` | `attw` | the attw publish gate, for the package here
|
|
265
|
-
| `cosyte-phi-scan` | `phi-scan` | the PHI commit-gate
|
|
266
|
-
| `cosyte-internal-refs` | `internal-refs` | the internal-reference gate
|
|
267
|
-
| `cosyte-readme` | `readme` | the README gate
|
|
268
|
-
| `cosyte-claude-card` | `claude-card` | the CLAUDE.md card checker
|
|
269
|
-
| `cosyte-agent-notes` | `agent-notes` | the agent-notes checker
|
|
270
|
-
| `cosyte-catalog` | `catalog` | the catalog check
|
|
247
|
+
| bin | `script-utils` command | the gate |
|
|
248
|
+
| ---------------------- | ---------------------- | ------------------------------------------------- |
|
|
249
|
+
| `cosyte-no-emdash` | `no-emdash` | the em and en dash gate, over files and names |
|
|
250
|
+
| `cosyte-attw` | `attw` | the attw publish gate, for the package here |
|
|
251
|
+
| `cosyte-phi-scan` | `phi-scan` | the PHI commit-gate |
|
|
252
|
+
| `cosyte-internal-refs` | `internal-refs` | the internal-reference gate |
|
|
253
|
+
| `cosyte-readme` | `readme` | the README gate |
|
|
254
|
+
| `cosyte-claude-card` | `claude-card` | the CLAUDE.md card checker |
|
|
255
|
+
| `cosyte-agent-notes` | `agent-notes` | deprecated (D62): the retired agent-notes checker |
|
|
256
|
+
| `cosyte-catalog` | `catalog` | the catalog check |
|
|
271
257
|
|
|
272
258
|
Each runs over the working directory, takes the arguments its gate takes, exits with the gate's own
|
|
273
259
|
code, and prints what it does with `--help`. `script-utils` with no command prints the list and exits 2.
|
|
260
|
+
Under CI (`CI` set to anything but empty, `false` or `0`, as GitHub Actions sets it) `--help` and
|
|
261
|
+
`-h` print to stderr and exit non-zero (2, or 1 for the dash gate, whose only codes are 0 and 1): help
|
|
262
|
+
checks nothing, so a package script that ends in `--help` must not pass a required check.
|
|
274
263
|
|
|
275
264
|
### The em-dash gate, which is a file rather than a subpath
|
|
276
265
|
|
|
@@ -308,6 +297,16 @@ its backslash-u escape (plain, braced and eight-digit), its braced backslash-x e
|
|
|
308
297
|
HTML entities, with or without the semicolon and with leading zeros. The default scan also reads
|
|
309
298
|
every tracked file's NAME, because a name is copy too.
|
|
310
299
|
|
|
300
|
+
A changelog whose dated archive predates the generated part names that archive with a boundary line
|
|
301
|
+
in the same file, `CHANGELOG.md em-only below ## Released before this file was generated` or
|
|
302
|
+
`CHANGELOG.md unread below ## Released before this file was generated`. The text after `below` is a
|
|
303
|
+
whole line of that file, matched exactly; from it to the end of the file is the archive. `em-only`
|
|
304
|
+
reads the archive for the em dash alone, in every form, so an en dash in an old range passes and an em
|
|
305
|
+
dash still fails; `unread` does not read the archive. Above the line the file is read for both dashes
|
|
306
|
+
like any other, and its name is scanned as always. A boundary line the file no longer carries is
|
|
307
|
+
refused, so a lost heading fails closed, and a file cannot be both left out whole and given a
|
|
308
|
+
boundary.
|
|
309
|
+
|
|
311
310
|
### The README gate
|
|
312
311
|
|
|
313
312
|
`@cosyte/script-utils/readme` checks a library's README against the house skeleton, and against a
|
|
@@ -362,40 +361,56 @@ record is generated from the package's own `package.json` (name, description, re
|
|
|
362
361
|
engines, repository, docs and npm URLs) and carries its tier; the data ships as `catalog.json`
|
|
363
362
|
beside the module, and `CATALOG` is that file, frozen.
|
|
364
363
|
|
|
365
|
-
Each package.json description is that library's one canonical line (ST14)
|
|
366
|
-
|
|
364
|
+
Each package.json description is that library's one canonical line (ST14). Two sides check it, and
|
|
365
|
+
each checks one thing:
|
|
366
|
+
|
|
367
|
+
- **cosyte/config keeps the catalog fresh.** `node scripts/catalog.mjs check --npm` compares the
|
|
368
|
+
committed catalog with every library's latest release on npm. It runs nightly and before each
|
|
369
|
+
config release, and fails when a library has released a description, release line or engines
|
|
370
|
+
range the catalog does not carry; `write --npm` regenerates it. npm keeps 255 characters of a
|
|
371
|
+
description, so a longer one is fresh when npm holds its first 255, and the run notes it.
|
|
372
|
+
- **A library can check its own line, advisory.** With no file of its own:
|
|
367
373
|
|
|
368
374
|
```sh
|
|
369
375
|
node node_modules/@cosyte/script-utils/catalog.js # the package in this directory
|
|
370
376
|
node node_modules/@cosyte/script-utils/catalog.js --dir <path> # another package directory
|
|
377
|
+
node node_modules/@cosyte/script-utils/catalog.js --strict # a disagreement fails
|
|
371
378
|
```
|
|
372
379
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
description
|
|
376
|
-
|
|
380
|
+
It compares the package.json description with the catalog this release of `@cosyte/script-utils`
|
|
381
|
+
carries. A disagreement, or a package the catalog does not list, prints both values as a note and
|
|
382
|
+
exits 0: a library that changes its description is not held red until the next script-utils
|
|
383
|
+
release, because config's check against npm picks the change up once the library is released. Exit
|
|
384
|
+
2 means the check could not run (no package.json, or an argument it does not accept). `--strict`
|
|
385
|
+
restores the gate: 1 for a disagreement, 2 for an unlisted package.
|
|
386
|
+
|
|
387
|
+
### The agent-notes checker, deprecated
|
|
388
|
+
|
|
389
|
+
`@cosyte/script-utils/agent-notes` and its bin, `cosyte-agent-notes`, are deprecated. They check a
|
|
390
|
+
repository's agent notes, `documentation/agent-notes.md`, and every
|
|
391
|
+
`documentation/agent-notes.md#<anchor>` pointer into them. On 2026-11-08 the notes were removed from
|
|
392
|
+
every Cosyte repository (D62): a CLAUDE.md card stands on its own, and what does not fit on it goes
|
|
393
|
+
where it is used, in a test, a comment at the code it guards, an ADR, the README or docs, or a
|
|
394
|
+
runbook (`documentation/claude-card.md` in cosyte/config). Remove the package script or CI step that
|
|
395
|
+
runs the checker.
|
|
396
|
+
|
|
397
|
+
Until a minor release removes them, the entry point and the bin keep a repository that still runs
|
|
398
|
+
the checker green while its notes are removed. With no `documentation/agent-notes.md` (not tracked,
|
|
399
|
+
or deleted from the working tree) the checker exits 0 with one line,
|
|
400
|
+
`agent notes: OK, no agent notes (retired, D62)`, whatever still points at the file. A repository
|
|
401
|
+
that still has the file is checked as before: exit 0 clean, 1 a finding (file, line and rule from
|
|
402
|
+
`AGENT_NOTES_RULES`, then a line saying the notes are retired), and 2 that the check could not run.
|
|
377
403
|
|
|
378
|
-
|
|
404
|
+
```ts runnable
|
|
405
|
+
import { checkAgentNotes, NOTES_PATH } from "@cosyte/script-utils/agent-notes";
|
|
379
406
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
form, `documentation/agent-notes.md#<anchor>`. A repository runs it with no file of its own:
|
|
407
|
+
// The file it checks, which no repository keeps any more.
|
|
408
|
+
NOTES_PATH; // => "documentation/agent-notes.md"
|
|
383
409
|
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
node node_modules/@cosyte/script-utils/agent-notes.js --dir <path> # another repository
|
|
410
|
+
// A card with no notes and nothing pointing at them has no findings.
|
|
411
|
+
checkAgentNotes({ claude: "# demo\n\nOur demo.\n", notes: null, files: [] }); // => []
|
|
387
412
|
```
|
|
388
413
|
|
|
389
|
-
It reads every file `git ls-files` lists, skipping and counting a binary file or anything that is not
|
|
390
|
-
a regular file. Every pointer in any of them must name a heading of the notes (its GitHub slug) or an
|
|
391
|
-
explicit `<a id>` or `<a name>` target. In CLAUDE.md the pointer must take the one form, not a bare
|
|
392
|
-
backticked `#anchor` or `agent-notes.md#` without its directory. A `documentation/agent-notes/`
|
|
393
|
-
directory is a finding, and so is a notes file CLAUDE.md never points into, a blank one, an empty
|
|
394
|
-
section (a heading followed by a deeper one is a container, not empty), and a citation of the
|
|
395
|
-
umbrella's deleted ADR 0023 or its doc-budget hook. A repository with no notes and no pointers
|
|
396
|
-
passes. Exit 0 means clean, 1 a finding (file, line and rule from `AGENT_NOTES_RULES`), and 2 that
|
|
397
|
-
the check could not run.
|
|
398
|
-
|
|
399
414
|
## Overrides
|
|
400
415
|
|
|
401
416
|
`isCliEntrypoint` has no options and cannot be overridden. It answers one question and its
|
|
@@ -528,6 +543,13 @@ every `scanRoots` entry yielded a file the WALK read, naming the starved roots);
|
|
|
528
543
|
cross-cutting detection floor that finds a dashed SSN shape and an email at an undeclared domain over
|
|
529
544
|
every file, always.
|
|
530
545
|
|
|
546
|
+
**A JavaScript or TypeScript source is read twice**: as its bytes, and as the document its string
|
|
547
|
+
literals spell, with `\uXXXX`, `\u{...}`, `\xHH` and the single-character escapes decoded (an
|
|
548
|
+
escaped backslash first, so a doubled backslash before `u002D` stays text). The floor and your
|
|
549
|
+
`detect` run over both views, and the decoded view adds only what the raw view did not report, with
|
|
550
|
+
`, escape-encoded in a source literal` on its reason. It is a view, not a parser, so it also decodes a
|
|
551
|
+
`String.raw` template or a comment quoting an escape; an `ID <value>` declaration clears such a hit.
|
|
552
|
+
|
|
531
553
|
**The RULES, local to your repo, never moved and never shipped in this package.**
|
|
532
554
|
`scripts/phi-allow-list.txt` (the positive declaration that a token is synthetic, tagged `NAME`,
|
|
533
555
|
`DOB`, `ADDR`, `ID`, `EMAILDOMAIN`); `phi-scan-overrides.md` (the `--allow-fixture` audit log, and
|
|
@@ -593,7 +615,7 @@ still refuses such an entry on both of its routes.
|
|
|
593
615
|
`@cosyte/script-utils/internal-refs` keeps our own bookkeeping off every surface a consumer reads:
|
|
594
616
|
item identifiers, phase and wave language, ADR numbers, internal repository paths and traceability
|
|
595
617
|
markers, on the pages, the npm metadata and, optionally, the doc comments that compile into shipped
|
|
596
|
-
type declarations.
|
|
618
|
+
type declarations and the string literals a consumer reads as error and warning text.
|
|
597
619
|
|
|
598
620
|
```ts
|
|
599
621
|
import { runInternalRefsScan } from "@cosyte/script-utils/internal-refs";
|
|
@@ -605,6 +627,7 @@ process.exit(
|
|
|
605
627
|
surfacePaths: ["README.md", "TRADEMARKS.md", "LICENSE", "docs-content"],
|
|
606
628
|
accountedTarballFiles: ["CHANGELOG.md", "dist"],
|
|
607
629
|
sourceDocComments: { enabled: true, paths: ["src/*.ts", "src/**/*.ts"] },
|
|
630
|
+
sourceStringLiterals: { enabled: true, paths: ["src/*.ts", "src/**/*.ts"] },
|
|
608
631
|
}),
|
|
609
632
|
);
|
|
610
633
|
```
|
|
@@ -632,14 +655,23 @@ gets wrong, and a default would be one repository's answer imposed on every othe
|
|
|
632
655
|
| 3 Public surface | `surfacePaths` | **Required**, empty refused, every entry must be tracked. |
|
|
633
656
|
| 4 Tarball accounting | `accountedTarballFiles` | **Required.** The `files` entries already accounted for. |
|
|
634
657
|
| 5 Source doc comments | `sourceDocComments` | Defaulted OFF. Enable where the build copies doc text verbatim. |
|
|
635
|
-
| 6
|
|
636
|
-
| 7
|
|
637
|
-
| 8 Added
|
|
638
|
-
| 9
|
|
639
|
-
| 10
|
|
658
|
+
| 6 Source string literals | `sourceStringLiterals` | Defaulted OFF. Enable where messages reach a consumer. No comments. |
|
|
659
|
+
| 7 Exit codes | `exitCodes` | Defaulted `{ clean: 0, hits: 1, refuse: 1 }`. `clean` must stay 0. |
|
|
660
|
+
| 8 Added detection | `extraRules` | Defaulted `[]`. Each added rule brings its own samples. |
|
|
661
|
+
| 9 Added self-test material | `extraSamples` | Defaulted `{}`. Additive only. |
|
|
662
|
+
| 10 Repository under test | `repoRoot` | Defaulted to the working directory, anchored at its git top level. |
|
|
663
|
+
| 11 Report destination | `write` | Defaulted to the process streams. |
|
|
640
664
|
|
|
641
665
|
`CONFIG_AXES` is the same table as data, so a consumer can enumerate it rather than transcribe it.
|
|
642
666
|
|
|
667
|
+
The string-literal pass reads double-quoted, single-quoted and template literals (a multi-line
|
|
668
|
+
template whole, a `${...}` as a space), decodes their escapes, and also reads the literals on one
|
|
669
|
+
source line joined, so a phrase split across `"a " + "b"` is seen. It never reads a comment: a `//`
|
|
670
|
+
line, a trailing `//` and a `/* */` block are skipped, so an item identifier in a maintainer's note
|
|
671
|
+
stays allowed. A regular-expression literal is skipped too. In both source passes a NUL byte reads as
|
|
672
|
+
a space instead of refusing the file, since a raw separator inside a literal is legitimate source;
|
|
673
|
+
the clean line counts them.
|
|
674
|
+
|
|
643
675
|
#### Two things worth knowing before you adopt this one
|
|
644
676
|
|
|
645
677
|
**The prefix set has to be extended by hand, and that is the cheaper mistake.** A new programme
|
package/agent-notes.d.ts
CHANGED
|
@@ -2,12 +2,24 @@
|
|
|
2
2
|
* Types for `@cosyte/script-utils/agent-notes`: the checker for a repository's agent notes, one
|
|
3
3
|
* file at `documentation/agent-notes.md` that CLAUDE.md points into as
|
|
4
4
|
* `documentation/agent-notes.md#<anchor>` (RD4).
|
|
5
|
+
*
|
|
6
|
+
* Deprecated (D62): the agent notes were removed from every Cosyte repository. A repository with no
|
|
7
|
+
* notes file passes `runAgentNotesCheck` with one line, and one that still has the file is checked
|
|
8
|
+
* as before. This entry point stays until a minor release removes it.
|
|
5
9
|
*/
|
|
6
10
|
|
|
7
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* The one place the notes live.
|
|
13
|
+
*
|
|
14
|
+
* @deprecated Retired with the agent notes (D62).
|
|
15
|
+
*/
|
|
8
16
|
export declare const NOTES_PATH: "documentation/agent-notes.md";
|
|
9
17
|
|
|
10
|
-
/**
|
|
18
|
+
/**
|
|
19
|
+
* A rule id a finding can carry.
|
|
20
|
+
*
|
|
21
|
+
* @deprecated Retired with the agent notes (D62).
|
|
22
|
+
*/
|
|
11
23
|
export type AgentNotesRule =
|
|
12
24
|
| "layout"
|
|
13
25
|
| "pointer-form"
|
|
@@ -18,19 +30,39 @@ export type AgentNotesRule =
|
|
|
18
30
|
| "empty-section"
|
|
19
31
|
| "stale-citation";
|
|
20
32
|
|
|
21
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Every rule id, with what it requires.
|
|
35
|
+
*
|
|
36
|
+
* @deprecated Retired with the agent notes (D62).
|
|
37
|
+
*/
|
|
22
38
|
export declare const AGENT_NOTES_RULES: Readonly<Record<AgentNotesRule, string>>;
|
|
23
39
|
|
|
24
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* Raised when the check cannot run; the CLI turns it into exit 2.
|
|
42
|
+
*
|
|
43
|
+
* @deprecated Retired with the agent notes (D62).
|
|
44
|
+
*/
|
|
25
45
|
export declare class AgentNotesRefusal extends Error {}
|
|
26
46
|
|
|
27
|
-
/**
|
|
47
|
+
/**
|
|
48
|
+
* GitHub's heading slug, as github-slugger writes it.
|
|
49
|
+
*
|
|
50
|
+
* @deprecated Retired with the agent notes (D62).
|
|
51
|
+
*/
|
|
28
52
|
export declare function slugify(text: string): string;
|
|
29
53
|
|
|
30
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* A slugger that suffixes repeats as GitHub does: the second `Setup` is `setup-1`.
|
|
56
|
+
*
|
|
57
|
+
* @deprecated Retired with the agent notes (D62).
|
|
58
|
+
*/
|
|
31
59
|
export declare function makeSlugger(): (text: string) => string;
|
|
32
60
|
|
|
33
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* One heading of the notes.
|
|
63
|
+
*
|
|
64
|
+
* @deprecated Retired with the agent notes (D62).
|
|
65
|
+
*/
|
|
34
66
|
export interface NotesHeading {
|
|
35
67
|
level: number;
|
|
36
68
|
text: string;
|
|
@@ -46,10 +78,15 @@ export interface NotesHeading {
|
|
|
46
78
|
* its headings.
|
|
47
79
|
*
|
|
48
80
|
* @throws {AgentNotesRefusal} On an HTML comment that never closes.
|
|
81
|
+
* @deprecated Retired with the agent notes (D62).
|
|
49
82
|
*/
|
|
50
83
|
export declare function readNotes(text: string): { targets: Set<string>; headings: NotesHeading[] };
|
|
51
84
|
|
|
52
|
-
/**
|
|
85
|
+
/**
|
|
86
|
+
* One place the notes or a pointer depart from RD4.
|
|
87
|
+
*
|
|
88
|
+
* @deprecated Retired with the agent notes (D62).
|
|
89
|
+
*/
|
|
53
90
|
export interface AgentNotesFinding {
|
|
54
91
|
rule: AgentNotesRule;
|
|
55
92
|
file: string;
|
|
@@ -58,6 +95,11 @@ export interface AgentNotesFinding {
|
|
|
58
95
|
message: string;
|
|
59
96
|
}
|
|
60
97
|
|
|
98
|
+
/**
|
|
99
|
+
* What `checkAgentNotes` reads.
|
|
100
|
+
*
|
|
101
|
+
* @deprecated Retired with the agent notes (D62).
|
|
102
|
+
*/
|
|
61
103
|
export interface AgentNotesInput {
|
|
62
104
|
/** CLAUDE.md's contents, or `null` when the repository has none. */
|
|
63
105
|
claude: string | null;
|
|
@@ -75,9 +117,15 @@ export interface AgentNotesInput {
|
|
|
75
117
|
* Check the notes and every pointer into them.
|
|
76
118
|
*
|
|
77
119
|
* @throws {AgentNotesRefusal} When the notes cannot be read to the end.
|
|
120
|
+
* @deprecated Retired with the agent notes (D62).
|
|
78
121
|
*/
|
|
79
122
|
export declare function checkAgentNotes(input: AgentNotesInput): AgentNotesFinding[];
|
|
80
123
|
|
|
124
|
+
/**
|
|
125
|
+
* What `runAgentNotesCheck` takes.
|
|
126
|
+
*
|
|
127
|
+
* @deprecated Retired with the agent notes (D62).
|
|
128
|
+
*/
|
|
81
129
|
export interface AgentNotesCheckOptions {
|
|
82
130
|
/** The repository root. Default: the working directory. */
|
|
83
131
|
dir?: string;
|
|
@@ -87,6 +135,9 @@ export interface AgentNotesCheckOptions {
|
|
|
87
135
|
|
|
88
136
|
/**
|
|
89
137
|
* Run the checker over one repository's tracked files and return the exit code: 0 clean, 1 at
|
|
90
|
-
* least one finding, 2 the check could not run.
|
|
138
|
+
* least one finding, 2 the check could not run. A repository with no notes file is clean: it gets
|
|
139
|
+
* one line, `agent notes: OK, no agent notes (retired, D62)`. It never exits the process.
|
|
140
|
+
*
|
|
141
|
+
* @deprecated Retired with the agent notes (D62).
|
|
91
142
|
*/
|
|
92
143
|
export declare function runAgentNotesCheck(options?: AgentNotesCheckOptions): 0 | 1 | 2;
|
package/agent-notes.js
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
// @cosyte/script-utils/agent-notes
|
|
2
2
|
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// DEPRECATED (D62, 2026-11-08). The agent notes were removed from every Cosyte repository, and the
|
|
4
|
+
// card spec (documentation/claude-card.md in cosyte/config) has no pointer form any more: a card
|
|
5
|
+
// stands on its own. This checker stays only so that a repository still running it keeps passing
|
|
6
|
+
// while its notes are removed:
|
|
7
|
+
//
|
|
8
|
+
// - with no notes file (not tracked, or deleted from the working tree) it exits 0 with one line,
|
|
9
|
+
// `agent notes: OK, no agent notes (retired, D62)`, whatever still points at the file;
|
|
10
|
+
// - with one, it checks the notes and every pointer into them as it always has (below).
|
|
11
|
+
//
|
|
12
|
+
// Removing it (this file, its declarations, the `./agent-notes` export and the `cosyte-agent-notes`
|
|
13
|
+
// bin) is a breaking change, so it waits for a minor release, which the operator approves (D56).
|
|
14
|
+
//
|
|
15
|
+
// THE AGENT-NOTES CHECKER (RD4, as it stood before D62). A repository's CLAUDE.md card stays short,
|
|
16
|
+
// and what does not fit in it lives in ONE file, `documentation/agent-notes.md`, which the card
|
|
17
|
+
// points into with ONE form: `documentation/agent-notes.md#<anchor>`. This checker replaced the eight
|
|
18
|
+
// per-repository checkers (mllp, ccda, ncpdp, astm, terminology, transform, deid and cli), keeping
|
|
19
|
+
// every rule they shared and resolving the ones they disagreed on.
|
|
8
20
|
//
|
|
9
21
|
// THE RULES, each one id a finding names:
|
|
10
22
|
//
|
|
@@ -40,6 +52,9 @@ import { isCliEntrypoint } from "./index.js";
|
|
|
40
52
|
/** The one place the notes live. */
|
|
41
53
|
export const NOTES_PATH = "documentation/agent-notes.md";
|
|
42
54
|
|
|
55
|
+
/** What a repository with no notes file is told, and passes on (D62). */
|
|
56
|
+
const RETIRED = "no agent notes (retired, D62)";
|
|
57
|
+
|
|
43
58
|
/** The directory layout the one-file rule retires. */
|
|
44
59
|
const NOTES_DIRECTORY = "documentation/agent-notes/";
|
|
45
60
|
|
|
@@ -406,8 +421,8 @@ export function checkAgentNotes(input) {
|
|
|
406
421
|
return findings;
|
|
407
422
|
}
|
|
408
423
|
|
|
409
|
-
/** The tracked
|
|
410
|
-
function
|
|
424
|
+
/** The tracked paths of a repository, each with whether git records it as a regular file. */
|
|
425
|
+
function listTracked(dir) {
|
|
411
426
|
let listing;
|
|
412
427
|
try {
|
|
413
428
|
listing = execFileSync("git", ["ls-files", "-z", "-s"], {
|
|
@@ -419,14 +434,24 @@ function trackedFiles(dir) {
|
|
|
419
434
|
} catch (err) {
|
|
420
435
|
throw new AgentNotesRefusal(`could not list the tracked files of ${dir}: ${err.message}`);
|
|
421
436
|
}
|
|
422
|
-
const
|
|
423
|
-
const skipped = [];
|
|
437
|
+
const entries = [];
|
|
424
438
|
for (const record of listing.split("\0")) {
|
|
425
439
|
if (record === "") continue;
|
|
426
|
-
const tab = record.indexOf("\t");
|
|
427
440
|
const mode = record.slice(0, 6);
|
|
428
|
-
|
|
429
|
-
|
|
441
|
+
entries.push({
|
|
442
|
+
path: record.slice(record.indexOf("\t") + 1),
|
|
443
|
+
regular: mode === "100644" || mode === "100755",
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
return entries;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/** The tracked files of a repository: regular files read, everything else counted. */
|
|
450
|
+
function readTracked(dir, entries) {
|
|
451
|
+
const files = [];
|
|
452
|
+
const skipped = [];
|
|
453
|
+
for (const { path, regular } of entries) {
|
|
454
|
+
if (!regular) {
|
|
430
455
|
skipped.push(path);
|
|
431
456
|
continue;
|
|
432
457
|
}
|
|
@@ -469,10 +494,20 @@ export function runAgentNotesCheck(options = {}) {
|
|
|
469
494
|
}
|
|
470
495
|
dir = resolve(dir);
|
|
471
496
|
try {
|
|
472
|
-
const
|
|
497
|
+
const entries = listTracked(dir);
|
|
498
|
+
// Retired (D62): with no notes file there is nothing to check, so nothing else is read, and a
|
|
499
|
+
// pointer left in the card, a source comment or a changelog is not a finding.
|
|
500
|
+
const hasNotes =
|
|
501
|
+
entries.some((entry) => entry.path === NOTES_PATH && entry.regular) &&
|
|
502
|
+
existsSync(join(dir, NOTES_PATH));
|
|
503
|
+
const { files, skipped } = hasNotes ? readTracked(dir, entries) : { files: [], skipped: [] };
|
|
473
504
|
const byPath = new Map(files.map((file) => [file.path, file.text]));
|
|
474
505
|
const claude = byPath.get("CLAUDE.md") ?? null;
|
|
475
506
|
const notes = byPath.get(NOTES_PATH) ?? null;
|
|
507
|
+
if (notes === null) {
|
|
508
|
+
process.stdout.write(`agent notes: OK, ${RETIRED}\n`);
|
|
509
|
+
return 0;
|
|
510
|
+
}
|
|
476
511
|
const others = files.filter((f) => f.path !== "CLAUDE.md" && f.path !== NOTES_PATH);
|
|
477
512
|
const decisions = join(dir, "documentation", "decisions");
|
|
478
513
|
const findings = checkAgentNotes({
|
|
@@ -495,7 +530,9 @@ export function runAgentNotesCheck(options = {}) {
|
|
|
495
530
|
findings
|
|
496
531
|
.map((f) => ` ${f.file}${f.line > 0 ? `:${f.line}` : ""} [${f.rule}] ${f.message}`)
|
|
497
532
|
.join("\n") +
|
|
498
|
-
|
|
533
|
+
`\nagent notes: the notes are retired (D62). Rather than fix these, delete ${NOTES_PATH}, ` +
|
|
534
|
+
`which passes this check, and move what still binds to where it is used ` +
|
|
535
|
+
`(documentation/claude-card.md in cosyte/config).\n`,
|
|
499
536
|
);
|
|
500
537
|
return 1;
|
|
501
538
|
} catch (err) {
|
|
@@ -1,23 +1,33 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// cosyte-agent-notes: the agent-notes checker for the repository in the working directory.
|
|
3
|
+
// Deprecated (D62): the agent notes were removed from every repository, so it passes a repository
|
|
4
|
+
// with no notes file, and stays only until a minor release removes it.
|
|
3
5
|
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
6
|
// `npx -y -p @cosyte/script-utils cosyte-agent-notes`. It forwards every argument to the gate and exits with
|
|
5
|
-
// the gate's own code; `--help` prints what it does and exits 0.
|
|
7
|
+
// the gate's own code; `--help` prints what it does and exits 0, or 2 under CI.
|
|
6
8
|
|
|
7
9
|
import { runAgentNotesCheck } from "../agent-notes.js";
|
|
10
|
+
import { answerHelp } from "./help.js";
|
|
8
11
|
|
|
9
|
-
const HELP = `cosyte-agent-notes:
|
|
12
|
+
const HELP = `cosyte-agent-notes: deprecated (D62). Remove the step that runs it.
|
|
10
13
|
|
|
11
14
|
cosyte-agent-notes [--dir <repository root>]
|
|
12
15
|
|
|
13
|
-
The notes
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
The agent notes, documentation/agent-notes.md, were removed from every Cosyte repository on
|
|
17
|
+
2026-11-08 (D62). A CLAUDE.md card stands on its own, and what does not fit on it goes where it is
|
|
18
|
+
used: a test, a comment at the code it guards, an ADR, the README or docs, or a runbook
|
|
19
|
+
(documentation/claude-card.md in cosyte/config).
|
|
20
|
+
|
|
21
|
+
Until a minor release removes this bin, a repository with no documentation/agent-notes.md passes
|
|
22
|
+
with one line, "no agent notes (retired, D62)", whatever still points at the file. One that still
|
|
23
|
+
has the file is checked as before: every documentation/agent-notes.md#<anchor> pointer in a tracked
|
|
24
|
+
file must name a heading or an explicit anchor. Exit 0 clean, 1 a finding, 2 the check could not
|
|
25
|
+
run.
|
|
16
26
|
`;
|
|
17
27
|
|
|
18
28
|
const args = process.argv.slice(2);
|
|
19
29
|
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
-
process.
|
|
30
|
+
process.exitCode = answerHelp("cosyte-agent-notes", HELP);
|
|
21
31
|
} else {
|
|
22
32
|
process.exitCode = runAgentNotesCheck({ argv: args });
|
|
23
33
|
}
|
package/bin/cosyte-attw.js
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// cosyte-attw: the attw publish gate for the package in the working directory.
|
|
3
3
|
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
4
|
// `npx -y -p @cosyte/script-utils cosyte-attw`. It forwards every argument to the gate and exits with
|
|
5
|
-
// the gate's own code; `--help` prints what it does and exits 0.
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0, or 2 under CI.
|
|
6
6
|
|
|
7
7
|
import { runAttwCli } from "../attw.js";
|
|
8
|
+
import { answerHelp } from "./help.js";
|
|
8
9
|
|
|
9
10
|
const HELP = `cosyte-attw: run the attw publish gate for the package in the working directory.
|
|
10
11
|
|
|
@@ -18,7 +19,7 @@ passed; attw's own status when attw judged the package; 1 a refusal.
|
|
|
18
19
|
|
|
19
20
|
const args = process.argv.slice(2);
|
|
20
21
|
if (args.includes("--help") || args.includes("-h")) {
|
|
21
|
-
process.
|
|
22
|
+
process.exitCode = answerHelp("cosyte-attw", HELP);
|
|
22
23
|
} else {
|
|
23
24
|
process.exitCode = runAttwCli(args);
|
|
24
25
|
}
|
package/bin/cosyte-catalog.js
CHANGED
|
@@ -2,22 +2,24 @@
|
|
|
2
2
|
// cosyte-catalog: the catalog check for the library in the working directory.
|
|
3
3
|
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
4
|
// `npx -y -p @cosyte/script-utils cosyte-catalog`. It forwards every argument to the gate and exits with
|
|
5
|
-
// the gate's own code; `--help` prints what it does and exits 0.
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0, or 2 under CI.
|
|
6
6
|
|
|
7
7
|
import { runCatalogCheck } from "../catalog.js";
|
|
8
|
+
import { answerHelp } from "./help.js";
|
|
8
9
|
|
|
9
10
|
const HELP = `cosyte-catalog: check that a library's package.json description is the catalog's.
|
|
10
11
|
|
|
11
|
-
cosyte-catalog [--dir <package directory>]
|
|
12
|
+
cosyte-catalog [--dir <package directory>] [--strict]
|
|
12
13
|
|
|
13
|
-
The catalog is the one list of our libraries, generated in cosyte/config
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
The catalog is the one list of our libraries, generated in cosyte/config, which keeps it fresh
|
|
15
|
+
against npm. This check is advisory: exit 0 when the description is the catalog's, and 0 with both
|
|
16
|
+
values printed when it is not or the package is unlisted; 2 when package.json cannot be read. With
|
|
17
|
+
--strict a disagreement exits 1 and an unlisted package 2.
|
|
16
18
|
`;
|
|
17
19
|
|
|
18
20
|
const args = process.argv.slice(2);
|
|
19
21
|
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
-
process.
|
|
22
|
+
process.exitCode = answerHelp("cosyte-catalog", HELP);
|
|
21
23
|
} else {
|
|
22
24
|
process.exitCode = runCatalogCheck({ argv: args });
|
|
23
25
|
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// cosyte-claude-card: the CLAUDE.md card checker for the repository in the working directory.
|
|
3
3
|
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
4
|
// `npx -y -p @cosyte/script-utils cosyte-claude-card`. It forwards every argument to the gate and exits with
|
|
5
|
-
// the gate's own code; `--help` prints what it does and exits 0.
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0, or 2 under CI.
|
|
6
6
|
|
|
7
7
|
import { runClaudeCardGate } from "../claude-card.js";
|
|
8
|
+
import { answerHelp } from "./help.js";
|
|
8
9
|
|
|
9
10
|
const HELP = `cosyte-claude-card: check a repository's CLAUDE.md against the card spec.
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ Gate, Hazard and Rules in order, at most 60 lines of at most 100 characters and
|
|
|
17
18
|
|
|
18
19
|
const args = process.argv.slice(2);
|
|
19
20
|
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
-
process.
|
|
21
|
+
process.exitCode = answerHelp("cosyte-claude-card", HELP);
|
|
21
22
|
} else {
|
|
22
23
|
process.exitCode = runClaudeCardGate({ argv: args });
|
|
23
24
|
}
|
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// cosyte-internal-refs: the internal-reference gate over the repository in the working directory.
|
|
3
3
|
// A bin of @cosyte/script-utils (D56), so a repository with no package.json can run it with
|
|
4
4
|
// `npx -y -p @cosyte/script-utils cosyte-internal-refs`. It forwards every argument to the gate and exits with
|
|
5
|
-
// the gate's own code; `--help` prints what it does and exits 0.
|
|
5
|
+
// the gate's own code; `--help` prints what it does and exits 0, or 2 under CI.
|
|
6
6
|
|
|
7
7
|
import { runInternalRefsCli } from "../internal-refs.js";
|
|
8
|
+
import { answerHelp } from "./help.js";
|
|
8
9
|
|
|
9
10
|
const HELP = `cosyte-internal-refs: run the internal-reference gate over the repository in the working directory.
|
|
10
11
|
|
|
@@ -17,7 +18,7 @@ clean, 1 hits, 2 the gate could not run.
|
|
|
17
18
|
|
|
18
19
|
const args = process.argv.slice(2);
|
|
19
20
|
if (args.includes("--help") || args.includes("-h")) {
|
|
20
|
-
process.
|
|
21
|
+
process.exitCode = answerHelp("cosyte-internal-refs", HELP);
|
|
21
22
|
} else {
|
|
22
23
|
process.exitCode = runInternalRefsCli(args);
|
|
23
24
|
}
|