@venn-lang/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -0
  3. package/dist/bin/venn.mjs +16 -0
  4. package/dist/cli.mjs +56378 -0
  5. package/dist/index.d.mts +98 -0
  6. package/dist/index.d.mts.map +1 -0
  7. package/dist/index.mjs +916 -0
  8. package/dist/index.mjs.map +1 -0
  9. package/package.json +67 -0
  10. package/src/bin/venn.ts +14 -0
  11. package/src/cli.ts +299 -0
  12. package/src/commands/build.ts +97 -0
  13. package/src/commands/check.ts +143 -0
  14. package/src/commands/deps.ts +228 -0
  15. package/src/commands/fmt.ts +55 -0
  16. package/src/commands/index.ts +2 -0
  17. package/src/commands/inside-workspace.ts +20 -0
  18. package/src/commands/list.ts +53 -0
  19. package/src/commands/new.ts +66 -0
  20. package/src/commands/run.ts +149 -0
  21. package/src/commands/script.ts +127 -0
  22. package/src/commands/upgrade.ts +86 -0
  23. package/src/commands/verify-plugin.ts +35 -0
  24. package/src/index.ts +7 -0
  25. package/src/manifest/index.ts +2 -0
  26. package/src/manifest/load-env.ts +70 -0
  27. package/src/manifest/load-manifest.ts +42 -0
  28. package/src/project/command-targets.ts +53 -0
  29. package/src/project/index.ts +9 -0
  30. package/src/project/resolve-targets.ts +70 -0
  31. package/src/project/select-packages.ts +37 -0
  32. package/src/project/targets-or-exit.ts +28 -0
  33. package/src/reporters/colors.ts +17 -0
  34. package/src/reporters/dot-sink.ts +18 -0
  35. package/src/reporters/error-line.ts +14 -0
  36. package/src/reporters/index.ts +9 -0
  37. package/src/reporters/junit-sink.ts +39 -0
  38. package/src/reporters/ndjson-stdout.ts +13 -0
  39. package/src/reporters/pick-reporter.ts +28 -0
  40. package/src/reporters/pretty/diff-lines.ts +53 -0
  41. package/src/reporters/pretty/index.ts +1 -0
  42. package/src/reporters/pretty/pretty-reporter.ts +122 -0
  43. package/src/reporters/pretty/pretty.types.ts +29 -0
  44. package/src/reporters/pretty/render.ts +88 -0
  45. package/src/reporters/problem-reporter.ts +13 -0
  46. package/src/reporters/problem-sink.ts +20 -0
  47. package/src/reporters/reporter.types.ts +21 -0
  48. package/src/run/collect-files.ts +43 -0
  49. package/src/run/ending.types.ts +17 -0
  50. package/src/run/exit-code.ts +15 -0
  51. package/src/run/node-io.ts +27 -0
  52. package/src/run/npm-loader.ts +41 -0
  53. package/src/run/package-types.ts +66 -0
  54. package/src/run/run-file.ts +106 -0
  55. package/src/run/should-leave.ts +11 -0
  56. package/src/run/step-titles.ts +61 -0
  57. package/src/shutdown/create-leave.ts +42 -0
  58. package/src/shutdown/create-shutdown.ts +34 -0
  59. package/src/shutdown/index.ts +7 -0
  60. package/src/shutdown/install-exit-hook.ts +21 -0
  61. package/src/shutdown/install-fault-hooks.ts +30 -0
  62. package/src/shutdown/install-hooks.ts +36 -0
  63. package/src/shutdown/install-signal-hooks.ts +26 -0
  64. package/src/shutdown/shutdown.types.ts +20 -0
  65. package/src/title/index.ts +2 -0
  66. package/src/title/program-title.ts +15 -0
  67. package/src/title/set-program-title.ts +15 -0
  68. package/src/upgrade/index.ts +6 -0
  69. package/src/upgrade/install-site.ts +70 -0
  70. package/src/upgrade/latest-version.ts +50 -0
  71. package/src/upgrade/upgrade-command.ts +32 -0
  72. package/src/upgrade/upgrade-plan.ts +36 -0
  73. package/src/upgrade/upgrade.types.ts +17 -0
  74. package/src/upgrade/version.ts +21 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Vinicius Borges
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,284 @@
1
+ # @venn-lang/cli
2
+
3
+ > The `venn` binary: one command for starting, checking, running and testing a Venn project.
4
+
5
+ This is the only package that touches `node:*`. It builds the `Host`, binds the real
6
+ implementations behind every port (filesystem, HTTP client, HTTP server, console, spawn) and hands
7
+ them to the runtime. Everything below it (`@venn-lang/core`, `@venn-lang/runtime`, `@venn-lang/sdk`) stays
8
+ platform-neutral, which is why the same compiler runs in a Web Worker for the editor.
9
+
10
+ ## Usage
11
+
12
+ ```ruby
13
+ # tests/hello.vn
14
+ module demo.hello
15
+
16
+ use "venn/http"
17
+ use "venn/assert"
18
+
19
+ flow "Hello" {
20
+ step "Ping" {
21
+ let res = http.get "https://example.com"
22
+ expect res.status == 200
23
+ }
24
+ }
25
+ ```
26
+
27
+ ```bash
28
+ venn test tests/hello.vn
29
+ ```
30
+
31
+ The repository is not published, so from a source checkout the binary is:
32
+
33
+ ```bash
34
+ pnpm --filter @venn-lang/cli build
35
+ node packages/cli/dist/bin/venn.mjs test examples/
36
+ ```
37
+
38
+ ## Commands
39
+
40
+ | Command | What it does |
41
+ | --- | --- |
42
+ | `venn new <name>` | Start a project in a new directory |
43
+ | `venn init` | Start a project in the directory you are in |
44
+ | `venn add <pkg…>` | Add a dependency to `venn.toml` and install it |
45
+ | `venn remove <pkg…>` | Remove a dependency and install without it |
46
+ | `venn update` | Update what is installed, within the ranges asked for |
47
+ | `venn install` | Install what the manifest asks for |
48
+ | `venn build` | Check every target and record the build under `target/` |
49
+ | `venn run [target]` | Run a file as a program: its statements, top to bottom |
50
+ | `venn test [target]` | Run every flow in a file or folder as a test suite |
51
+ | `venn list [target]` | Print the flows and steps that would run |
52
+ | `venn fmt [target]` | Format `.vn` files in place |
53
+ | `venn check [target]` | Statically check without running |
54
+ | `venn verify-plugin <path>` | Inspect a plugin module and check its shape |
55
+ | `venn upgrade` | Move a global install to the latest published version |
56
+
57
+ ### Starting a project
58
+
59
+ ```bash
60
+ venn new my-suite # a program: venn.toml, src/main.vn, .gitignore
61
+ venn new my-lib --lib # a library: venn.toml, src/lib.vn
62
+ venn new monorepo --workspace # a root that owns members
63
+ venn init --name api # the same, in the current directory
64
+ ```
65
+
66
+ | Flag | Effect |
67
+ | --- | --- |
68
+ | `--bin` | A program with a `main`. This is the default. |
69
+ | `--lib` | A library: other packages use what it marks `pub`. |
70
+ | `--workspace` | A root that owns members, one lock and one `target/`. |
71
+ | `--dry-run` | Print what would be written and write nothing. |
72
+ | `--name <name>` | `init` only. Defaults to the directory's own name. |
73
+
74
+ A `.gitignore` is written only when the new package is not already inside a workspace, because one
75
+ `target/` per workspace means one line ignoring it, at the root that owns it. An existing
76
+ `venn.toml` is never overwritten: the command stops with `VN2102`.
77
+
78
+ ### Dependencies
79
+
80
+ ```bash
81
+ venn add zod # newest, pinned back into venn.toml as ^x.y.z
82
+ venn add hono@^4 -D # a development dependency
83
+ venn remove zod
84
+ venn install --frozen # refuse anything the lock did not record
85
+ ```
86
+
87
+ | Flag | Effect |
88
+ | --- | --- |
89
+ | `-p, --package <name>` | Act on one workspace member. |
90
+ | `-D, --dev` | `add` and `remove`: the `[dev-dependencies]` table. |
91
+ | `--frozen` | `install`: check what is installed against `venn.lock` and fail on drift. |
92
+
93
+ `venn.toml` is edited first, then the package manager runs against a `package.json` generated into
94
+ `target/`. That ordering is what makes the manifest the source rather than a copy. Which manager
95
+ runs is `[tooling] manager` in the root manifest: `pnpm` (the default), `npm`, `bun` or `yarn`.
96
+
97
+ After a successful install the lockfile is written and the types each package publishes are derived
98
+ into `target/types/`. Each one reports a measured coverage line, `<name>: <pct>% of <n> exports
99
+ typed`, rather than a claim. Those files are what `venn check` reads to type an
100
+ `import { z } from "zod"`.
101
+
102
+ Names are validated before anything runs (`VN2105`). Under `--frozen` the lock is the input, not the
103
+ output: a missing lock is `VN2106` and any drift is `VN2107`, listed file by file.
104
+
105
+ ### Running
106
+
107
+ ```bash
108
+ venn run src/main.vn arg1 arg2
109
+ venn run --bin worker --env staging
110
+ ```
111
+
112
+ `run` executes the file as a program. `test` runs its flows as a suite. Arguments after the file
113
+ reach the program as `io.args`.
114
+
115
+ A program that reached its last line is not asked to stop: a server that bound a port keeps
116
+ serving, and the event loop decides when the process ends. A program that said `exit N`, or that
117
+ ended badly, leaves at once, and whatever it opened is closed on the way out. The same closing runs
118
+ for a signal, an uncaught fault and an ordinary exit.
119
+
120
+ | Flag | Command | Effect |
121
+ | --- | --- | --- |
122
+ | `--bin <name>` | `run` | Which program, when the package has several. |
123
+ | `--env <name>` | `run`, `test` | The environment from `venn.toml`. Default `local`. |
124
+ | `-p, --package <name>` | all | Act on one workspace member. |
125
+ | `--reporter <name>` | `test` | `pretty`, `ndjson`, `dot` or `junit`. |
126
+ | `--flow <text>` | `test`, `list` | Only flows whose title contains this. |
127
+ | `--step <text>` | `test`, `list` | Only steps whose title contains this. |
128
+ | `--tags <a,b>` | `test` | Comma-separated `@tag` filter. |
129
+ | `--bail` | `test` | Stop after the first failing flow. |
130
+
131
+ ### Checking
132
+
133
+ ```bash
134
+ venn check . # resolve actions, matchers, imports and types
135
+ venn fmt src/ # format in place
136
+ venn fmt --check . # report what would change and fail, for CI
137
+ venn build --release
138
+ ```
139
+
140
+ `check` parses each file, resolves its imports, builds the plugin registry from the whole stdlib and
141
+ runs the type checker. Problems from every file are gathered and reported once. `fmt --check` exits
142
+ 1 when anything would change.
143
+
144
+ `build` checks every target of the selected packages and writes `target/debug/build.json`, or
145
+ `target/release/build.json` with `--release`. There is no code generation yet and the record claims
146
+ none: it lists the targets covered, the files read and the number of problems. A build with problems
147
+ always fails; what the profile decides is whether the record is written anyway. `[profile.dev]` is
148
+ lenient by default and `[profile.release]` is strict, and either can set `strict` explicitly.
149
+
150
+ ### verify-plugin
151
+
152
+ ```bash
153
+ venn verify-plugin ./dist/index.mjs
154
+ ```
155
+
156
+ Imports the module, takes its default export (or the first export that looks like a plugin) and
157
+ prints the name, the namespace and how many actions, matchers and resources it declares. Exits 1
158
+ when the shape is wrong.
159
+
160
+ ### upgrade
161
+
162
+ ```bash
163
+ venn upgrade # ask first, then run the install
164
+ venn upgrade --dry-run # print the command that would run, change nothing
165
+ venn upgrade --yes # skip the question, for a script
166
+ ```
167
+
168
+ Finds which manager installed this copy by reading the path it is running from, then runs that
169
+ manager's own global install. It does not rewrite its own files: the CLI cannot replace itself while
170
+ it is executing, and on Windows the running executable is locked.
171
+
172
+ The path is the signal because `npm_config_user_agent`, the obvious alternative, is only set while a
173
+ package script runs and is empty when the binary is invoked directly, which is every real use.
174
+
175
+ Two cases are refused rather than guessed at. A copy the project owns is left alone, since its
176
+ version is pinned in the manifest and the next install would undo the upgrade; the message says to
177
+ update it there instead. A path that matches no manager is refused outright, with the install command
178
+ to run by hand. Both exit 1.
179
+
180
+ A prerelease is never offered to someone on a stable version, since opting into one is a decision
181
+ rather than an update.
182
+
183
+ ## Targets: what a bare command means
184
+
185
+ A path given outright always wins and is never second-guessed. With no path, the nearest `venn.toml`
186
+ answers, walking up from the current directory; a workspace answers with its `default-members`, or
187
+ with all of them when it named none.
188
+
189
+ | Command | With no path |
190
+ | --- | --- |
191
+ | `venn test`, `venn list` | The `tests/` directory of each selected package |
192
+ | `venn run` | The package's single `bin` target, conventionally `src/main.vn` |
193
+ | `venn check`, `venn fmt` | Every `.vn` file the selected packages own |
194
+
195
+ Directories are walked recursively, sorted so runs are reproducible, skipping `node_modules`, `dist`
196
+ and `.git`. Nothing silently succeeds: no project is `VN2101`, an unknown `-p` name is `VN2103`, and
197
+ several `bin` targets with no `--bin` prints the names to pick from.
198
+
199
+ ## Reporters
200
+
201
+ | Name | Output |
202
+ | --- | --- |
203
+ | `pretty` | A live tree: a banner per file, a branch per flow, a verdict per step, then every failure repeated at the end with its `VNxxxx` code and source location. |
204
+ | `ndjson` | One event envelope per line on stdout. |
205
+ | `dot` | One character per assertion, then a summary line. |
206
+ | `junit` | A JUnit XML document, emitted on `run.finished`. |
207
+
208
+ With no `--reporter`, a terminal gets `pretty` and anything piped gets `ndjson`, so scripts and CI
209
+ keep a stream they can parse.
210
+
211
+ ## Environments
212
+
213
+ `--env <name>` selects an environment, `local` unless told otherwise. Variables come from three
214
+ places, lowest precedence first:
215
+
216
+ 1. `[env.<name>]` in `venn.toml`, the documented default, committed.
217
+ 2. The dotenv files, in order: `.env`, `.env.<name>`, `.env.local`, `.env.<name>.local`, or whatever
218
+ `[env] files` lists instead.
219
+ 3. The environment the process was started with.
220
+
221
+ The real environment wins, because that is how CI passes a token in. It overrides rather than adds:
222
+ a name has to be declared in one of the first two places for the third to fill it, which keeps
223
+ `PATH` and `TEMP` out of the editor's completion. A value that exists only in CI is read through
224
+ `secrets.*`, which needs no declaration and redacts what it returns.
225
+
226
+ The manifest that governs a file is found by walking up to the project the file belongs to, not by
227
+ reading whatever sits in the same folder, so `venn test packages/api/tests/login.vn` sees the same
228
+ environments and `[paths]` aliases as running it from inside `packages/api`.
229
+
230
+ ## API
231
+
232
+ The package also exports the seam the commands are built on, for embedding a run in another program.
233
+
234
+ | Export | What it is |
235
+ | --- | --- |
236
+ | `runFile(args)` | Parse and run one `.vn` source with the full stdlib loaded. Returns `{ problems, result }`. |
237
+ | `RunFileOutcome` | The result type: `Problem[]` plus the runtime's `RunResult`. |
238
+ | `runCommand(options)` | What `venn test` does for one file: collect, run, report, return an exit code. |
239
+ | `verifyPluginCommand({ path })` | What `venn verify-plugin` does. |
240
+ | `createStdoutSink()` | An NDJSON `EventSink` that writes each envelope to stdout. |
241
+ | `reportProblems(problems)` | Print `VNxxxx` problems to stderr with their source location. |
242
+
243
+ `runFile` takes the ports it should use, so a test can drive it entirely offline:
244
+
245
+ ```ts
246
+ import { createTestHost } from "@venn-lang/contracts";
247
+ import { createFakeClient, okResponse } from "@venn-lang/http";
248
+ import { createMemorySink } from "@venn-lang/runtime";
249
+ import { runFile } from "@venn-lang/cli";
250
+
251
+ const source = `module demo.hello
252
+ use "venn/http"
253
+ use "venn/assert"
254
+
255
+ flow "Hello" {
256
+ step "Ping" {
257
+ const res = http.get "https://example.com"
258
+ expect res.status == 200
259
+ }
260
+ }`;
261
+
262
+ const outcome = await runFile({
263
+ source,
264
+ uri: "memory://hello.vn",
265
+ host: createTestHost(),
266
+ sink: createMemorySink(),
267
+ httpClient: createFakeClient({
268
+ responses: { "https://example.com": okResponse({ status: 200 }) },
269
+ }),
270
+ });
271
+
272
+ outcome.problems; // []
273
+ outcome.result?.passed; // 1
274
+ ```
275
+
276
+ `mode: "script"` runs the file top to bottom instead of running its flows. `filter`, `bail`, `env`,
277
+ `io`, `npm` and `cleanup` are the rest of what the commands pass in.
278
+
279
+ ## See also
280
+
281
+ - [`@venn-lang/runtime`](../runtime) for the scheduler, the plugin registry and the event stream.
282
+ - [`@venn-lang/project`](../project) for manifests, workspaces, lockfiles and build profiles.
283
+ - [`@venn-lang/contracts`](../contracts) for the ports and the Node implementations bound here.
284
+ - [`@venn-lang/lsp`](../lsp) for the same compiler behind an editor.
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env node
2
+ import { enableCompileCache } from "node:module";
3
+ //#region src/bin/venn.ts
4
+ /**
5
+ * The launcher, deliberately tiny.
6
+ *
7
+ * `enableCompileCache` keeps V8's compilation of the bundle on disk between
8
+ * runs, but only for modules loaded after it is called, so the engine has to
9
+ * arrive through a dynamic import rather than a static one. The URL is computed
10
+ * so the bundler leaves it alone: this file must stay separate from the file it
11
+ * loads, or there is nothing left to cache.
12
+ */
13
+ enableCompileCache?.();
14
+ await import(new URL("../cli.mjs", import.meta.url).href);
15
+ //#endregion
16
+ export {};