@levr-one/cli 0.11.1 → 0.11.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/README.md CHANGED
@@ -23,7 +23,7 @@ replaces the `levr` bin from the deprecated `@levr-one/setup` package.
23
23
  ### Or run it without a global install
24
24
 
25
25
  ```bash
26
- npx @levr-one/cli --help
26
+ npx @levr-one/cli@latest --help
27
27
  ```
28
28
 
29
29
  `npx` is right for one-shot use (`mcp add`, trying a command). Two things to
@@ -40,18 +40,22 @@ In scripts and CI, pass `--yes` — **in a terminal the prompt blocks until it i
40
40
  answered**, so an unattended run with a TTY hangs rather than failing:
41
41
 
42
42
  ```bash
43
- npx --yes @levr-one/cli push ./test-results.xml
43
+ npx --yes @levr-one/cli@latest push ./test-results.xml
44
44
  ```
45
45
 
46
- Two things to get right:
46
+ Three things to get right:
47
47
 
48
- - **Always `npx @levr-one/cli`, never `npx levr`.** npx treats the first
48
+ - **Add `@latest`.** Without a version, npx runs any copy of `@levr-one/cli`
49
+ already installed on the machine, however old, and an old copy does not know
50
+ newer commands and flags (`No flag registered for --workspace`).
51
+ - **Always `npx @levr-one/cli@latest`, never `npx levr`.** npx treats the first
49
52
  positional as a _package_ name, so `npx levr` would fetch and run whatever
50
53
  package is published under the unscoped name `levr` — which is not ours.
51
- - **npm's flags go before the package name.** `npx @levr-one/cli --yes` passes
54
+ - **npm's flags go before the package name.** `npx @levr-one/cli@latest --yes` passes
52
55
  `--yes` to `levr`, not to npm.
53
56
 
54
- For reproducible CI, pin `@levr-one/cli` to a version you have validated.
57
+ The examples here use `@latest`. For reproducible CI, replace it with a
58
+ version you have validated, such as `@levr-one/cli@0.11.1`.
55
59
 
56
60
  ## Quick start
57
61
 
@@ -59,7 +63,7 @@ For reproducible CI, pin `@levr-one/cli` to a version you have validated.
59
63
  the client opens a browser to authorize the first time it connects:
60
64
 
61
65
  ```bash
62
- npx @levr-one/cli mcp add
66
+ npx @levr-one/cli@latest mcp add
63
67
  ```
64
68
 
