@labelbox/recursion-cli 0.0.46 → 0.0.48

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
@@ -1,14 +1,15 @@
1
1
  # @labelbox/recursion-cli
2
2
 
3
- `rl` — the Recursion CLI. It mirrors the TypeScript SDK
3
+ `recursion` — the Recursion CLI. It mirrors the TypeScript SDK
4
4
  (`@labelbox/recursion-sdk`) exactly: where the SDK is `rl.synthesizers.create(...)`, the
5
- CLI is `rl synthesizers create …`. Dots become spaces; you get `--help` at every
6
- level.
5
+ CLI is `recursion synthesizers create …`. Dots become spaces; you get `--help` at every
6
+ level. The short `rl` executable remains a permanently supported convenience alias and
7
+ runs the same command tree; documentation uses the canonical `recursion` spelling.
7
8
 
8
9
  ```sh
9
- rl --help # list nouns (synthesizers, synthesizer-runs)
10
- rl synthesizers --help # list verbs (create, get, list, …)
11
- rl synthesizers create --help # list flags
10
+ recursion --help # list nouns (synthesizers, synthesizer-runs)
11
+ recursion synthesizers --help # list verbs (create, get, list, …)
12
+ recursion synthesizers create --help # list flags
12
13
  ```
13
14
 
14
15
  ## How it works (fully live, zero per-operation code)
@@ -48,15 +49,15 @@ never falls back to a partial direct write.
48
49
 
49
50
  ### Docs browse surfaces
50
51
 
