Wire the Sandbox Workflow into the CLI
We have two pieces. A lifecycle function that does the work, and a CLI that knows when to do it. Time to connect them.
This is one of those lessons where the code change is small but the moment is big. The first time you run pnpm review <url> and watch it boot a Sandbox, clone the repo, and print back a structured result is when the project stops feeling like a tutorial and starts feeling like a tool.
Outcome
Replace the console.log placeholder in the action callback with a real call to runSandboxLifecycle, and print the returned result.
Fast Track
- Import
runSandboxLifecyclefrom./sandbox-lifecycle. - Call it inside the validated
actioncallback. - Log the structured result.
Hands-on exercise
Open src/cli.ts and add the import:
import { Command } from 'commander';
import { runSandboxLifecycle } from './sandbox-lifecycle';
function isValidGitHubRepoUrl(input: string): boolean {
return /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/?$/.test(input);
}
const program = new Command();
program
.name('repo-review')
.description('Clone and review a GitHub repository in a Sandbox')
.version('0.1.0');
program
.command('review <repoUrl>')
.description('Run a Sandbox review against a GitHub repository URL')
.action(async (repoUrl: string) => {
if (!isValidGitHubRepoUrl(repoUrl)) {
console.error(`Invalid GitHub repository URL: ${repoUrl}`);
console.error('Expected format: https://github.com/<owner>/<repo>');
return;
}
console.log(`Reviewing ${repoUrl}...`);
const result = await runSandboxLifecycle(repoUrl);
console.log(`Sandbox: ${result.sandboxId}`);
console.log(`Clone exit code: ${result.cloneExitCode}`);
console.log(`Files:\n${result.files}`);
console.log(`README preview:\n${result.readmePreview}`);
});
program.parse();Notice we're not catching the error from runSandboxLifecycle yet. If the clone fails, the function throws, and we'll see an unhandled rejection in the terminal. We'll fix that in the next lesson when we add real exit-code handling. For now, the happy path is the goal.
Also: since runSandboxLifecycle is no longer being called from the bottom of src/sandbox-lifecycle.ts, you can delete the temporary main() test caller we added in lesson 1.4. The CLI is the real caller now.
TypeScript imports omit the .ts extension (./sandbox-lifecycle, not ./sandbox-lifecycle.ts). If you see "cannot find module", drop the extension.
Logging the full ls -la output is verbose. That's fine for now since we're confirming the wire is connected. We'll trim what gets printed when we add the proper reporter in Chapter 5.
Try It
pnpm review https://github.com/vercel/examplesExpected output:
Reviewing https://github.com/vercel/examples...
Sandbox: sbx_7N2k4A...
Clone exit code: 0
Files:
total 32
drwxr-xr-x ... README.md
drwxr-xr-x ... examples
README preview:
# Vercel Examples
This repository contains...That's the whole first half of the course in one CLI invocation. Validation, Sandbox boot, clone, file inspection, clean shutdown.
Commit
git add src/cli.ts src/sandbox-lifecycle.ts
git commit -m "feat(cli): wire sandbox lifecycle into the review command"Done-When
- CLI imports
runSandboxLifecyclefrom./sandbox-lifecycle - Valid URL triggers a real Sandbox run
- Invalid URL still exits early without booting a Sandbox
- Result fields (
sandboxId,cloneExitCode,files,readmePreview) all print
Solution
import { Command } from 'commander';
import { runSandboxLifecycle } from './sandbox-lifecycle';
function isValidGitHubRepoUrl(input: string): boolean {
return /^https:\/\/github\.com\/[\w.-]+\/[\w.-]+\/?$/.test(input);
}
const program = new Command();
program
.name('repo-review')
.description('Clone and review a GitHub repository in a Sandbox')
.version('0.1.0');
program
.command('review <repoUrl>')
.description('Run a Sandbox review against a GitHub repository URL')
.action(async (repoUrl: string) => {
if (!isValidGitHubRepoUrl(repoUrl)) {
console.error(`Invalid GitHub repository URL: ${repoUrl}`);
console.error('Expected format: https://github.com/<owner>/<repo>');
return;
}
console.log(`Reviewing ${repoUrl}...`);
const result = await runSandboxLifecycle(repoUrl);
console.log(`Sandbox: ${result.sandboxId}`);
console.log(`Clone exit code: ${result.cloneExitCode}`);
console.log(`Files:\n${result.files}`);
console.log(`README preview:\n${result.readmePreview}`);
});
program.parse();Was this helpful?