From 8139ca84c5059eb664f8f15c788b8d3415a1d8cd Mon Sep 17 00:00:00 2001 From: Aymeric Rabot Date: Fri, 7 Aug 2026 10:17:10 -0400 Subject: [PATCH] fix(cli): clarify npx and global commands --- packages/cli/README.md | 9 +++++ packages/cli/scripts/smoke-packed-runtime.ts | 12 ++++++ packages/cli/src/bin/pascal.ts | 42 ++++++++++---------- packages/cli/src/cli.test.ts | 2 + 4 files changed, 45 insertions(+), 20 deletions(-) diff --git a/packages/cli/README.md b/packages/cli/README.md index 27ef8a314..565d67f71 100644 --- a/packages/cli/README.md +++ b/packages/cli/README.md @@ -11,6 +11,11 @@ from your terminal—without cloning or building the Pascal repository. npx @pascal-app/cli editor ``` +`npx` makes the CLI available for that invocation only; it does not add a permanent +`pascal` command to your shell. Continue to prefix commands with +`npx @pascal-app/cli`, or use the optional global installation below when you want the +shorter command. + The first run walks through local storage, runtime installation, automatic port selection, process startup, and a health check with live terminal feedback. It then opens `http://pascal.localhost:`. Your projects are stored separately from the @@ -56,6 +61,10 @@ npm install --global @pascal-app/cli pascal editor ``` +After a global installation, `pascal status`, `pascal logs --follow`, and the other +commands work directly in new terminal sessions. A prior `npx` invocation alone does +not install this shortcut. + Use `--no-open` on a headless machine. Use `--foreground` when a process supervisor should own the editor or when you want logs attached to the current terminal. Pascal asks the operating system for an available loopback port by default, so it does diff --git a/packages/cli/scripts/smoke-packed-runtime.ts b/packages/cli/scripts/smoke-packed-runtime.ts index 44d8945d5..dc813b07b 100644 --- a/packages/cli/scripts/smoke-packed-runtime.ts +++ b/packages/cli/scripts/smoke-packed-runtime.ts @@ -66,6 +66,18 @@ try { ) { throw new Error('a repeated editor command did not reuse the managed process') } + const humanStart = await run( + process.execPath, + [smokeExecutable, 'editor', '--no-open'], + undefined, + smokeEnvironment, + ) + if ( + !humanStart.stdout.includes('npx @pascal-app/cli status') || + !humanStart.stdout.includes('npm install --global @pascal-app/cli') + ) { + throw new Error('human start output did not explain transient and global commands') + } await run( process.execPath, [smokeExecutable, 'project', 'list', '--json'], diff --git a/packages/cli/src/bin/pascal.ts b/packages/cli/src/bin/pascal.ts index 655bd2a10..0910238d8 100755 --- a/packages/cli/src/bin/pascal.ts +++ b/packages/cli/src/bin/pascal.ts @@ -22,6 +22,13 @@ import { version } from '../version.js' const HELP = `Pascal — local 3D editor +RUN WITHOUT INSTALLING: + npx @pascal-app/cli + +ENABLE THE SHORT GLOBAL COMMAND: + npm install --global @pascal-app/cli + pascal + USAGE: pascal editor [--foreground] [--no-open] [--port ] pascal start [--foreground] [--port ] @@ -93,7 +100,6 @@ async function runStart(args: string[], shouldOpen: boolean): Promise { if (values.help) return print(HELP) const port = parseIntegerOption(values.port, 'port') const progress = values.json ? undefined : new TerminalProgress() - let installedRuntime = false progress?.start('Preparing your local Pascal editor') let result: Awaited> try { @@ -101,12 +107,7 @@ async function runStart(args: string[], shouldOpen: boolean): Promise { paths, port, foreground: values.foreground, - onProgress: progress - ? (event) => { - if (event.step === 'runtime-installing') installedRuntime = true - reportStartProgress(progress, event) - } - : undefined, + onProgress: progress ? (event) => reportStartProgress(progress, event) : undefined, }) } catch (error) { progress?.stop() @@ -117,19 +118,20 @@ async function runStart(args: string[], shouldOpen: boolean): Promise { output( values.json, { ...result.state, alreadyRunning: result.alreadyRunning }, - result.alreadyRunning - ? `Pascal is already running at ${result.state.url}` - : installedRuntime - ? [ - `Pascal is ready at ${result.state.url}`, - `Projects stay in ${paths.data}`, - '', - 'Next steps:', - ' npx @pascal-app/cli status Check the local editor', - ' npx @pascal-app/cli logs --follow Follow editor logs', - ' npx @pascal-app/cli stop Stop the background process', - ].join('\n') - : `Pascal is running at ${result.state.url}`, + [ + result.alreadyRunning + ? `Pascal is already running at ${result.state.url}` + : `Pascal is ready at ${result.state.url}`, + `Projects stay in ${paths.data}`, + '', + 'Manage it with npx:', + ' npx @pascal-app/cli status Check the local editor', + ' npx @pascal-app/cli logs --follow Follow editor logs', + ' npx @pascal-app/cli stop Stop the background process', + '', + 'To enable the shorter "pascal" command in your shell:', + ' npm install --global @pascal-app/cli', + ].join('\n'), ) if (result.child) { const exitCode = await new Promise((resolve) => diff --git a/packages/cli/src/cli.test.ts b/packages/cli/src/cli.test.ts index 13ff37c1e..69acc999f 100644 --- a/packages/cli/src/cli.test.ts +++ b/packages/cli/src/cli.test.ts @@ -15,6 +15,8 @@ describe('command parsing', () => { expect(result.exitCode).toBe(0) expect(result.stdout).toContain('pascal editor') + expect(result.stdout).toContain('npx @pascal-app/cli ') + expect(result.stdout).toContain('npm install --global @pascal-app/cli') }) test('rejects a partially numeric port', async () => {