51
- Beyond the executable `rl <noun> <verb>` operations, the manifest carries the docs,
52
- exposed as one consistent positional shape — `rl <group> [<id>]` (bare lists, an id
52
+ Beyond the executable `recursion <noun> <verb>` operations, the manifest carries the docs,
53
+ exposed as one consistent positional shape — `recursion <group> [<id>]` (bare lists, an id
53
54
  shows that one):
54
55
 
55
56
  ```sh
56
- rl resources [<id>] # Reference — resource hubs: object shape, operations, recipes
57
- rl recipes [<id>] # How-to — multi-step user-goal guides (--format cli|ts|curl)
58
- rl explain [<concept>] # Explanation — concept pages
59
- rl tutorials [<id>] # Tutorials — getting-started docs
57
+ recursion resources [<id>] # Reference — resource hubs: object shape, operations, recipes
58
+ recursion recipes [<id>] # How-to — multi-step user-goal guides (--format cli|ts|curl)
59
+ recursion explain [<concept>] # Explanation — concept pages
60
+ recursion tutorials [<id>] # Tutorials — getting-started docs
60
61
  ```
61
62
 
62
63
  ## Install / run
@@ -66,9 +67,9 @@ setup beyond Node itself.
66
67
 
67
68
  ```sh
68
69
  npm uninstall -g @recursion/cli @labelbox/rl-cli # no-op unless a pre-rename package is installed
69
- npm install -g @labelbox/recursion-cli # they all own the global `rl` bin — EEXIST otherwise
70
+ npm install -g @labelbox/recursion-cli # old packages also own the `rl` alias — EEXIST otherwise
70
71
  # or, zero-install:
71
- npx @labelbox/recursion-cli --help
72
+ npx --package @labelbox/recursion-cli recursion --help
72
73
  ```
73
74
 
74
75
  The CLI is standalone, so this pulls in no `@labelbox/recursion-sdk`.
@@ -110,9 +111,9 @@ host with `--base-url <url>` (defaults to the public Recursion API gateway, see
110
111
  no-content operations).
111
112
 
112
113
  ```sh
113
- rl synthesizers get --synthesizer-job-id sj_01HX...
114
- rl synthesizers create --environment-id env_01HX... --from-json ./body.json
115
- rl synthesizer-runs trigger --problem-version-id pv_01HX... --from-json ./run.json
114
+ recursion synthesizers get --synthesizer-job-id sj_01HX...
115
+ recursion synthesizers create --environment-id env_01HX... --from-json ./body.json
116
+ recursion synthesizer-runs trigger --problem-version-id pv_01HX... --from-json ./run.json
116
117
  ```
117
118
 
118
119
  ## The command surface is the live server
package/dist/embed.d.ts CHANGED
@@ -8,7 +8,7 @@ export interface ExecuteCliOptions {
8
8
  apiKey: string;
9
9
  /** The API the CLI dispatches against. Server configuration, not caller input. */
10
10
  baseUrl: string;
11
- /** Reported by `rl --version`. */
11
+ /** Reported by `recursion --version`. */
12
12
  version: string;
13
13
  /** The pre-assembled manifest — served from memory, never re-fetched over HTTP. */
14
14
  manifest: Manifest;
@@ -31,7 +31,7 @@ export interface CliResult {
31
31
  */
32
32
  export declare function assertAllowedArgv(args: readonly string[]): void;
33
33
  /**
34
- * Run one `rl` invocation in-process and capture its output.
34
+ * Run one `recursion` invocation in-process and capture its output.
35
35
  *
36
36
  * Resolves for both success and failure — a non-zero exit is a normal outcome the
37
37
  * model should see and correct from, not an exception. It rejects only on a
package/dist/embed.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { run } from './run.js';
2
- // Running `rl` inside a server process, on behalf of an agent, without a shell.
2
+ // Running `recursion` inside a server process, on behalf of an agent, without a shell.
3
3
  //
4
4
  // The MCP endpoint exposes one tool whose argument is an argv array. This module
5
5
  // is the whole surface it needs: `executeCli` builds the argv, runs the same
@@ -91,11 +91,11 @@ export function assertAllowedArgv(args) {
91
91
  const command = args.find((arg) => !arg.startsWith('-'));
92
92
  if (command !== undefined && BLOCKED_COMMANDS.has(command)) {
93
93
  throw new Error(`"${command}" is not available here: it acts on a local developer checkout, ` +
94
- 'which this server does not have. Run it from your own terminal with the rl CLI.');
94
+ 'which this server does not have. Run it from your own terminal with the recursion CLI.');
95
95
  }
96
96
  }
97
97
  /**
98
- * Run one `rl` invocation in-process and capture its output.
98
+ * Run one `recursion` invocation in-process and capture its output.
99
99
  *
100
100
  * Resolves for both success and failure — a non-zero exit is a normal outcome the
101
101
  * model should see and correct from, not an exception. It rejects only on a
@@ -125,7 +125,7 @@ export async function executeCli(options) {
125
125
  // argv can outrank it.
126
126
  argv: [
127
127
  'node',
128
- 'rl',
128
+ 'recursion',
129
129
  `--api-key=${options.apiKey}`,
130
130
  `--base-url=${options.baseUrl}`,
131
131
  ...options.args,
package/dist/git-host.js CHANGED
@@ -128,7 +128,7 @@ function stashClaim(repoDir, claim) {
128
128
  function readStashedClaim(repoDir) {
129
129
  const path = claimFilePath(repoDir);
130
130
  if (!existsSync(path)) {
131
- throw new Error(`no claim found at ${path} — run \`rl scaffold\` in this directory first, or pass --path to point at a scaffolded repo`);
131
+ throw new Error(`no claim found at ${path} — run \`recursion scaffold\` in this directory first, or pass --path to point at a scaffolded repo`);
132
132
  }
133
133
  const parsed = StashedClaimSchema.safeParse(JSON.parse(readFileSync(path, 'utf8')));
134
134
  if (!parsed.success) {
package/dist/manifest.js CHANGED
@@ -159,7 +159,7 @@ export const ManifestOperationSchema = z.object({
159
159
  params: z.array(ManifestParamSchema),
160
160
  response: ShapeNodeSchema.optional(),
161
161
  });
162
- // ── Resources — the Reference browse surface (`rl resources [<id>]`) ──────────
162
+ // ── Resources — the Reference browse surface (`recursion resources [<id>]`) ──────────
163
163
  const ManifestResourceObjectSchema = z.object({
164
164
  name: z.string(),
165
165
  fields: z.array(ShapeNodeSchema),
@@ -175,14 +175,14 @@ export const ManifestResourceSchema = z.object({
175
175
  object: ManifestResourceObjectSchema.optional(),
176
176
  operationIds: z.array(z.string()),
177
177
  });
178
- // ── Recipes — the How-to browse surface (`rl recipes [<id>]`) ─────────────────
178
+ // ── Recipes — the How-to browse surface (`recursion recipes [<id>]`) ─────────────────
179
179
  const RecipeSnippetSchema = z.object({ setup: z.string(), main: z.string() });
180
180
  // Steps are read only to map a recipe to the resources it touches (via each SDK
181
181
  // step's operationId), so they're modeled loosely — just the fields the CLI uses.
182
182
  const ManifestRecipeStepSchema = z.object({ operationId: z.string().optional() });
183
183
  // A recipe's place in the relationship graph (mirrors `RecipeRelated` in
184
184
  // sdk-ts's recipes-schema). Modeled loosely here — the CLI only reads these to
185
- // render the "Related" / "Unblocks" block on `rl recipes <id>`; the generator
185
+ // render the "Related" / "Unblocks" block on `recursion recipes <id>`; the generator
186
186
  // is the source of truth that validates them. Without these fields the manifest
187
187
  // parse would silently strip `related`, so the CLI would never see the links.
188
188
  const ManifestLinkTargetSchema = z.object({
@@ -224,7 +224,7 @@ export const ManifestRecipeSchema = z.object({
224
224
  // The recipe's relationship links (optional — absent for an island recipe).
225
225
  related: ManifestRelatedSchema.optional(),
226
226
  });
227
- // ── Concepts — the Explanation browse surface (`rl explain [<concept>]`) ──────
227
+ // ── Concepts — the Explanation browse surface (`recursion explain [<concept>]`) ──────
228
228
  export const ManifestConceptSchema = z.object({
229
229
  id: z.string(),
230
230
  title: z.string(),
@@ -234,7 +234,7 @@ export const ManifestConceptSchema = z.object({
234
234
  // metadata — the frontend fetches the `.md` separately; the CLI bundles it).
235
235
  body: z.string(),
236
236
  });
237
- // ── Tutorials — the getting-started docs (`rl tutorials [<id>]`) ──────────────
237
+ // ── Tutorials — the getting-started docs (`recursion tutorials [<id>]`) ──────────────
238
238
  export const ManifestTutorialSchema = z.object({
239
239
  id: z.string(),
240
240
  title: z.string(),
@@ -279,7 +279,7 @@ export function parseManifest(value, source) {
279
279
  // means no pre-rename package (@labelbox/rl-cli, or the older
280
280
  // @recursion/cli) owns the `rl` bin. A pre-rename user sees the
281
281
  // pre-rename binary's own message instead.
282
- 'this `rl` CLI is out of date — upgrade it (`npm install -g @labelbox/recursion-cli`)'
282
+ 'this `recursion` CLI is out of date — upgrade it (`npm install -g @labelbox/recursion-cli`)'
283
283
  : 'the server is older than this CLI — point --base-url at an up-to-date server';
284
284
  throw new Error(`command-manifest format mismatch: this CLI speaks v${MANIFEST_FORMAT_VERSION}, ` +
285
285
  `${source} served v${formatVersion}. ${direction}.`);
@@ -460,7 +460,7 @@ function selectCache(baseUrl) {
460
460
  publishCacheIfAbsent(canonicalPath, legacy.raw);
461
461
  return readCache(canonicalPath) ?? legacy;
462
462
  }
463
- // Every `rl` invocation gates on this fetch, so it must be bounded: a server that
463
+ // Every `recursion` invocation gates on this fetch, so it must be bounded: a server that
464
464
  // accepts the connection but never responds (a hung gateway, a stalled captive
465
465
  // portal) would otherwise hang the CLI forever. A timeout makes `fetch` reject, so
466
466
  // the catch below degrades to the cached manifest (or the clear cold-cache error).
package/dist/program.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare function parseBodyBase(opts: Record<string, unknown>): Record<str
20
20
  * Where CLI output goes, and the reason nothing here touches `process` directly.
21
21
  *
22
22
  * The terminal entrypoint (`bin.ts`) binds these to the real streams. The MCP
23
- * endpoint binds them to string buffers so one `rl` invocation can be executed
23
+ * endpoint binds them to string buffers so one `recursion` invocation can be executed
24
24
  * **in-process** on behalf of an agent and its output returned as a tool result.
25
25
  * A stray `process.stdout.write` would leak an agent's output into the server's
26
26
  * logs; a stray `process.exit` would take the whole API server down mid-request.
@@ -86,7 +86,7 @@ export declare function leafShapeHelp(entry: ManifestOperation): string;
86
86
  * and `--data` is the single documented way to pass a body.
87
87
  */
88
88
  export declare function addOptions(command: Command, entry: ManifestOperation, fileFlags?: boolean): void;
89
- /** Output formats `rl recipes <id>` can render, mapped to the composed snippet field. */
89
+ /** Output formats `recursion recipes <id>` can render, mapped to the composed snippet field. */
90
90
  declare const RECIPE_FORMATS: {
91
91
  readonly cli: "cli";
92
92
  readonly ts: "sdk";
@@ -94,7 +94,7 @@ declare const RECIPE_FORMATS: {
94
94
  readonly curl: "curl";
95
95
  };
96
96
  export type RecipeFormat = keyof typeof RECIPE_FORMATS;
97
- /** The full catalog, grouped by category, for `rl recipes`. */
97
+ /** The full catalog, grouped by category, for `recursion recipes`. */
98
98
  export declare function renderRecipeList(reference: Record<string, ManifestRecipe>): string;
99
99
  /**
100
100
  * The "Related" + "Unblocks" block for a recipe — its place in the graph. The
@@ -104,10 +104,10 @@ export declare function renderRecipeList(reference: Record<string, ManifestRecip
104
104
  * and where it can go next. Returns `''` when the recipe is an island.
105
105
  */
106
106
  export declare function renderRelatedBlock(entry: ManifestRecipe, allRecipes: Record<string, ManifestRecipe>): string;
107
- /** One recipe rendered for `rl recipes <id>`: goal + composed code + related links + a docs link. */
107
+ /** One recipe rendered for `recursion recipes <id>`: goal + composed code + related links + a docs link. */
108
108
  export declare function renderRecipeShow(entry: ManifestRecipe, format: RecipeFormat, allRecipes: Record<string, ManifestRecipe>): string;
109
109
  /** The program shell — name, description, version, and the global options. */
110
110
  export declare function buildBaseProgram(version: string, io: CliIo): Command;
111
- /** Build the full `rl` program from a fetched manifest. */
111
+ /** Build the full `recursion` program from a fetched manifest. */
112
112
  export declare function buildProgram(manifest: Manifest, ctx: ProgramContext): Command;
113
113
  export {};
package/dist/program.js CHANGED
@@ -336,7 +336,7 @@ export function addOptions(command, entry, fileFlags = true) {
336
336
  // resource type as their request body, so `agent_id` is both the path
337
337
  // parameter and a property of the body schema. Commander throws when the same
338
338
  // flag is added twice, and that throw happens while the whole program is
339
- // being built, so one such operation takes down `rl` entirely rather than
339
+ // being built, so one such operation takes down `recursion` entirely rather than
340
340
  // just itself. Path and query win: they address the resource and are what the
341
341
  // URL is built from. The shadowed body field is still settable through
342
342
  // `--from-json` / `--data`.
@@ -386,15 +386,15 @@ export function addOptions(command, entry, fileFlags = true) {
386
386
  command.option('--data <json>', 'Inline JSON request body');
387
387
  }
388
388
  }
389
- // ── docs browse surfaces — one consistent shape: `rl <group> [<id>]` ─────────
389
+ // ── docs browse surfaces — one consistent shape: `recursion <group> [<id>]` ─────────
390
390
  //
391
391
  // All four read-only browse groups (resources / recipes / explain / tutorials)
392
392
  // take one positional shape: bare lists, an id shows that one. No `show`/`list`
393
- // verbs (those are the action layer's, `rl <noun> <verb>`). All data is the live
393
+ // verbs (those are the action layer's, `recursion <noun> <verb>`). All data is the live
394
394
  // manifest; these commands are pure presentation (no further network).
395
395
  // The Labelbox app host (not the recursion API base) — recipes/docs render in-app.
396
396
  const RECIPE_DOCS_BASE_URL = 'https://app.labelbox.com';
397
- /** Output formats `rl recipes <id>` can render, mapped to the composed snippet field. */
397
+ /** Output formats `recursion recipes <id>` can render, mapped to the composed snippet field. */
398
398
  const RECIPE_FORMATS = { cli: 'cli', ts: 'sdk', py: 'python', curl: 'curl' };
399
399
  function isRecipeFormat(value) {
400
400
  return Object.hasOwn(RECIPE_FORMATS, value);
@@ -421,7 +421,7 @@ function groupByDomain(entries, domainOf, domains) {
421
421
  const ACTIONS_HELP_GROUP = 'Actions:';
422
422
  const DOCS_HELP_GROUP = 'Documentation:';
423
423
  const GIT_HOST_HELP_GROUP = 'Coding tasks:';
424
- /** Nested resource groups (`rl problems --help`, etc.) — allowed subcommands. */
424
+ /** Nested resource groups (`recursion problems --help`, etc.) — allowed subcommands. */
425
425
  const COMMANDS_HELP_GROUP = 'Commands';
426
426
  /** Nested resource groups — subcommands the caller can't run (separate help section). */
427
427
  const UNAVAILABLE_HELP_GROUP = 'Unavailable (missing permission)';
@@ -463,7 +463,7 @@ function resolveResourceDomain(resource, byId) {
463
463
  }
464
464
  return undefined;
465
465
  }
466
- /** The `rl <callPath>` invocation for an operation, with its required-flag hints. */
466
+ /** The `recursion <callPath>` invocation for an operation, with its required-flag hints. */
467
467
  function operationInvocation(op) {
468
468
  const flags = [];
469
469
  for (const param of op.params) {
@@ -475,12 +475,12 @@ function operationInvocation(op) {
475
475
  if (op.bodyKey)
476
476
  flags.push('--data <json>');
477
477
  // kebab each segment so the printed command matches the real one (the tree is
478
- // built from `kebab(segment)`) — `rl environments attach-external-id`, not the
478
+ // built from `kebab(segment)`) — `recursion environments attach-external-id`, not the
479
479
  // camelCase callPath.
480
480
  const command = op.callPath.map(kebab).join(' ');
481
- return `rl ${command}${flags.length ? ` ${flags.join(' ')}` : ''}`;
481
+ return `recursion ${command}${flags.length ? ` ${flags.join(' ')}` : ''}`;
482
482
  }
483
- /** `rl resources [<id>]` — the Reference hub (grouped browse, or one resource). */
483
+ /** `recursion resources [<id>]` — the Reference hub (grouped browse, or one resource). */
484
484
  function addResourcesCommand(program, manifest, io) {
485
485
  const byId = manifest.resources;
486
486
  program
@@ -493,11 +493,11 @@ function addResourcesCommand(program, manifest, io) {
493
493
  return;
494
494
  }
495
495
  // `Object.hasOwn`, not `byId[id]`: a Zod `z.record` is a plain object, so a bare
496
- // read walks the prototype chain — `rl resources constructor` would resolve to a
496
+ // read walks the prototype chain — `recursion resources constructor` would resolve to a
497
497
  // truthy inherited member, bypass the not-found guard, and crash in the renderer.
498
498
  const entry = Object.hasOwn(byId, id) ? byId[id] : undefined;
499
499
  if (!entry) {
500
- throw new Error(`unknown resource "${id}". Run \`rl resources\` to list resources.`);
500
+ throw new Error(`unknown resource "${id}". Run \`recursion resources\` to list resources.`);
501
501
  }
502
502
  io.stdout(renderResourceShow(entry, manifest));
503
503
  });
@@ -506,7 +506,7 @@ function renderResourceList(manifest) {
506
506
  const byId = manifest.resources;
507
507
  const all = Object.values(byId);
508
508
  const groups = groupByDomain(all, (r) => resolveResourceDomain(r, byId), manifest.domains);
509
- const lines = ['Resources — run `rl resources <id>` for the full overview.', ''];
509
+ const lines = ['Resources — run `recursion resources <id>` for the full overview.', ''];
510
510
  for (const group of groups) {
511
511
  lines.push(group.title);
512
512
  const sorted = group.entries.sort((a, b) => a.order - b.order || a.id.localeCompare(b.id, 'en'));
@@ -549,7 +549,7 @@ function renderResourceShow(resource, manifest) {
549
549
  }
550
550
  return `${parts.join('\n')}\n`;
551
551
  }
552
- /** `rl recipes [<id>]` — How-to (list grouped by category, or one recipe). */
552
+ /** `recursion recipes [<id>]` — How-to (list grouped by category, or one recipe). */
553
553
  function addRecipesCommand(program, manifest, io) {
554
554
  program
555
555
  .command('recipes [id]')
@@ -564,7 +564,7 @@ function addRecipesCommand(program, manifest, io) {
564
564
  // Object.hasOwn guards the prototype chain (see the resources hub above).
565
565
  const entry = Object.hasOwn(manifest.recipes, id) ? manifest.recipes[id] : undefined;
566
566
  if (!entry) {
567
- throw new Error(`unknown recipe "${id}". Run \`rl recipes\` to see all recipes.`);
567
+ throw new Error(`unknown recipe "${id}". Run \`recursion recipes\` to see all recipes.`);
568
568
  }
569
569
  const format = opts.format ?? 'cli';
570
570
  // `Object.hasOwn`, not `format in RECIPE_FORMATS`: `in` walks the prototype
@@ -577,7 +577,7 @@ function addRecipesCommand(program, manifest, io) {
577
577
  io.stdout(renderRecipeShow(entry, format, manifest.recipes));
578
578
  });
579
579
  }
580
- /** The full catalog, grouped by category, for `rl recipes`. */
580
+ /** The full catalog, grouped by category, for `recursion recipes`. */
581
581
  export function renderRecipeList(reference) {
582
582
  const entries = Object.values(reference);
583
583
  if (entries.length === 0)
@@ -588,7 +588,7 @@ export function renderRecipeList(reference) {
588
588
  group.push(entry);
589
589
  byCategory.set(entry.category, group);
590
590
  }
591
- const lines = ['Recipes — run `rl recipes <id>` for the full walkthrough.', ''];
591
+ const lines = ['Recipes — run `recursion recipes <id>` for the full walkthrough.', ''];
592
592
  for (const category of [...byCategory.keys()].sort()) {
593
593
  lines.push(category);
594
594
  const group = byCategory.get(category) ?? [];
@@ -649,7 +649,7 @@ export function renderRelatedBlock(entry, allRecipes) {
649
649
  }
650
650
  return lines.length > 0 ? `Related\n${lines.join('\n')}\n` : '';
651
651
  }
652
- /** One recipe rendered for `rl recipes <id>`: goal + composed code + related links + a docs link. */
652
+ /** One recipe rendered for `recursion recipes <id>`: goal + composed code + related links + a docs link. */
653
653
  export function renderRecipeShow(entry, format, allRecipes) {
654
654
  // Optional on the wire: an older server predates the Python surface, so
655
655
  // `--format py` renders the note rather than crashing on a missing snippet.
@@ -672,7 +672,7 @@ export function renderRecipeShow(entry, format, allRecipes) {
672
672
  '',
673
673
  ].join('\n');
674
674
  }
675
- /** `rl explain [<concept>]` — Explanation (list grouped by domain, or one page). */
675
+ /** `recursion explain [<concept>]` — Explanation (list grouped by domain, or one page). */
676
676
  function addExplainCommand(program, manifest, io) {
677
677
  program
678
678
  .command('explain [concept]')
@@ -688,14 +688,17 @@ function addExplainCommand(program, manifest, io) {
688
688
  ? manifest.concepts[concept]
689
689
  : undefined;
690
690
  if (!entry) {
691
- throw new Error(`unknown concept "${concept}". Run \`rl explain\` to list concepts.`);
691
+ throw new Error(`unknown concept "${concept}". Run \`recursion explain\` to list concepts.`);
692
692
  }
693
693
  io.stdout(renderConceptShow(entry));
694
694
  });
695
695
  }
696
696
  function renderConceptList(manifest) {
697
697
  const groups = groupByDomain(Object.values(manifest.concepts), (c) => c.domain, manifest.domains);
698
- const lines = ['Explanations — run `rl explain <concept>` for the full page.', ''];
698
+ const lines = [
699
+ 'Explanations — run `recursion explain <concept>` for the full page.',
700
+ '',
701
+ ];
699
702
  for (const group of groups) {
700
703
  lines.push(group.title);
701
704
  for (const c of group.entries.sort((a, b) => a.id.localeCompare(b.id, 'en'))) {
@@ -713,7 +716,7 @@ function renderConceptShow(concept) {
713
716
  }
714
717
  return `${parts.join('\n')}\n`;
715
718
  }
716
- /** `rl tutorials [<id>]` — Tutorials (the existing getting-started docs). */
719
+ /** `recursion tutorials [<id>]` — Tutorials (the existing getting-started docs). */
717
720
  function addTutorialsCommand(program, manifest, io) {
718
721
  program
719
722
  .command('tutorials [id]')
@@ -721,7 +724,10 @@ function addTutorialsCommand(program, manifest, io) {
721
724
  .description('Browse getting-started tutorials (run without an argument to list all)')
722
725
  .action((id) => {
723
726
  if (id === undefined) {
724
- const lines = ['Tutorials — run `rl tutorials <id>` for the full text.', ''];
727
+ const lines = [
728
+ 'Tutorials — run `recursion tutorials <id>` for the full text.',
729
+ '',
730
+ ];
725
731
  for (const t of manifest.tutorials)
726
732
  lines.push(` ${t.id} — ${t.title}`);
727
733
  io.stdout(`${lines.join('\n')}\n`);
@@ -729,7 +735,7 @@ function addTutorialsCommand(program, manifest, io) {
729
735
  }
730
736
  const entry = manifest.tutorials.find((t) => t.id === id);
731
737
  if (!entry) {
732
- throw new Error(`unknown tutorial "${id}". Run \`rl tutorials\` to list tutorials.`);
738
+ throw new Error(`unknown tutorial "${id}". Run \`recursion tutorials\` to list tutorials.`);
733
739
  }
734
740
  if (entry.body === null) {
735
741
  io.stdout(`${entry.title}\n\nThis tutorial is a notebook — open it in the app: ` +
@@ -741,7 +747,7 @@ function addTutorialsCommand(program, manifest, io) {
741
747
  }
742
748
  /** The program shell — name, description, version, and the global options. */
743
749
  export function buildBaseProgram(version, io) {
744
- return (new Command('rl')
750
+ return (new Command('recursion')
745
751
  // Route commander's own output (help text, unknown-command errors, invalid
746
752
  // option values) through the caller's sinks, and turn its `process.exit`
747
753
  // calls into thrown `CommanderError`s.
@@ -753,13 +759,13 @@ export function buildBaseProgram(version, io) {
753
759
  // process streams and still calling `process.exit`.
754
760
  .configureOutput({ writeOut: io.stdout, writeErr: io.stderr })
755
761
  .exitOverride()
756
- .description('Command-line interface for the Recursion RL platform')
762
+ .description('Command-line interface for the Recursion Platform')
757
763
  .version(version)
758
764
  .option('--api-key <key>', 'API key (defaults to the LABELBOX_API_KEY env var)')
759
765
  .option('--base-url <url>', 'Recursion API base URL (defaults to the RECURSION_BASE_URL env var, then the production host)')
760
766
  .option('--quiet', 'Output only the resulting resource id (JSON otherwise)'));
761
767
  }
762
- /** Build the full `rl` program from a fetched manifest. */
768
+ /** Build the full `recursion` program from a fetched manifest. */
763
769
  export function buildProgram(manifest, ctx) {
764
770
  // `ctx` structurally satisfies CliIo, so the root program — and by inheritance
765
771
  // every command built below — writes to the caller's sinks and never exits.
@@ -859,7 +865,7 @@ export function buildProgram(manifest, ctx) {
859
865
  for (const [parent, gatedCount] of gatedCountByParent) {
860
866
  parent.addHelpText('before', permissionGroupBanner(gatedCount));
861
867
  }
862
- // The docs browse groups + the bespoke `rl skills` action group.
868
+ // The docs browse groups + the bespoke `recursion skills` action group.
863
869
  addResourcesCommand(program, manifest, ctx);
864
870
  addRecipesCommand(program, manifest, ctx);
865
871
  addExplainCommand(program, manifest, ctx);
@@ -873,7 +879,7 @@ export function buildProgram(manifest, ctx) {
873
879
  // Lazy implicit help skips _initCommandGroup unless created via helpCommand(true).
874
880
  program.helpCommand(true);
875
881
  program.addHelpText('after', `
876
- Run \`rl <command> --help\` for details and flags on any command.
877
- Browse the full reference with \`rl resources\`, or guided walkthroughs with \`rl recipes\`.`);
882
+ Run \`recursion <command> --help\` for details and flags on any command.
883
+ Browse the full reference with \`recursion resources\`, or guided walkthroughs with \`recursion recipes\`.`);
878
884
  return program;
879
885
  }
package/dist/resolve.d.ts CHANGED
@@ -4,7 +4,7 @@ export declare function flagValue(argv: string[], name: string): string | undefi
4
4
  export declare function resolveApiKey(argv: string[]): string | undefined;
5
5
  /**
6
6
  * The base URL from the environment: the current `RECURSION_BASE_URL`, else the
7
- * deprecated `RL_GYM_BASE_URL`. Shared by `resolveBaseUrl` here and the `rl skills`
7
+ * deprecated `RL_GYM_BASE_URL`. Shared by `resolveBaseUrl` here and the `recursion skills`
8
8
  * resolver so the precedence lives — and is tested — in one place.
9
9
  */
10
10
  export declare function envBaseUrl(): string | undefined;
package/dist/resolve.js CHANGED
@@ -13,7 +13,7 @@ export function flagValue(argv, name) {
13
13
  const index = argv.indexOf(`--${name}`);
14
14
  if (index >= 0) {
15
15
  // The space-form value is the next token — but only if it isn't itself an
16
- // option (`rl --api-key --quiet`): a dangling `--api-key` has no value, so we
16
+ // option (`recursion --api-key --quiet`): a dangling `--api-key` has no value, so we
17
17
  // return undefined and let the env fallback apply rather than silently
18
18
  // consuming `--quiet` as a bogus (non-empty) key. Values that legitimately
19
19
  // start with `-` use the `--name=value` form above.
@@ -30,7 +30,7 @@ export function resolveApiKey(argv) {
30
30
  }
31
31
  /**
32
32
  * The base URL from the environment: the current `RECURSION_BASE_URL`, else the
33
- * deprecated `RL_GYM_BASE_URL`. Shared by `resolveBaseUrl` here and the `rl skills`
33
+ * deprecated `RL_GYM_BASE_URL`. Shared by `resolveBaseUrl` here and the `recursion skills`
34
34
  * resolver so the precedence lives — and is tested — in one place.
35
35
  */
36
36
  export function envBaseUrl() {
package/dist/run.d.ts CHANGED
@@ -31,7 +31,7 @@ export interface RunDeps {
31
31
  *
32
32
  * **Never throws and never exits.** It returns the exit code the caller should
33
33
  * use, having already rendered any failure to `deps.stderr`. That contract is
34
- * what lets the MCP endpoint run one `rl` invocation in-process on behalf of an
34
+ * what lets the MCP endpoint run one `recursion` invocation in-process on behalf of an
35
35
  * agent: a thrown error would become a 500 instead of a tool result, and a
36
36
  * `process.exit` would take the API server down mid-request. `bin.ts` is the only
37
37
  * caller that turns the returned code back into a real exit.
package/dist/run.js CHANGED
@@ -12,7 +12,7 @@ const MISSING_KEY_MESSAGE = 'missing API key — set LABELBOX_API_KEY or pass --
12
12
  *
13
13
  * **Never throws and never exits.** It returns the exit code the caller should
14
14
  * use, having already rendered any failure to `deps.stderr`. That contract is
15
- * what lets the MCP endpoint run one `rl` invocation in-process on behalf of an
15
+ * what lets the MCP endpoint run one `recursion` invocation in-process on behalf of an
16
16
  * agent: a thrown error would become a 500 instead of a tool result, and a
17
17
  * `process.exit` would take the API server down mid-request. `bin.ts` is the only
18
18
  * caller that turns the returned code back into a real exit.
package/dist/skills.d.ts CHANGED
@@ -41,7 +41,7 @@ export declare function listSkills(opts: {
41
41
  apiKey?: string | undefined;
42
42
  baseUrl: string;
43
43
  }): Promise<SkillSummary[]>;
44
- /** Programmatic core of `rl skills check` — fetch latest, read installed, compare. */
44
+ /** Programmatic core of `recursion skills check` — fetch latest, read installed, compare. */
45
45
  export declare function checkSkill(name: string, opts: {
46
46
  apiKey: string;
47
47
  baseUrl: string;
@@ -53,7 +53,7 @@ export declare function checkSkill(name: string, opts: {
53
53
  /** Raised when `install` refuses to clobber a locally-modified file (no `--force`). */
54
54
  export declare class HandEditedError extends Error {
55
55
  }
56
- /** Programmatic core of `rl skills install` — fetch latest and write it (guarded). */
56
+ /** Programmatic core of `recursion skills install` — fetch latest and write it (guarded). */
57
57
  export declare function installSkill(name: string, opts: {
58
58
  apiKey: string;
59
59
  baseUrl: string;
package/dist/skills.js CHANGED
@@ -6,14 +6,14 @@ import process from 'node:process';
6
6
  import { z } from 'zod';
7
7
  import { DEFAULT_BASE_URL, supportUrl } from './manifest.js';
8
8
  import { envBaseUrl } from './resolve.js';
9
- // ── bespoke `rl skills` group (hand-written, not spec-derived) ────────────────
9
+ // ── bespoke `recursion skills` group (hand-written, not spec-derived) ────────────────
10
10
  //
11
11
  // Keeps a downloaded Claude skill current. The platform serves each skill from
12
12
  // `GET /skills/:skillName` with a content-hash version; this group fetches that,
13
13
  // compares it to the installed `~/.claude/skills/<name>/SKILL.md`, and refreshes
14
- // it. The skill's own doctor preflight runs `rl skills check` each session and
15
- // refreshes via `rl skills install` when stale. Deliberately NOT an `@SdkRoute`
16
- // endpoint — that would auto-generate a colliding `rl skills` command.
14
+ // it. The skill's own doctor preflight runs `recursion skills check` each session and
15
+ // refreshes via `recursion skills install` when stale. Deliberately NOT an `@SdkRoute`
16
+ // endpoint — that would auto-generate a colliding `recursion skills` command.
17
17
  /** The skill installed by default when no name is given. */
18
18
  const DEFAULT_SKILL = 'recursion';
19
19
  // ── hash contract — MUST stay byte-identical to apps/recursion/api/src/skills/skill-hash.ts ──
@@ -79,9 +79,9 @@ function resolveAuth(program) {
79
79
  }
80
80
  return { apiKey, baseUrl: resolveBaseUrl(program) };
81
81
  }
82
- // Each `rl skills` fetch is bounded for the same reason as the manifest fetch
82
+ // Each `recursion skills` fetch is bounded for the same reason as the manifest fetch
83
83
  // (see manifest.ts): a server that accepts the connection but never responds would
84
- // otherwise hang `rl skills check`/`install`/`list` indefinitely. The timeout makes
84
+ // otherwise hang `recursion skills check`/`install`/`list` indefinitely. The timeout makes
85
85
  // `fetch` reject so the caller surfaces a clear error rather than blocking forever.
86
86
  const SKILLS_FETCH_TIMEOUT_MS = 30_000;
87
87
  /**
@@ -143,7 +143,7 @@ function readInstalled(path) {
143
143
  return undefined;
144
144
  }
145
145
  }
146
- /** Programmatic core of `rl skills check` — fetch latest, read installed, compare. */
146
+ /** Programmatic core of `recursion skills check` — fetch latest, read installed, compare. */
147
147
  export async function checkSkill(name, opts) {
148
148
  const latest = await fetchSkill(name, opts.apiKey, opts.baseUrl);
149
149
  const installed = readInstalled(installedPath(name, opts.skillFile));
@@ -152,7 +152,7 @@ export async function checkSkill(name, opts) {
152
152
  /** Raised when `install` refuses to clobber a locally-modified file (no `--force`). */
153
153
  export class HandEditedError extends Error {
154
154
  }
155
- /** Programmatic core of `rl skills install` — fetch latest and write it (guarded). */
155
+ /** Programmatic core of `recursion skills install` — fetch latest and write it (guarded). */
156
156
  export async function installSkill(name, opts) {
157
157
  const latest = await fetchSkill(name, opts.apiKey, opts.baseUrl);
158
158
  const path = installedPath(name, opts.skillFile);
@@ -197,7 +197,7 @@ export function addSkillsCommands(program, docsHelpGroup) {
197
197
  return;
198
198
  }
199
199
  const lines = available.map((s) => s.description ? ` ${s.name} — ${s.description}` : ` ${s.name}`);
200
- process.stdout.write(`Installable skills (run \`rl skills install <name>\`):\n${lines.join('\n')}\n`);
200
+ process.stdout.write(`Installable skills (run \`recursion skills install <name>\`):\n${lines.join('\n')}\n`);
201
201
  }
202
202
  catch (err) {
203
203
  process.stderr.write(`error: ${err instanceof Error ? err.message : String(err)}\n`);
@@ -218,7 +218,7 @@ export function addSkillsCommands(program, docsHelpGroup) {
218
218
  return;
219
219
  }
220
220
  const lead = status === 'missing' ? 'is not installed' : 'is out of date';
221
- process.stdout.write(`${skill} ${lead} — run \`rl skills install ${skill}\`.\n`);
221
+ process.stdout.write(`${skill} ${lead} — run \`recursion skills install ${skill}\`.\n`);
222
222
  process.exit(1);
223
223
  }
224
224
  catch (err) {
package/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@labelbox/recursion-cli",
3
- "version": "0.0.46",
3
+ "version": "0.0.48",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/Labelbox/recursion-platform.git"
8
8
  },
9
9
  "bin": {
10
+ "recursion": "./dist/bin.js",
10
11
  "rl": "./dist/bin.js"
11
12
  },
12
13
  "files": [