diff --git a/.changeset/brave-pumas-stream.md b/.changeset/brave-pumas-stream.md deleted file mode 100644 index 249f800..0000000 --- a/.changeset/brave-pumas-stream.md +++ /dev/null @@ -1,20 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Give `cosyte parse` a documented input-size limit and incremental multi-record output. - -An input past 67108864 bytes (64 MiB) is now refused with a value-free `CLI_INPUT_TOO_LARGE` -diagnostic naming the limit and the data-error exit code (`65`), never the internal-error code a -platform allocation failure used to produce. The check runs against the running byte count as the -input arrives, so the refusal lands before anything allocates memory proportional to the oversized -input. The limit is rendered from one constant into `cosyte --help` and the command reference, and a -test reds if those two ever disagree. - -Multi-record output (`--ndjson` and MLLP frames) is emitted record by record as each record is -parsed, rather than accumulated and written once at the end. Per-record isolation and the exit-code -contract are unchanged. A fatal condition part way through keeps the lines already written and still -resolves to that failure's own non-zero exit code, so a partial record stream is never presented as a -complete one; a truncated MLLP stream is the visible case, where the frames that completed are now -emitted before the truncation is detected. A downstream consumer that closes the pipe is a value-free -`CLI_OUTPUT_WRITE_FAILED` rather than an unhandled write error. diff --git a/.changeset/chilled-moons-refuse.md b/.changeset/chilled-moons-refuse.md deleted file mode 100644 index 51c8fba..0000000 --- a/.changeset/chilled-moons-refuse.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Make the outside-the-repo tarball install a step the release pipeline runs, instead of a checklist -step a human has to remember. `.github/workflows/release.yml` gains an `install-gate` job that the -publishing job `needs:`, so a red gate stops the publish: it packs this tree, installs the packed -tarball from a directory outside the repository working tree, executes both declared bins from that -installed copy, and refuses a dependency specifier naming a local path in a field a consumer's -install resolves. It fails closed, with a distinct reason for an install that exited non-zero, a bin -missing from the package, a bin that ran and failed, and a run that reached no verdict at all. diff --git a/.changeset/great-pandas-hammer.md b/.changeset/great-pandas-hammer.md deleted file mode 100644 index 3808505..0000000 --- a/.changeset/great-pandas-hammer.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Bring the dependency overrides, the install hardening and the PHI scanner's completeness rule up to -the shared package baseline, and cut the always-read agent guide to its declared line ceiling without -losing a rule. - -- **The `js-yaml` override now covers the advisory's extended range.** The pin moves from `4.2.0` on - `>=4.0.0 <4.2.0` to `4.3.0` on `>=4.0.0 <4.3.0`, so a transitive resolution inside the newly - covered window is remediated rather than silently admitted. The `esbuild` override is untouched, - and the lockfile records the new pin. -- **`pnpm-workspace.yaml` declares the install hardening this package relies on**: a - `minimumReleaseAge` of 1440 minutes, the cooling-off window that blunts a compromised release, and - a `trustPolicy` of `no-downgrade`. **The `packageManager` pin moves to a pnpm release that honours - both keys**, because the previous pin predates them and would have ignored the file entirely, which - is a settings file that decorates rather than defends. -- **The PHI scanner refuses a run that enumerated a target and never read it.** A logged - `--allow-fixture` bypass withdraws a file after the scan has already named it, and the run used to - report on whatever was left as though the corpus were whole: over a corpus whose only violator was - withdrawn, that reads as clean. It now reports every hit FIRST, so a refusal can never discard a - finding, then refuses by name with a status distinct from both the clean status and the hits status. - The question is answered as a set difference and never as a count, because a count counts the - targets that did get read, so the refusal can name the paths that went unread. An honest run, with - nothing withdrawn, keeps exactly the exit codes it had, and so do the pre-commit hook and the - whole-tree sweep. -- **The always-read agent guide is inside its line budget, and nothing it said was lost.** Every rule - and every trap stays there as a one-line imperative with a resolving pointer, and the sentences that - explained each one moved into the narrative document verbatim, where the two-file contract gate - checks on every run that each pointer still lands on a section with a body. diff --git a/.changeset/lucky-donkeys-smile.md b/.changeset/lucky-donkeys-smile.md deleted file mode 100644 index 2e2eedf..0000000 --- a/.changeset/lucky-donkeys-smile.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Gate the two-file agent-guidance contract with `pnpm check:agent-notes`, run from the test suite so -it rides the required CI contexts and `prepublishOnly`. The narrative file must be tracked, no -section may be emptied down to its heading, and every pointer at it must resolve. The matcher was -derived by counting both pointer spellings on this tree rather than ported from a sibling, a bare-form -census refuses if the spelling this scope rests on ever changes, and the gate refuses at exit 2 -rather than reporting green over a corpus it never opened. diff --git a/.changeset/olive-cameras-shave.md b/.changeset/olive-cameras-shave.md deleted file mode 100644 index 8c6ccfd..0000000 --- a/.changeset/olive-cameras-shave.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Publish an output schema for every MCP tool, and make every tool result conform to it. - -An agent calling a cosyte tool previously received `structuredContent: { exit, ok }` with no schema to -check it against, and a text content block holding the command's stdout, which was a different value -from the structured one. Deciding whether a result held data or a diagnostic meant pattern-matching a -text blob. - -Each of the four tools now advertises an `outputSchema` (and a `title`) on `tools/list`, and every -dispatch path returns structured content conforming to the schema its own tool declared. The result -carries `ok`, a `status` of `success` / `verdict` / `failed`, the `exit` code from the documented -exit-code contract, a stable `code` on a failed call, and the tool's own payload under `data`: the -parsed model and warnings, the validation verdict and findings, the structural summary, or the -converted Bundle. `status` is the property that separates a negative verdict about the message (the -tool ran; the payload is present) from a call that produced nothing, which no text blob could -distinguish reliably. - -The text content block is now the serialized JSON of that same structured result, so a client that -reads only text sees exactly the value a schema-aware client validates. - -On a failed call the structured result is value-free by construction: every property is drawn from a -fixed set (the outcome vocabulary, the exit-code contract, the diagnostic-code registry), so no part -of the caller's input can appear in it. The tool name of an unknown tool is no longer echoed back, for -the same reason. Tool names, tool count, input schemas, the exit-code contract and the rule that a -parsed-but-invalid `validate` is a successful call are all unchanged, and no dependency was added. diff --git a/.changeset/quiet-pipes-close.md b/.changeset/quiet-pipes-close.md deleted file mode 100644 index 835e80f..0000000 --- a/.changeset/quiet-pipes-close.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Terminate cleanly when a downstream consumer closes the CLI's stdout, and publish the code that -says so. `cosyte parse big.ndjson | head -3`, a pager you quit, or any early-exiting reader now ends -the run quietly under a new documented exit code, `74` (`IOERR`, the value `sysexits.h` assigns -`EX_IOERR`), with the stable value-free `CLI_OUTPUT_WRITE_FAILED` diagnostic on stderr. - -The contract is additive: no existing code is renamed, repurposed or removed, and no existing code's -meaning changes. `74` was chosen over the internal-error code on a measurement rather than a guess: -a write to a closed stdout is a Node `EPIPE` on the stream, a handled condition the consumer owns, -and reporting it as `70` told an operator their pipeline had hit a bug in the CLI. The one condition -that moves onto the new code is the closed-consumer write failure itself, which previously reported -`70` on the record-stream path. - -- **Neither write path reports a success over output that reached nobody.** The whole-result write - in the `cosyte` bin bypassed the guarded output sink altogether, and the record stream's sink only - consulted a flag the platform sets asynchronously, so a short enough run could have every line - enqueued before the closed consumer was reported: `cosyte parse msg.hl7 | head -1` and - `cosyte parse bulk.ndjson --ndjson | head -1` could each print a summary and exit `0` over output - that was never delivered. Every chunk bound for stdout now waits for the platform's - acknowledgement before the run may resolve, so an undelivered result resolves to `74` whatever the - command had computed, and the summary describing that computed outcome is withdrawn with it. -- **`cosyte-mcp` terminates quietly when its client goes away.** A closed stdout used to reach the - process as an unhandled stream error, printing a Node stack trace that named this machine's - install paths. It is now a value-free diagnostic and the same exit code, and the server ends - rather than serving a channel nobody is reading. -- **A closed stderr cannot turn either termination into an unhandled error.** Both bins hear the - diagnostic channel's own error and fall back to the exit code as the whole signal. -- **The value-free posture is unchanged and now tested against real processes.** No diagnostic on - this path carries a byte of the input, an `EPIPE` string, a Node error name or a stack frame. diff --git a/.changeset/soft-cameras-repeat.md b/.changeset/soft-cameras-repeat.md deleted file mode 100644 index b18fc3d..0000000 --- a/.changeset/soft-cameras-repeat.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -`cosyte redact` (alias `deid`) now produces a real de-identified copy, delegated whole to -`@cosyte/deid`. - -The command that was an honest refusal is wired. For `ccda`, `fhir`, `hl7` and `x12`, stdout carries -the de-identified document, serialized exactly as `cosyte fmt` serializes that format, and stderr -carries the library's own value-free manifest: one line per locus it acted on, with the category, the -transform, the structural path, the count, the disposition and its stable code, plus the library's -own published output label and version. The CLI adds no policy, no locus map, no transform and no -fallback scrub, and it asserts no de-identification standard of its own. - -Everything else is a typed refusal that emits nothing at all, because a partial pass offered as a -de-identified copy is the hazard this command exists to prevent. `astm`, `mllp` and `ncpdp` have no -adapter in that library, so they are `CLI_NOT_IMPLEMENTED` (exit `69`). `dicom` is covered there, but -its de-identified form is a Part 10 byte stream and this CLI's data channel is text, so it is -`CLI_FORMAT_UNSUPPORTED` (exit `65`), the CLI's own limit rather than the library's. If the library -reports any locus it could not handle, the run is a new `CLI_DEID_INCOMPLETE` diagnostic and exit `1` -with empty stdout and every blocked path named on stderr. - -`@cosyte/deid` is declared as an `optionalDependency`: an install without it degrades to a value-free -`CLI_PARSER_UNAVAILABLE` and exit `69`, decided before the input is read, so a copy that cannot -de-identify never touches the bytes it cannot strip. No other command loads the library. - -Identifier surrogates (MRN, account and member numbers) are keyed with a per-invocation ephemeral -key. The CLI holds no key material and offers no key surface, so surrogates are consistent within one -output and deliberately not stable across runs; that is stated on the diagnostic channel and in the -documentation rather than left to be discovered. `redact` also does not honour -`--unsafe-show-values`: an excerpt of the input you asked to have stripped is exactly the leak the -command exists to prevent. - -No published exit value moved, and no existing diagnostic code was renamed. diff --git a/.changeset/tidy-otters-repeat.md b/.changeset/tidy-otters-repeat.md deleted file mode 100644 index 5338a89..0000000 --- a/.changeset/tidy-otters-repeat.md +++ /dev/null @@ -1,19 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Correct the `sysexits.h` attribution in the exit-code contract. - -Exit `2` was documented as `EX_USAGE`, but `sysexits.h` assigns `EX_USAGE` the value `64`. The label -sat in two places in `src/core/exit-codes.ts` (the module's contract table and the `USAGE` member's -JSDoc), so it compiled into the published type declarations and rendered on a consumer's hover; -`README.md` and the concepts page grounded their whole table in that header while listing `1` and -`2`, which it does not define. Exit `2` now carries no sysexits constant, and `1` and `2` are both -stated as this CLI's own values. The four labels that were already correct (`EX_DATAERR` 65, -`EX_NOINPUT` 66, `EX_UNAVAILABLE` 69, `EX_SOFTWARE` 70) are unchanged. - -No published exit value moved, and no member of the exported `EXIT` map was added, removed or -renamed. Every command still exits with the status it exited with before, and a consumer who -hard-coded `EX_USAGE = 2` against this CLI keeps working. A new test reads every source file, the -README and every published docs page and reds if a sysexits constant is ever again attached to a -number that header does not assign it. diff --git a/.changeset/tidy-runtimes-narrow.md b/.changeset/tidy-runtimes-narrow.md deleted file mode 100644 index e83b19d..0000000 --- a/.changeset/tidy-runtimes-narrow.md +++ /dev/null @@ -1,23 +0,0 @@ ---- -"@cosyte/cli": patch ---- - -Declare a Node range that matches the release lines this package is actually tested on. `engines.node` -narrows from `>=22.0.0` to `>=22.0.0 <26.0.0`, so the manifest no longer claims support for a Node -line nobody here has run. - -Node 26 is supported upstream and sat inside the old open-ended range, while the test matrix exercises -22 and 24 only. Anyone installing on Node 26 was therefore running an untested combination, and would -have learned that from a crash rather than from the manifest. The claim narrows to the tested set -rather than the tested set being widened to the claim: each matrix cell is a required status check -whose name is fixed outside this repository, so adding a line is a change that has to start there. - -- **The tested set is now stated in this repository.** The CI caller passes the Node majors - explicitly instead of inheriting them from the shared pipeline's input default, so the set is - readable and diffable here, with no change to which versions run or to any status-check name. -- **A guard fails the build when the two drift apart again.** It derives the Node lines upstream - still supports from a release schedule committed beside it, at the run date, and reports any - supported line the declared range admits but the matrix does not exercise, any matrix cell the - range refuses, and any prose surface restating either one differently. -- **The prerequisite in the installation guide states the upper bound too**, so the published - documentation and the published manifest cannot disagree about which runtimes are supported. diff --git a/docs-content/installation.md b/docs-content/installation.md index 3316fa3..d7c1997 100644 --- a/docs-content/installation.md +++ b/docs-content/installation.md @@ -104,7 +104,7 @@ autodetector, the exit-code contract, and the value-free diagnostic types: ```ts runnable import { VERSION } from "@cosyte/cli"; -VERSION; // => "0.0.6" +VERSION; // => "0.0.7" ``` If that resolves and prints the release you installed, the install is good: head to the diff --git a/package.json b/package.json index 579e141..1d6f864 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@cosyte/cli", - "version": "0.0.6", + "version": "0.0.7", "description": "The cosyte CLI: a PHI-safe developer front door over the @cosyte/* healthcare parsers. Parse HL7 v2 / FHIR from a file or stdin to typed JSON, with a documented exit-code contract.", "keywords": [ "cli", diff --git a/src/core/version.ts b/src/core/version.ts index e83e38c..5d71dc7 100644 --- a/src/core/version.ts +++ b/src/core/version.ts @@ -23,4 +23,4 @@ * console.log(`cosyte ${VERSION}`); * ``` */ -export const VERSION: string = "0.0.6"; +export const VERSION: string = "0.0.7";