65
69
  **Pushing test results?** Authentication is all you need — log in once (or
@@ -82,40 +86,72 @@ levr mcp add --dry-run # preview the changes without writing
82
86
  levr mcp add --client cursor,zed --yes # non-interactive selection
83
87
  ```
84
88
 
85
- What it writes is **credential-free** — a `levr` server entry that connects
86
- via `mcp-remote`. No token or secret is stored; your client opens the browser
87
- to authorize with Levr on its first connection. After a run, restart the
88
- client(s) and authorize once.
89
+ What it writes is **credential-free** — a `levr` server entry holding only
90
+ the server's address. No token or secret is stored; your client opens the
91
+ browser to authorize with Levr on its first connection. After a run, restart
92
+ the client(s) and authorize once.
93
+
94
+ Clients that speak HTTP MCP themselves (Claude Code, VS Code, Gemini CLI,
95
+ Codex CLI, Grok Build, Antigravity) connect to the URL directly. Claude
96
+ Desktop, Cursor, Windsurf and Zed get an entry that runs
97
+ `npx -y mcp-remote <url>`, which connects on their behalf.
89
98
 
90
99
  **Supported clients:**
91
100
 
92
- | Client | How it's configured |
93
- | -------------- | ------------------------------------------------------------------------------ |
94
- | Claude Desktop | config file (`claude_desktop_config.json`) |
95
- | Claude Code | prints the `claude mcp add --transport http levr <url>` command for you to run |
96
- | Cursor | config file (`~/.cursor/mcp.json`) |
97
- | Windsurf | config file (`~/.codeium/windsurf/mcp_config.json`) |
98
- | Zed | config file (`settings.json`, `context_servers`) |
99
- | VS Code, Codex | listed but not yet installable (coming soon) |
101
+ | Client | How it's configured |
102
+ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
103
+ | Claude Desktop | config file (`claude_desktop_config.json`) |
104
+ | Claude Code | runs `claude mcp add --transport http --scope user levr <url>` for you (prints it instead when `claude` is not on your `PATH`) |
105
+ | Cursor | config file (`~/.cursor/mcp.json`) |
106
+ | Windsurf | config file (`~/.codeium/windsurf/mcp_config.json`) |
107
+ | Zed | config file (`settings.json`, `context_servers`) |
108
+ | VS Code | config file (`Code/User/mcp.json` in your VS Code settings folder; `.vscode/mcp.json` with `--scope project`) |
109
+ | Gemini CLI | config file (`~/.gemini/settings.json`) |
110
+ | Codex CLI | config file (`~/.codex/config.toml`, TOML) |
111
+ | Grok Build | config file (`~/.grok/config.toml`, TOML) |
112
+ | Antigravity (CLI + IDE) | config file (`~/.gemini/config/mcp_config.json`) |
100
113
 
101
114
  Config edits are **safe and repeatable**: existing MCP servers and comments
102
- in your config files are preserved (jsonc-aware merge), and re-running is a
103
- no-op that reports "already set up".
115
+ in your config files are preserved, and re-running reports "already set up".
116
+ An entry of the same name that points at a different address is left alone
117
+ and reported; pass `--replace` to switch it.
104
118
 
105
119
  **Flags:**
106
120
 
107
- | Flag | Alias | Description |
108
- | ----------------- | ----- | ----------------------------------------------------- |
109
- | `--client <id,…>` | | Set up these client ids (comma-separated or repeated) |
110
- | `--all` | | Set up every detected, installable client |
111
- | `--yes` | `-y` | Non-interactive; auto-select detected clients |
112
- | `--dry-run` | | Show the changes without writing |
113
- | `--url <url>` | | MCP server URL (default derived from the API server) |
121
+ | Flag | Alias | Description |
122
+ | -------------------------------- | ----- | ----------------------------------------------------------------------------------- |
123
+ | `--client <id,…>` | | Set up these client ids (comma-separated or repeated) |
124
+ | `--all` | | Set up every detected, installable client |
125
+ | `--yes` | `-y` | Non-interactive; auto-select detected clients |
126
+ | `--dry-run` | | Show the changes without writing |
127
+ | `--scope <user\|project\|local>` | | Where the entry lands: every project (default), this repo for everyone, or just you |
128
+ | `--workspace <url_key\|name>` | | Pin the entry to one workspace (needs `levr auth login`) |
129
+ | `--name <entry>` | | Name of the client entry (default `levr`), to keep a second one beside it |
130
+ | `--replace` | | Switch an existing entry of this name to the new address |
131
+ | `--url <url>` | | MCP server URL (default derived from the API server) |
114
132
 
115
133
  Runs non-interactively whenever `--all`, `--client`, or `--yes` is passed —
116
134
  or automatically when not attached to a terminal (CI). Unknown client ids and
117
135
  failed writes exit non-zero.
118
136
 
137
+ ### More than one workspace
138
+
139
+ If your account is in more than one workspace, the plain MCP URL does not know
140
+ which one you mean, and its tools reply that no workspace is selected. Connect
141
+ each workspace by its own URL, `https://ai.levr.one/api/v1/mcp/w/<url_key>`:
142
+
143
+ ```bash
144
+ levr auth login
145
+ levr mcp add --workspace acme # pin the levr entry to acme
146
+ levr mcp add --workspace beta --name levr-beta # add a second entry beside it
147
+ levr mcp add --workspace beta --replace # or switch the entry to beta
148
+ ```
149
+
150
+ Each entry lists every Levr tool once more. A URL for a workspace you are not
151
+ in is refused with `403`; connect the plain URL and ask for `list_workspaces`
152
+ to see the URLs you can use. Full guide:
153
+ [levr CLI Overview › More than one workspace](https://doc.levr.one/cli/levr-cli-overview#more-than-one-workspace).
154
+
119
155
  ## Push test results
120
156
 
121
157
  ```bash
@@ -233,7 +269,7 @@ levr import ./cases.csv --team-id <uuid> --map "State=labels:state"
233
269
  env:
234
270
  LEVR_TOKEN: ${{ secrets.LEVR_TOKEN }}
235
271
  # LEVR_TEAM_ID is optional — server resolves from automation source or workspace default
236
- run: npx --yes @levr-one/cli push ./test-results.xml
272
+ run: npx --yes @levr-one/cli@latest push ./test-results.xml
237
273
  ```
238
274
 
239
275
  ### GitLab CI
@@ -241,7 +277,7 @@ levr import ./cases.csv --team-id <uuid> --map "State=labels:state"
241
277
  ```yaml
242
278
  push-results:
243
279
  script:
244
- - npx --yes @levr-one/cli push ./test-results.xml
280
+ - npx --yes @levr-one/cli@latest push ./test-results.xml
245
281
  variables:
246
282
  LEVR_TOKEN: $LEVR_TOKEN
247
283
  ```
@@ -250,13 +286,14 @@ push-results:
250
286
 
251
287
  ```groovy
252
288
  withEnv(["LEVR_TOKEN=${LEVR_TOKEN}"]) {
253
- sh 'npx --yes @levr-one/cli push ./test-results.xml'
289
+ sh 'npx --yes @levr-one/cli@latest push ./test-results.xml'
254
290
  }
255
291
  ```
256
292
 
257
293
  ## Authentication
258
294
 
259
- Needed for `push` and `workspace` commands (`mcp add` needs none). Three modes:
295
+ Needed for `push` and `workspace` commands, and for `mcp add --workspace`.
296
+ Three modes:
260
297
 
261
298
  ### Interactive (browser) — default
262
299
 
package/dist/cli.js CHANGED
@@ -135,7 +135,7 @@ or when not running in a terminal (CI). Config edits preserve existing
135
135
  servers and comments, and re-running is a no-op.
136
136
 
137
137
  Examples:
138
- npx @levr-one/cli mcp add # detect clients and pick interactively
138
+ npx @levr-one/cli@latest mcp add # detect clients and pick interactively
139
139
  levr mcp add --all # set up every detected client
140
140
  levr mcp add --client cursor --yes
141
141
  levr mcp add --scope project # commit the config to this repo
@@ -563,12 +563,17 @@ Examples:
563
563
  //#region src/commands/workspace/select.ts
564
564
  const selectCommand = buildCommand({
565
565
  docs: {
566
- brief: "Select a workspace",
566
+ brief: "Select the CLI workspace (MCP client configs are unchanged)",
567
567
  fullDescription: `Select a workspace by ID.
568
568
 
569
- The selected workspace is used for all subsequent commands.
569
+ The selected workspace is used for all subsequent levr commands.
570
570
  Use 'levr workspace list' to see available workspaces.
571
571
 
572
+ It does not change MCP client configs: each AI client keeps using the
573
+ workspace its MCP URL names. To point a client at a workspace, run
574
+ 'levr mcp add --workspace <url_key>' (add --replace to switch an existing
575
+ entry).
576
+
572
577
  Requires JWT authentication (levr auth login).
573
578
 
574
579
  Examples:
@@ -673,7 +678,7 @@ Examples:
673
678
  }
674
679
  },
675
680
  loader: async () => {
676
- const { gatesRunHandler } = await import("./runHandler-CCxnGJjS.js");
681
+ const { gatesRunHandler } = await import("./runHandler-9tfiD3ZK.js");
677
682
  return gatesRunHandler;
678
683
  }
679
684
  });
@@ -869,7 +874,7 @@ Examples:
869
874
 
870
875
  //#endregion
871
876
  //#region package.json
872
- var version = "0.11.1";
877
+ var version = "0.11.3";
873
878
 
874
879
  //#endregion
875
880
  //#region src/app.ts
@@ -88,6 +88,20 @@ const issueGateVerificationReportGateResultsV1 = (options) => {
88
88
  const GATE_STEP_TAIL_LIMIT = 4e3;
89
89
  /** How long one command may run before it is killed (15 min). */
90
90
  const GATE_COMMAND_TIMEOUT_MS = 900 * 1e3;
91
+ /**
92
+ * How long an L4 semantic judge may run (3 min) — ENG-4469.
93
+ *
94
+ * The L4 templates used to carry their own `timeout 120`, which bounded the
95
+ * judge on Linux and broke it on stock macOS (no GNU `timeout`). With the
96
+ * wrapper gone, the runner holds the budget, as qinetic-mcp's executor does
97
+ * with the same 180s. A hung judge (agy waiting on a prompt that never
98
+ * renders) would otherwise hold `levr gates run` for the full 15 minutes.
99
+ */
100
+ const GATE_SEMANTIC_TIMEOUT_MS = 180 * 1e3;
101
+ /** The kill budget for one command: semantic judges get the shorter one. */
102
+ function commandTimeoutMs(command) {
103
+ return command.technique === "semantic" ? GATE_SEMANTIC_TIMEOUT_MS : GATE_COMMAND_TIMEOUT_MS;
104
+ }
91
105
  /** Keep the END of a stream — where runner summaries and verdicts are. */
92
106
  function tail(value, limit = GATE_STEP_TAIL_LIMIT) {
93
107
  const s = value ?? "";
@@ -145,12 +159,12 @@ function shellExists(shell) {
145
159
  * can report ENOBUFS with a numeric status (ENG-5106 L1 review round 4
146
160
  * F-010/F-011).
147
161
  */
148
- const runInShell = (command) => {
162
+ const runInShell = (command, timeoutMs = GATE_COMMAND_TIMEOUT_MS) => {
149
163
  const shell = chooseShell();
150
164
  const r = spawnSync(shell.file, shell.args(command), {
151
165
  encoding: "utf8",
152
166
  maxBuffer: 64 * 1024 * 1024,
153
- timeout: GATE_COMMAND_TIMEOUT_MS
167
+ timeout: timeoutMs
154
168
  });
155
169
  const stderr = (r.stderr ?? "") + (r.error ? `\n[runner] ${r.error.message}` : "") + (r.signal ? `\n[runner] killed by ${r.signal}` : "");
156
170
  const killed = r.error !== void 0 || r.signal !== null;
@@ -189,7 +203,7 @@ function toReportResult(command, outcome) {
189
203
  function runLocalCommands(commands, run = runInShell, onEach, onBefore) {
190
204
  return commands.map((command, index) => {
191
205
  onBefore?.(command, index);
192
- const result = toReportResult(command, run(command.command));
206
+ const result = toReportResult(command, run(command.command, commandTimeoutMs(command)));
193
207
  onEach?.(command, result);
194
208
  return result;
195
209
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@levr-one/cli",
3
- "version": "0.11.1",
3
+ "version": "0.11.3",
4
4
  "description": "The command-line interface for Levr",
5
5
  "type": "module",
6
6
  "license": "MIT",