@cosyte/cli 0.0.1 → 0.0.3
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 +189 -2
- package/README.md +85 -18
- package/dist/bin/cosyte-mcp.cjs +21 -14
- package/dist/bin/cosyte-mcp.cjs.map +1 -1
- package/dist/bin/cosyte-mcp.mjs +21 -14
- package/dist/bin/cosyte-mcp.mjs.map +1 -1
- package/dist/bin/cosyte.cjs +27 -19
- package/dist/bin/cosyte.cjs.map +1 -1
- package/dist/bin/cosyte.mjs +27 -19
- package/dist/bin/cosyte.mjs.map +1 -1
- package/dist/index.cjs +25 -17
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -17
- package/dist/index.d.ts +51 -17
- package/dist/index.mjs +25 -18
- package/dist/index.mjs.map +1 -1
- package/dist/mcp.cjs +21 -14
- package/dist/mcp.cjs.map +1 -1
- package/dist/mcp.d.cts +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.mjs +21 -14
- package/dist/mcp.mjs.map +1 -1
- package/package.json +13 -13
package/CHANGELOG.md
CHANGED
|
@@ -9,11 +9,198 @@ this file is maintained by hand (Changesets handles the version bump and publish
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **`@cosyte/cli` can be installed from npm again (CLI-UNINSTALLABLE-MANIFEST).** `0.0.1` and `0.0.2`
|
|
15
|
+
both published with all ten `@cosyte/*` sibling packages declared as `file:vendor/*.tgz` local
|
|
16
|
+
paths. `vendor/` is not in `files` and there is no `bundledDependencies`, so the tarball shipped
|
|
17
|
+
none of them and every install route died on the first: `ENOENT ...
|
|
18
|
+
node_modules/@cosyte/cli/vendor/cosyte-fhir-0.0.0.tgz`. The siblings are now real registry ranges:
|
|
19
|
+
`@cosyte/hl7` (`^0.0.7`) and `@cosyte/terminology` (`^0.0.9`) as hard `dependencies`, and the six
|
|
20
|
+
breadth parsers plus `@cosyte/transform` (`^0.0.4`) as `optionalDependencies`. Verified the way a
|
|
21
|
+
dry-run cannot: the packed tarball was installed in a clean directory outside the repo (exit `0`),
|
|
22
|
+
then both bins were run and the `.` subpath imported under ESM and CJS.
|
|
23
|
+
|
|
24
|
+
- **`VERSION` and `cosyte --version` now report the release you are running (CLI-VERSION-DRIFT).**
|
|
25
|
+
`src/core/version.ts` exported `"0.0.0"` while `package.json` said `0.0.2`, and the constant's own
|
|
26
|
+
doc comment claimed it was "synced with `package.json#version` on release by the Changesets
|
|
27
|
+
`version` script" when no such step existed. Confirmed in the published tarball: `@cosyte/cli@0.0.2`
|
|
28
|
+
ships `VERSION = "0.0.0"` in `dist/index.mjs` and `dist/index.cjs`. It reached two user-visible
|
|
29
|
+
surfaces, `cosyte --version` and the MCP server's advertised `serverInfo.version`. `scripts/sync-version.mjs`
|
|
30
|
+
(ported from `@cosyte/transform`) now runs inside the `version` script, and `test/sanity.test.ts`
|
|
31
|
+
compares the export against `package.json` so a skipped sync goes red rather than shipping.
|
|
32
|
+
**The two assertions that let five bad releases through the sibling packages are fixed here, not
|
|
33
|
+
just the value:** `docs-content/installation.md` asserted `typeof VERSION` (true of every wrong
|
|
34
|
+
value) and now asserts the exact version, which the sync script keeps in step; and the declaration's
|
|
35
|
+
`: string` annotation, which the sync script's pattern keys on, is pinned by its own test, so
|
|
36
|
+
dropping it fails at `pnpm test` instead of silently at release time.
|
|
37
|
+
|
|
38
|
+
- **The `attw` publish gate no longer exits 0 on an untyped pack (ATTW-FALSE-GREEN-PORT).** The
|
|
39
|
+
`attw` script was the bare CLI (`attw --pack . --profile node16`), and
|
|
40
|
+
`@arethetypeswrong/cli@0.18.4`'s `getExitCode.js` opens with `if (!analysis.types) return 0`,
|
|
41
|
+
returning before the problem list is read. So a tarball carrying no declarations at all printed
|
|
42
|
+
"This package does not contain types." and **exited 0**, and `verify.sh` propagated that 0 as a
|
|
43
|
+
pass. Reproduced on this package with zero concurrency and the real `--profile node16`
|
|
44
|
+
invocation, twice: with `dist/` removed, and with all ten of the build's declaration files
|
|
45
|
+
deleted and its JS left in place. The second is the realistic window, because `tsup` writes JS in
|
|
46
|
+
one pass and declarations in a later one; instrumented on one build here at a 10ms poll, all
|
|
47
|
+
eight `.mjs`/`.cjs` files appeared on a single poll at 4.92s and all ten declarations on a single
|
|
48
|
+
later poll at 11.82s, a 6.90s interval. That interval moves with load (7.85s on a busier box);
|
|
49
|
+
the ordering, and the ten declarations landing together, do not. Not answered with a lock or a
|
|
50
|
+
build queue: the gate is now able to report that its own inputs were missing, whatever removed
|
|
51
|
+
them.
|
|
52
|
+
- `scripts/attw.mjs` (ported from the fix shipped in `@cosyte/terminology`) runs two nets around
|
|
53
|
+
the real binary. A **preflight** that every relative path `package.json` promises exists and is
|
|
54
|
+
non-empty, naming the missing file; and a **post-check** that promotes the untyped sentence to a
|
|
55
|
+
failure, which catches declarations that are on disk but excluded from the tarball. Every
|
|
56
|
+
argument this repo passes is forwarded, so `--profile node16` keeps its exact meaning: measured
|
|
57
|
+
here, without it `@cosyte/cli/mcp` fails `node10` resolution and `attw` exits 1.
|
|
58
|
+
- **The preflight also walks `bin`, which the ported original did not**, because this is a `bin`
|
|
59
|
+
package and the sibling it came from ships no executable. Measured: with `dist/bin/cosyte.mjs`
|
|
60
|
+
deleted and everything else built, `attw --pack . --profile node16` printed every subpath green
|
|
61
|
+
and exited 0 over a tarball with no `cosyte` command in it.
|
|
62
|
+
- **`--quiet`, `-q`, `--format`, `-f` and `--config-path` are refused by option name and not by
|
|
63
|
+
value**, as is a `.attw.json` setting `quiet` or `format` (`readConfig()` applies it after
|
|
64
|
+
argv). Each was measured here to remove the untyped sentence from the output while still
|
|
65
|
+
exiting 0. `--config-path` is refused on a measurement rather than the inference the original
|
|
66
|
+
recorded. **Two limits are disclosed rather than closed**, both measured: the match is on an
|
|
67
|
+
exact argv token, so commander's attached and clustered short forms `-fjson` and `-Pf json` get
|
|
68
|
+
through (`-qP` does not; the wrapper treats an empty transcript as a failure), and a declared
|
|
69
|
+
path not starting with `.` is skipped by the preflight. The invocation this replaced exited 0
|
|
70
|
+
on the same pack with no arguments at all.
|
|
71
|
+
- **A correction to the ported message.** `files: ["dist"]` packs all ten declarations `tsup`
|
|
72
|
+
emits while `package.json` names only four; the shared `dist/io-<hash>.d.*` **and the four
|
|
73
|
+
`dist/bin/*.d.*`** ride along unnamed, and `analysis.types` is true if the tarball carries any
|
|
74
|
+
declaration. Measured on this tree: losing the four declared declarations gives "No types"
|
|
75
|
+
problems and **exit 1** while any of the other six survives, and only losing all ten gives the
|
|
76
|
+
untyped sentence and **exit 0**. So a partial declaration loss is caught by `attw` itself and
|
|
77
|
+
only a total one is the false green. The preflight reports both outcomes instead of asserting
|
|
78
|
+
the exit 0, which the inherited wording would have done falsely.
|
|
79
|
+
- `test/scripts/attw-gate.test.ts` pins all of it against the real binary, including attw's own
|
|
80
|
+
exit 0, a negative control on a well-formed package, that a genuine attw failure still fails
|
|
81
|
+
with attw's own status, and that the profile flag survives the wrapper in both directions.
|
|
82
|
+
- `scripts/verify.sh` in the meta-repo is unchanged; its propagation was never at fault.
|
|
83
|
+
|
|
84
|
+
- **The public surface now states that `0.0.1` is published and uninstallable (ASSETS-P8).** `0.0.1`
|
|
85
|
+
published on 2026-07-29 with all ten `file:vendor/*.tgz` dependency specifiers intact. `vendor/` is
|
|
86
|
+
not in `files` and there is no `bundledDependencies`, so every install route (`npm i`, `npm i -g`,
|
|
87
|
+
`npx`) fails with `ENOENT` on `node_modules/@cosyte/cli/vendor/cosyte-fhir-0.0.0.tgz`. Reproduced
|
|
88
|
+
in a clean directory. A published version is immutable (ADR 0001), so `0.0.1` stays broken and the
|
|
89
|
+
fix must ship as a later version.
|
|
90
|
+
- `README.md` gains a **"Known issue"** section stating what fails, the exact error, why, and that
|
|
91
|
+
a source checkout is the only workaround. `docs-content/installation.md` gains the same under
|
|
92
|
+
**"Installing fails today"**. Both copies of the broken `npx`-based MCP registration snippet are
|
|
93
|
+
annotated (`README.md` and `docs-content/mcp.md`).
|
|
94
|
+
- **Four false claims corrected.** (1) `README.md` said "not yet published to npm"; it is
|
|
95
|
+
published. (2) It described swapping the vendored sibling deps for real `@cosyte/*` npm ranges as
|
|
96
|
+
a step still to come "at that flip"; the flip already happened without the swap, which is the
|
|
97
|
+
defect. (3) The same "not yet published" claim appeared in **three** `docs-content/` pages. (4)
|
|
98
|
+
The `redact`/`deid` terminal diagnostic, the `--help` text, three JSDoc blocks that compile into
|
|
99
|
+
`dist/*.d.ts`, and **five** docs pages said `@cosyte/deid` was unpublished, unshipped, or unbuilt;
|
|
100
|
+
it is published at `0.0.2`, and the accurate statement is that the CLI does not wire it yet. Every
|
|
101
|
+
count here was re-derived by census of the base tree, not estimated.
|
|
102
|
+
- **The "all eight formats" claim was checked and is correct**: `CosyteFormat` and `OP_SUPPORT` both
|
|
103
|
+
enumerate exactly eight.
|
|
104
|
+
- `RELEASING.md` records that the documented dependency-swap step was skipped, that a green
|
|
105
|
+
`npm publish --dry-run` cannot catch this (it packs a tarball but never resolves its deps from a
|
|
106
|
+
registry), and adds a checklist step to install the published version from outside the repo.
|
|
107
|
+
- **The route to an installable release, recorded in `RELEASING.md` and verified against the
|
|
108
|
+
registry.** `@cosyte/hl7` (`0.0.3`), `@cosyte/terminology` (`0.0.4`) and all six breadth parsers
|
|
109
|
+
would swap to real ranges today. `@cosyte/fhir` is unpublished (`FHIR-NPM-NAME`, an npm E403
|
|
110
|
+
name-similarity rejection) and `@cosyte/transform@0.0.2` fails `E404` on its `@cosyte/fhir` peer,
|
|
111
|
+
so neither can. An installable release is nonetheless reachable before that unblocks, because npm
|
|
112
|
+
tolerates an `optionalDependency` that fails to resolve (measured). It needs a code change first:
|
|
113
|
+
`@cosyte/fhir` and `@cosyte/transform` are imported with a raw `await import()`, so they must be
|
|
114
|
+
routed through a guarded loader to degrade to `CLI_PARSER_UNAVAILABLE` (exit `69`) rather than
|
|
115
|
+
crash. `loadOptional()` cannot be reused unchanged: it takes a `CosyteFormat`, and `"transform"` is
|
|
116
|
+
not one, and its diagnostic hardcodes the word "parser". Not undertaken here.
|
|
117
|
+
- **Superseded within this same unreleased set**, by the two entries at the top of this section:
|
|
118
|
+
the swap and the loader change were both carried out. Two claims that entry made did not survive
|
|
119
|
+
contact: the "name similarity" reading of the `@cosyte/fhir` `E403` was **retracted across the
|
|
120
|
+
ecosystem on 2026-08-03** (the cause is unexplained; do not assert one), and "npm tolerates an
|
|
121
|
+
`optionalDependency` that fails to resolve" is true only in isolation. Declaring **both**
|
|
122
|
+
`@cosyte/fhir` and `@cosyte/transform` optional fails the install outright with `ERESOLVE`, which
|
|
123
|
+
is why `@cosyte/fhir` ended up not declared at all rather than declared optional.
|
|
124
|
+
|
|
125
|
+
### Changed
|
|
126
|
+
|
|
127
|
+
- **FHIR support is unavailable in an npm-installed copy, and now says so instead of crashing.**
|
|
128
|
+
`@cosyte/fhir` is not on the npm registry, so it cannot be declared as a dependency at all;
|
|
129
|
+
measured, declaring it alongside `@cosyte/transform` (which requires it) fails the whole install
|
|
130
|
+
with `ERESOLVE`. `@cosyte/transform` is therefore skipped by npm as an unresolvable optional
|
|
131
|
+
dependency. Both were previously loaded with a bare `await import()`, which in an installed copy
|
|
132
|
+
would have surfaced a raw resolver error and a stack frame, so FHIR `parse`/`inspect`/`fmt`/`validate`
|
|
133
|
+
and `convert` now degrade to a value-free `CLI_PARSER_UNAVAILABLE` (exit `69`) with a diagnostic that
|
|
134
|
+
says the package is not on the registry rather than "install it". **Neither diagnostic says "install
|
|
135
|
+
it", and that is deliberate**: `npm install @cosyte/transform` fails `E404` on its own
|
|
136
|
+
`@cosyte/fhir` peer, so `loadOptional()`'s stock wording ("install it to use this format, it is an
|
|
137
|
+
optional dependency") would point a user at a command that cannot succeed. New
|
|
138
|
+
`loadOptionalPackage(detail, load)` under `loadOptional`, exported on the `.` subpath.
|
|
139
|
+
`@cosyte/fhir` is retained as a `devDependency` on the vendored tarball so this repo's own FHIR and
|
|
140
|
+
`convert` tests still run; note that `devDependencies` **are** published, so that one
|
|
141
|
+
`file:vendor/*.tgz` specifier does remain in the manifest, harmlessly, because a consumer never
|
|
142
|
+
installs a dependency's `devDependencies` (verified: the install exits `0`). HL7 v2, `map-codes` and
|
|
143
|
+
the six breadth formats are unaffected and work from a plain install.
|
|
144
|
+
|
|
145
|
+
- **Documented a pre-existing defect that this release makes reachable for the first time:
|
|
146
|
+
`--omit=optional` produces an install in which the `cosyte` command does not run at all.** The
|
|
147
|
+
install exits `0`, then every invocation, `--version` included, fails with `ERR_MODULE_NOT_FOUND` on
|
|
148
|
+
`@modelcontextprotocol/sdk` and a raw stack trace, because the built `dist/bin/cosyte.mjs` imports
|
|
149
|
+
the SDK statically at the top level rather than only on the `mcp` path. Verified identical on the
|
|
150
|
+
base commit, so it is not introduced here; it simply could not be hit before, because the package
|
|
151
|
+
could not be installed at all. `docs-content/` now says not to use that flag instead of implying it
|
|
152
|
+
is a supported way to slim the install. **The code defect is not fixed here** and needs its own
|
|
153
|
+
change: it also falsifies the "a plain `cosyte parse` never pulls it" claim in `src/bin/cosyte.ts`.
|
|
154
|
+
|
|
155
|
+
- **The docs no longer tell you to run `npx @cosyte/cli …`, which never worked.** Separate from the
|
|
156
|
+
packaging defect and not fixed by it: `npx` runs the executable whose name matches the package
|
|
157
|
+
name's last segment, which would be `cli`, and this package ships `cosyte` and `cosyte-mcp`, so the
|
|
158
|
+
short form fails with `could not determine executable to run`. Reproduced on the published `0.0.2`
|
|
159
|
+
and on the fixed tarball, whose `bin` block is byte-identical. `README.md`, `docs-content/installation.md`
|
|
160
|
+
and both MCP registration snippets now use `npx --package @cosyte/cli cosyte …` /
|
|
161
|
+
`npx -y --package @cosyte/cli cosyte-mcp`, each measured working. A `cli` bin alias would fix the
|
|
162
|
+
short form and is deliberately not added, because `npm install -g` would then claim a command named
|
|
163
|
+
`cli` on the user's `PATH`.
|
|
14
164
|
|
|
15
165
|
### Added
|
|
16
166
|
|
|
167
|
+
- **`README.md` now opens with the shared Cosyte lockup in a `<picture>` block, above the H1.** The
|
|
168
|
+
dark-ground tile (`cosyte-lockup-tile-on-dark-1200x300.png`) sits behind a
|
|
169
|
+
`prefers-color-scheme: dark` media query and the light-ground tile
|
|
170
|
+
(`cosyte-lockup-tile-on-light-1200x300.png`) is the inner `<img>` fallback, so the mark is read on
|
|
171
|
+
a ground that matches the page it is read on. The `# @cosyte/cli` heading and the blockquote under
|
|
172
|
+
it are unchanged: the artwork reads "Cosyte" and the heading names the package, so nothing on the
|
|
173
|
+
page is duplicated. Both URLs were re-checked immediately before the push and returned
|
|
174
|
+
`200 image/png` (10513 bytes dark, 10455 light). The block was copied byte for byte out of
|
|
175
|
+
`@cosyte/hl7`'s `README.md` rather than retyped and diffed against it, because a transcription
|
|
176
|
+
error in one of these URLs is a broken image on a public package page.
|
|
177
|
+
|
|
178
|
+
**This replaces the per-package banner added earlier in the same unreleased window, and the reason
|
|
179
|
+
it replaces it is recorded rather than dropped.** That banner was a plain markdown image, chosen
|
|
180
|
+
over `<img>` or `<picture>` on the stated ground that whether npm's markdown sanitizer preserves a
|
|
181
|
+
`<picture>` element was unverified. That was an accurate account of what was known when it was
|
|
182
|
+
written, and it has since been measured: **GitHub honours the `prefers-color-scheme` switch**
|
|
183
|
+
(observed on `@cosyte/astm` in dark mode, where the rendered image's `currentSrc` resolves to the
|
|
184
|
+
on-dark tile and its parent element is `PICTURE`), and **on the npm package page the `<img>` is
|
|
185
|
+
hoisted out of its `<picture>` by the anchor wrapper** rather than the element being stripped, so
|
|
186
|
+
the light cut renders, which is the correct one there because npmjs.com has no dark mode. Those two
|
|
187
|
+
measurements were taken on `astm` and reported into this repo rather than re-taken here; what was
|
|
188
|
+
re-checked directly for `cli` is that both tile URLs return `200 image/png`. The failure mode is
|
|
189
|
+
safe either way: a renderer that strips `<source>` renders the inner `<img>`, so the worst case is
|
|
190
|
+
a light-ground mark on a dark page, never a missing or broken image.
|
|
191
|
+
|
|
192
|
+
**Why this is corrected rather than annotated in place.** `0.0.1` published on 2026-07-29, a day
|
|
193
|
+
before the banner landed, so no `@cosyte/cli` tarball has ever carried the banner or the sentence
|
|
194
|
+
announcing it. Annotating an entry no consumer received would publish an addition and its
|
|
195
|
+
replacement as two changes when only one was ever visible, so the banner entry is removed from this
|
|
196
|
+
release and its reasoning is carried here instead of being silently reversed.
|
|
197
|
+
|
|
198
|
+
The alt text describes the artwork, a plus mark set in two overlapping rounded squares beside the
|
|
199
|
+
Cosyte wordmark, rather than the package. It is what a screen reader on the npm page reads out and
|
|
200
|
+
what a reader gets when the image fails to load, so repeating the `@cosyte/cli` heading below it
|
|
201
|
+
would be a wasted line. The wording is the one eight sibling packages already carry, confirmed
|
|
202
|
+
against both rendered PNGs here rather than copied on trust.
|
|
203
|
+
|
|
17
204
|
- **Phase 7: release hardening (the final roadmap phase; the CLI is feature-complete).** No new
|
|
18
205
|
runtime command surface: this phase is publish-readiness.
|
|
19
206
|
- **Fuzz gate over the two input boundaries.** `test/fuzz.property.test.ts` fuzzes the terminal
|
package/README.md
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
<picture>
|
|
2
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://cosyte.com/tile/cosyte-lockup-tile-on-dark-1200x300.png">
|
|
3
|
+
<img alt="Cosyte: a plus mark set in two overlapping rounded squares, one solid and one outlined, beside the Cosyte wordmark" src="https://cosyte.com/tile/cosyte-lockup-tile-on-light-1200x300.png">
|
|
4
|
+
</picture>
|
|
5
|
+
|
|
1
6
|
# @cosyte/cli
|
|
2
7
|
|
|
3
8
|
> The **cosyte CLI**: a PHI-safe developer front door over the `@cosyte/*` healthcare parsers.
|
|
@@ -17,7 +22,43 @@ It is a thin, honest skin over libraries that already own correctness ([`@cosyte
|
|
|
17
22
|
and owns two disciplines of its own: a documented **exit-code contract** and a **value-free
|
|
18
23
|
diagnostic** posture.
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
## Known issue: `0.0.1` and `0.0.2` are published but cannot be installed
|
|
26
|
+
|
|
27
|
+
**`npm install @cosyte/cli@0.0.1` and `@0.0.2` both fail.** They end in an `ENOENT`, like this:
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
npm error code ENOENT
|
|
31
|
+
npm error path node_modules/@cosyte/cli/vendor/cosyte-fhir-0.0.0.tgz
|
|
32
|
+
npm error enoent ENOENT: no such file or directory
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`npx` and `npm install -g` fail the same way on those two versions, and so does the `npx`-based MCP
|
|
36
|
+
server registration below. There is no workaround from the consumer side. **Nothing is wrong with your
|
|
37
|
+
setup, and re-running it will not help.**
|
|
38
|
+
|
|
39
|
+
**Why.** Those manifests declared the ten `@cosyte/*` sibling packages as local file paths
|
|
40
|
+
(`file:vendor/*.tgz`) rather than as npm version ranges. Those tarballs live in the git repository and
|
|
41
|
+
are deliberately not part of the published package, so npm resolved the paths against a directory that
|
|
42
|
+
does not exist in your `node_modules` and stopped at the first one. It was a packaging defect in those
|
|
43
|
+
releases, not a fault in any command.
|
|
44
|
+
|
|
45
|
+
**A published version is immutable, so `0.0.1` and `0.0.2` stay broken: install a later version.** The
|
|
46
|
+
sibling packages are now declared as real npm ranges, and the resulting tarball has been installed from
|
|
47
|
+
outside this repository and exercised, which is the check a `npm publish --dry-run` cannot perform.
|
|
48
|
+
|
|
49
|
+
**One dependency could not be made real, and it costs you FHIR support.** `@cosyte/fhir` is not on the
|
|
50
|
+
npm registry, so it cannot be a dependency of this package at all; `@cosyte/transform` is on npm but
|
|
51
|
+
requires it, so npm skips that too. In an installed copy:
|
|
52
|
+
|
|
53
|
+
- FHIR `parse` / `inspect` / `fmt` / `validate` and the `convert` command report a value-free
|
|
54
|
+
`CLI_PARSER_UNAVAILABLE` and exit `69`. They never guess, and they never blame your input.
|
|
55
|
+
- HL7 v2, `map-codes`, and the six breadth formats (X12, C-CDA, DICOM, NCPDP, ASTM, MLLP) all work.
|
|
56
|
+
|
|
57
|
+
To use the FHIR commands today, run the CLI from a source checkout (`pnpm install && pnpm build`, then
|
|
58
|
+
invoke `dist/bin/cosyte.mjs`), where the FHIR library is supplied locally.
|
|
59
|
+
|
|
60
|
+
> **Status:** pre-alpha (`0.0.x`). `0.0.1` and `0.0.2` are on npm and **cannot be installed** (see
|
|
61
|
+
> above); a later version fixes that. The `cosyte` command wraps **all eight
|
|
21
62
|
> cosyte formats** (**HL7 v2**, **FHIR R4**, **X12**, **ASTM**, **NCPDP SCRIPT**, **C-CDA**, **DICOM**,
|
|
22
63
|
> and **MLLP**) plus the `@cosyte/transform` and `@cosyte/terminology` higher-layer libraries, with
|
|
23
64
|
> conservative content-format autodetection and a documented exit-code contract:
|
|
@@ -34,8 +75,8 @@ diagnostic** posture.
|
|
|
34
75
|
> free issues on stderr, and a non-zero exit on an error-severity conversion issue.
|
|
35
76
|
> - **`map-codes`**: translate a code through a BYO FHIR ConceptMap via `@cosyte/terminology`; the
|
|
36
77
|
> target coding(s) on stdout, or a value-free unmapped signal + exit `1`.
|
|
37
|
-
> - **`redact` / `deid`**: gated to an honest `CLI_NOT_IMPLEMENTED` (exit `69`) until
|
|
38
|
-
>
|
|
78
|
+
> - **`redact` / `deid`**: gated to an honest `CLI_NOT_IMPLEMENTED` (exit `69`) until the CLI wires
|
|
79
|
+
> `@cosyte/deid`; it never reads the input and never emits a partial scrub dressed up as de-identified.
|
|
39
80
|
> - **`completion <bash|zsh|fish>`**: print a static shell completion script.
|
|
40
81
|
>
|
|
41
82
|
> **Support is honest per (format, operation)**: not every parser faithfully supports every command, so
|
|
@@ -45,23 +86,42 @@ diagnostic** posture.
|
|
|
45
86
|
> as the single door to a value on a secondary surface, and never a temp file with PHI. An **MCP server**
|
|
46
87
|
> (`cosyte-mcp`) exposes the same core to an LLM/agent as callable tools.
|
|
47
88
|
>
|
|
48
|
-
> The CLI is **feature-complete
|
|
49
|
-
>
|
|
50
|
-
>
|
|
51
|
-
>
|
|
52
|
-
>
|
|
89
|
+
> The CLI is **feature-complete**: an argv+stdin+MCP fuzz gate, an exit-code golden matrix, a
|
|
90
|
+
> built-package smoke of both bins, and a clean `npm publish` dry-run. Those gates cover the code, and
|
|
91
|
+
> the code was never what was broken. The one release step they do not cover is the dependency swap
|
|
92
|
+
> described above, which is why `0.0.1` and `0.0.2` published green and still cannot be installed: a
|
|
93
|
+
> dry-run builds the tarball but never resolves it from a registry. **That swap has now been made**,
|
|
94
|
+
> and installing the packed tarball from outside the repository is a release step in its own right.
|
|
95
|
+
> `@cosyte/fhir` is the one dependency it could not cover, which is why FHIR support is absent from an
|
|
96
|
+
> installed copy rather than merely deferred. See [RELEASING.md](./RELEASING.md).
|
|
53
97
|
|
|
54
98
|
## Run it
|
|
55
99
|
|
|
56
|
-
>
|
|
57
|
-
>
|
|
100
|
+
> **These commands do not work yet, and the reason is now only that no fixed version has shipped.**
|
|
101
|
+
> The newest version on npm is `0.0.2`, and it is one of the two that cannot be installed. The
|
|
102
|
+
> packaging defect is fixed in this repository and proven by installing the packed tarball outside it,
|
|
103
|
+
> but a published version is immutable, so the fix reaches you only in the next release. Until then,
|
|
104
|
+
> run it from a source checkout: `pnpm install && pnpm build`, then invoke `dist/bin/cosyte.mjs`.
|
|
58
105
|
|
|
59
106
|
```bash
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
cosyte --help
|
|
107
|
+
npm install -g @cosyte/cli # put `cosyte` on your PATH
|
|
108
|
+
cosyte parse message.hl7 # format autodetected → HL7 v2
|
|
63
109
|
```
|
|
64
110
|
|
|
111
|
+
Or without installing, using `npx`:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
npx --package @cosyte/cli cosyte parse message.hl7
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
> **`npx @cosyte/cli …` (the short form) does not work, and this is not the packaging defect above.**
|
|
118
|
+
> It fails with `could not determine executable to run`. When a package ships more than one
|
|
119
|
+
> executable, `npx` runs the one whose name matches the package name's last segment; that would be
|
|
120
|
+
> `cli`, and this package ships `cosyte` and `cosyte-mcp`. Naming the executable explicitly with
|
|
121
|
+
> `--package` is the supported form, and it works. A `cli` executable is deliberately **not** added as
|
|
122
|
+
> an alias: `npm install -g` would then put a command called `cli` on every user's `PATH`, which is far
|
|
123
|
+
> too generic a name to claim.
|
|
124
|
+
|
|
65
125
|
## `cosyte parse`
|
|
66
126
|
|
|
67
127
|
Read a file (or stdin via `-`), autodetect the format by content, and print the parsed model as typed
|
|
@@ -207,13 +267,14 @@ cosyte parse broken.hl7 --format hl7 --unsafe-show-values # appends a bounded i
|
|
|
207
267
|
## `cosyte redact` / `cosyte deid`
|
|
208
268
|
|
|
209
269
|
De-identification is the one operation whose _job_ is to strip PHI. It is **not implemented yet, on
|
|
210
|
-
purpose.** It belongs to [`@cosyte/deid`](https://github.com/cosyte/deid)
|
|
211
|
-
the wrapped parsers expose no de-identification API
|
|
270
|
+
purpose.** It belongs to [`@cosyte/deid`](https://github.com/cosyte/deid). That package is now
|
|
271
|
+
published, but the CLI does not wire it yet, and the wrapped parsers expose no de-identification API of
|
|
272
|
+
their own. A built-in "minimal Safe-Harbor" pass over only
|
|
212
273
|
the obvious fields would leave PHI behind and _look_ de-identified while silently under-redacting: the
|
|
213
274
|
exact false-safety hazard `redact` exists to avoid. So `cosyte redact <file>` is an honest, typed
|
|
214
275
|
`CLI_NOT_IMPLEMENTED` (exit `69`, `EX_UNAVAILABLE`) that **never reads your input** and **never emits a
|
|
215
|
-
partial scrub dressed up as safe**. It will produce a real de-identified copy once
|
|
216
|
-
|
|
276
|
+
partial scrub dressed up as safe**. It will produce a real de-identified copy once the CLI wires
|
|
277
|
+
`@cosyte/deid` and that integration is vetted.
|
|
217
278
|
|
|
218
279
|
## `cosyte completion`
|
|
219
280
|
|
|
@@ -234,11 +295,17 @@ subprocess**, not a hosted endpoint. Register it in an MCP client's config:
|
|
|
234
295
|
```json
|
|
235
296
|
{
|
|
236
297
|
"mcpServers": {
|
|
237
|
-
"cosyte": { "command": "npx", "args": ["-y", "@cosyte/cli", "mcp"] }
|
|
298
|
+
"cosyte": { "command": "npx", "args": ["-y", "--package", "@cosyte/cli", "cosyte-mcp"] }
|
|
238
299
|
}
|
|
239
300
|
}
|
|
240
301
|
```
|
|
241
302
|
|
|
303
|
+
> **`--package` is required, and the shorter `["-y", "@cosyte/cli", "mcp"]` does not work**: it fails
|
|
304
|
+
> with `could not determine executable to run`, for the `npx` executable-selection reason described
|
|
305
|
+
> under [Run it](#run-it) above. This also needs a version that can be installed at all, so not
|
|
306
|
+
> `0.0.1` or `0.0.2`. The `convert` tool reports `CLI_PARSER_UNAVAILABLE` from an npm install, because
|
|
307
|
+
> the FHIR library is not on the registry.
|
|
308
|
+
|
|
242
309
|
`cosyte mcp` and the standalone `cosyte-mcp` bin both start the stdio server. It exposes four tools
|
|
243
310
|
(`parse`, `validate`, `inspect`, `convert`), each calling the same command the terminal runs, so the CLI
|
|
244
311
|
and the agent get identical results. The PHI posture is inherited and hardened: there is **no
|
package/dist/bin/cosyte-mcp.cjs
CHANGED
|
@@ -7,7 +7,7 @@ var types_js = require('@modelcontextprotocol/sdk/types.js');
|
|
|
7
7
|
var util = require('util');
|
|
8
8
|
|
|
9
9
|
// src/core/version.ts
|
|
10
|
-
var VERSION = "0.0.
|
|
10
|
+
var VERSION = "0.0.3";
|
|
11
11
|
|
|
12
12
|
// src/core/exit-codes.ts
|
|
13
13
|
var EXIT = {
|
|
@@ -23,7 +23,7 @@ var EXIT = {
|
|
|
23
23
|
DATAERR: 65,
|
|
24
24
|
/** No input: the named file does not exist or is unreadable (`EX_NOINPUT`). */
|
|
25
25
|
NOINPUT: 66,
|
|
26
|
-
/** Unavailable: a required capability is not yet
|
|
26
|
+
/** Unavailable: a required capability is not yet wired (e.g. `redact` before `@cosyte/deid`), a
|
|
27
27
|
* distinct non-zero signal that is never a fake success (`EX_UNAVAILABLE`). */
|
|
28
28
|
UNAVAILABLE: 69,
|
|
29
29
|
/** Internal error: an unexpected exception (a bug), distinct from a handled bad input (`EX_SOFTWARE`). */
|
|
@@ -51,8 +51,8 @@ var CLI_CODES = {
|
|
|
51
51
|
CLI_PARSER_UNAVAILABLE: "CLI_PARSER_UNAVAILABLE",
|
|
52
52
|
/** The wrapped parser rejected the input. Positional context only, never the offending bytes. Exit `65`. */
|
|
53
53
|
CLI_PARSE_FAILED: "CLI_PARSE_FAILED",
|
|
54
|
-
/** A command whose ground-layer library is not yet
|
|
55
|
-
*
|
|
54
|
+
/** A command whose ground-layer library is not yet wired (e.g. `redact` before `@cosyte/deid` is
|
|
55
|
+
* wired). Never a fake success: a distinct, value-free "unavailable" signal. Exit `69`. */
|
|
56
56
|
CLI_NOT_IMPLEMENTED: "CLI_NOT_IMPLEMENTED",
|
|
57
57
|
/** An unexpected internal error (a bug). Exit `70`. */
|
|
58
58
|
CLI_INTERNAL: "CLI_INTERNAL"
|
|
@@ -228,19 +228,25 @@ function anyError(findings) {
|
|
|
228
228
|
return findings.some((f) => ERROR_SEVERITIES.has(f.severity));
|
|
229
229
|
}
|
|
230
230
|
async function loadOptional(format, load) {
|
|
231
|
+
return loadOptionalPackage(
|
|
232
|
+
`the @cosyte/${format} parser is not installed; install it to use this format (it is an optional dependency)`,
|
|
233
|
+
load
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
async function loadOptionalPackage(detail, load) {
|
|
231
237
|
try {
|
|
232
238
|
return await load();
|
|
233
239
|
} catch (e) {
|
|
234
240
|
if (isModuleNotFound(e)) {
|
|
235
|
-
throw new CliError(
|
|
236
|
-
CLI_CODES.CLI_PARSER_UNAVAILABLE,
|
|
237
|
-
EXIT.UNAVAILABLE,
|
|
238
|
-
`the @cosyte/${format} parser is not installed; install it to use this format (it is an optional dependency)`
|
|
239
|
-
);
|
|
241
|
+
throw new CliError(CLI_CODES.CLI_PARSER_UNAVAILABLE, EXIT.UNAVAILABLE, detail);
|
|
240
242
|
}
|
|
241
243
|
throw e;
|
|
242
244
|
}
|
|
243
245
|
}
|
|
246
|
+
var FHIR_UNAVAILABLE = "the @cosyte/fhir parser is not installed, and it is not currently on the npm registry, so it cannot be installed from there; FHIR support is unavailable in this install";
|
|
247
|
+
async function loadFhir() {
|
|
248
|
+
return loadOptionalPackage(FHIR_UNAVAILABLE, () => import('@cosyte/fhir'));
|
|
249
|
+
}
|
|
244
250
|
function isModuleNotFound(e) {
|
|
245
251
|
if (typeof e !== "object" || e === null) return false;
|
|
246
252
|
const code = "code" in e ? e.code : void 0;
|
|
@@ -259,7 +265,7 @@ async function parseFormat(format, bytes) {
|
|
|
259
265
|
};
|
|
260
266
|
}
|
|
261
267
|
case "fhir": {
|
|
262
|
-
const { parseResource, serializeResource } = await
|
|
268
|
+
const { parseResource, serializeResource } = await loadFhir();
|
|
263
269
|
const text = decode(bytes);
|
|
264
270
|
const { resource, issues } = parseResource(text);
|
|
265
271
|
const model = JSON.parse(serializeResource(resource));
|
|
@@ -320,7 +326,7 @@ async function inspectFormat(format, bytes) {
|
|
|
320
326
|
};
|
|
321
327
|
}
|
|
322
328
|
case "fhir": {
|
|
323
|
-
const { parseResource, resourceType, readBundle } = await
|
|
329
|
+
const { parseResource, resourceType, readBundle } = await loadFhir();
|
|
324
330
|
const { resource, issues } = parseResource(decode(bytes));
|
|
325
331
|
const rt = resourceType(resource) ?? null;
|
|
326
332
|
if (rt === "Bundle") {
|
|
@@ -425,7 +431,7 @@ async function inspectFormat(format, bytes) {
|
|
|
425
431
|
async function validateFormat(format, bytes) {
|
|
426
432
|
switch (format) {
|
|
427
433
|
case "fhir": {
|
|
428
|
-
const { parseResource, validateResource } = await
|
|
434
|
+
const { parseResource, validateResource } = await loadFhir();
|
|
429
435
|
const { resource, issues } = parseResource(decode(bytes));
|
|
430
436
|
const validation = validateResource(resource);
|
|
431
437
|
const findings = [
|
|
@@ -673,11 +679,12 @@ async function convertCommand(args, deps, posture = VALUE_FREE) {
|
|
|
673
679
|
return { stdout: `${conversion.bundleText}
|
|
674
680
|
`, stderr, exit };
|
|
675
681
|
}
|
|
682
|
+
var TRANSFORM_UNAVAILABLE = "the @cosyte/transform conversion library is not installed, so convert is unavailable; it requires @cosyte/fhir, which is not currently on the npm registry, so npm skips transform as an unresolvable optional dependency and installing it directly fails for the same reason";
|
|
676
683
|
async function runConvert(bytes, posture) {
|
|
677
684
|
const [{ parseHL7 }, { toFhir }, { serializeResource }] = await Promise.all([
|
|
678
685
|
import('@cosyte/hl7'),
|
|
679
|
-
import('@cosyte/transform'),
|
|
680
|
-
|
|
686
|
+
loadOptionalPackage(TRANSFORM_UNAVAILABLE, () => import('@cosyte/transform')),
|
|
687
|
+
loadFhir()
|
|
681
688
|
]);
|
|
682
689
|
let msg;
|
|
683
690
|
try {
|