refactor(cli): Replace pkg with esbuild for smaller builds

Migrate from @yao-pkg/pkg (standalone binaries) to esbuild (bundled JS).
This removes ~46 packages from the dependency tree and produces a 174KB
bundle instead of ~50MB per-platform binaries.

Users of the CLI already have Node.js installed (required for Claude Code),
so standalone binaries are unnecessary overhead.

Changes:
- Remove @yao-pkg/pkg, add esbuild
- Add scripts/build-cli.mjs for esbuild bundling
- Update package.json build scripts and extraResources config
- Update README with new installation instructions
- Include batch-processor improvements from linter

Session: 472b48d8-4774-4955-8c98-2c5b6294beb5
This commit is contained in:
Pedram Amini
2025-12-05 17:05:17 -06:00
parent 5cd591f8ce
commit d2534e89d6
5 changed files with 676 additions and 771 deletions

View File

@@ -346,21 +346,29 @@ You can run separate batch processes in different Maestro sessions simultaneousl
## Command Line Interface
Maestro includes a CLI tool (`maestro-playbook`) for running playbooks from the command line, cron jobs, or CI/CD pipelines. The CLI is a standalone binary that requires no additional dependencies.
Maestro includes a CLI tool (`maestro-playbook`) for running playbooks from the command line, cron jobs, or CI/CD pipelines. The CLI requires Node.js (which you already have if you're using Claude Code).
### Installation
The CLI binary is bundled with Maestro. After installation, create a symlink to add it to your PATH:
The CLI is bundled with Maestro as a JavaScript file. Create a shell wrapper to run it:
```bash
# macOS (after installing Maestro.app)
sudo ln -sf "/Applications/Maestro.app/Contents/Resources/maestro-playbook" /usr/local/bin/maestro-playbook
echo '#!/bin/bash\nnode "/Applications/Maestro.app/Contents/Resources/maestro-playbook.js" "$@"' | sudo tee /usr/local/bin/maestro-playbook && sudo chmod +x /usr/local/bin/maestro-playbook
# Windows (run as Administrator in PowerShell)
# The binary is located at: C:\Program Files\Maestro\resources\maestro-playbook.exe
# Linux (deb/rpm installs to /opt)
echo '#!/bin/bash\nnode "/opt/Maestro/resources/maestro-playbook.js" "$@"' | sudo tee /usr/local/bin/maestro-playbook && sudo chmod +x /usr/local/bin/maestro-playbook
# Linux (AppImage - extract first, or use deb/rpm which installs to /opt)
sudo ln -sf "/opt/Maestro/resources/maestro-playbook" /usr/local/bin/maestro-playbook
# Windows (PowerShell as Administrator) - create a batch file
@"
@echo off
node "%ProgramFiles%\Maestro\resources\maestro-playbook.js" %*
"@ | Out-File -FilePath "$env:ProgramFiles\Maestro\maestro-playbook.cmd" -Encoding ASCII
```
Alternatively, run directly with Node.js:
```bash
node "/Applications/Maestro.app/Contents/Resources/maestro-playbook.js" list groups
```
### Usage

1318
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,17 +13,7 @@
"url": "https://github.com/yourusername/maestro.git"
},
"bin": {
"maestro-playbook": "./dist/cli/cli/index.js"
},
"pkg": {
"scripts": "dist/cli/**/*.js",
"targets": [
"node20-macos-arm64",
"node20-macos-x64",
"node20-win-x64",
"node20-linux-x64"
],
"outputPath": "dist/cli/bin"
"maestro-playbook": "./dist/cli/maestro-playbook.js"
},
"scripts": {
"dev": "concurrently \"npm run dev:main\" \"npm run dev:renderer\"",
@@ -33,14 +23,13 @@
"generate:pwa-icons": "node scripts/generate-pwa-icons.mjs",
"build": "npm run build:main && npm run build:renderer && npm run build:web && npm run build:cli",
"build:main": "tsc -p tsconfig.main.json",
"build:cli": "tsc -p tsconfig.cli.json",
"build:cli:bin": "npm run build:cli && pkg dist/cli/cli/index.js --config package.json",
"build:cli": "node scripts/build-cli.mjs",
"build:renderer": "vite build",
"build:web": "vite build --config vite.config.web.mts",
"package": "node scripts/set-version.mjs npm run build && npm run build:cli:bin && node scripts/set-version.mjs electron-builder --mac --win --linux",
"package:mac": "node scripts/set-version.mjs npm run build && npm run build:cli:bin && node scripts/set-version.mjs electron-builder --mac",
"package:win": "node scripts/set-version.mjs npm run build && npm run build:cli:bin && node scripts/set-version.mjs electron-builder --win",
"package:linux": "node scripts/set-version.mjs npm run build && npm run build:cli:bin && node scripts/set-version.mjs electron-builder --linux",
"package": "node scripts/set-version.mjs npm run build && node scripts/set-version.mjs electron-builder --mac --win --linux",
"package:mac": "node scripts/set-version.mjs npm run build && node scripts/set-version.mjs electron-builder --mac",
"package:win": "node scripts/set-version.mjs npm run build && node scripts/set-version.mjs electron-builder --win",
"package:linux": "node scripts/set-version.mjs npm run build && node scripts/set-version.mjs electron-builder --linux",
"start": "electron .",
"clean": "rm -rf dist release node_modules/.vite",
"postinstall": "electron-rebuild -f -w node-pty"
@@ -78,8 +67,8 @@
"icon": "build/icon.icns",
"extraResources": [
{
"from": "dist/cli/bin/maestro-macos-${arch}",
"to": "maestro-playbook"
"from": "dist/cli/maestro-playbook.js",
"to": "maestro-playbook.js"
}
]
},
@@ -101,8 +90,8 @@
"icon": "build/icon.ico",
"extraResources": [
{
"from": "dist/cli/bin/maestro-win-x64.exe",
"to": "maestro-playbook.exe"
"from": "dist/cli/maestro-playbook.js",
"to": "maestro-playbook.js"
}
]
},
@@ -116,8 +105,8 @@
"icon": "build/icon.png",
"extraResources": [
{
"from": "dist/cli/bin/maestro-linux-x64",
"to": "maestro-playbook"
"from": "dist/cli/maestro-playbook.js",
"to": "maestro-playbook.js"
}
]
},
@@ -166,13 +155,13 @@
"@types/react-syntax-highlighter": "^15.5.13",
"@types/ws": "^8.5.10",
"@vitejs/plugin-react": "^4.2.1",
"@yao-pkg/pkg": "^6.10.1",
"autoprefixer": "^10.4.16",
"canvas": "^3.2.0",
"concurrently": "^8.2.2",
"electron": "^28.1.0",
"electron-builder": "^24.9.1",
"electron-rebuild": "^3.2.9",
"esbuild": "^0.24.2",
"lucide-react": "^0.303.0",
"postcss": "^8.4.33",
"react": "^18.2.0",

49
scripts/build-cli.mjs Normal file
View File

@@ -0,0 +1,49 @@
#!/usr/bin/env node
/**
* Build script for the Maestro CLI using esbuild.
*
* Bundles the CLI into a single JavaScript file that can be run with Node.js.
* Users of this CLI already have Node.js installed (required for Claude Code),
* so we don't need standalone binaries.
*/
import * as esbuild from 'esbuild';
import * as fs from 'fs';
import * as path from 'path';
import { fileURLToPath } from 'url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const rootDir = path.resolve(__dirname, '..');
const outfile = path.join(rootDir, 'dist/cli/maestro-playbook.js');
async function build() {
console.log('Building CLI with esbuild...');
try {
await esbuild.build({
entryPoints: [path.join(rootDir, 'src/cli/index.ts')],
bundle: true,
platform: 'node',
target: 'node20',
outfile,
format: 'cjs',
sourcemap: true,
minify: false, // Keep readable for debugging
// Note: shebang is already in src/cli/index.ts, no banner needed
external: [],
});
// Make the output executable
fs.chmodSync(outfile, 0o755);
const stats = fs.statSync(outfile);
const sizeKB = (stats.size / 1024).toFixed(1);
console.log(`✓ Built ${outfile} (${sizeKB} KB)`);
} catch (error) {
console.error('Build failed:', error);
process.exit(1);
}
}
build();

View File

@@ -298,20 +298,23 @@ export async function* runPlaybook(
};
// Substitute template variables in the prompt
const finalPrompt = substituteTemplateVariables(playbook.prompt, templateContext);
const basePrompt = substituteTemplateVariables(playbook.prompt, templateContext);
// Read document content and expand template variables in it
const { content: docContent } = readDocAndCountTasks(folderPath, docEntry.filename);
if (docContent) {
const expandedDocContent = substituteTemplateVariables(docContent, templateContext);
// Write the expanded content back to the document temporarily
// (Claude will read this file, so it needs the expanded variables)
if (expandedDocContent !== docContent) {
writeDoc(folderPath, `${docEntry.filename}.md`, expandedDocContent);
}
const expandedDocContent = docContent
? substituteTemplateVariables(docContent, templateContext)
: '';
// Write expanded content back to document (so agent edits have correct paths)
if (expandedDocContent && expandedDocContent !== docContent) {
writeDoc(folderPath, `${docEntry.filename}.md`, expandedDocContent);
}
// Spawn agent
// Combine prompt with document content - agent works on what it's given
const finalPrompt = `${basePrompt}\n\n---\n\n# ${docEntry.filename}.md\n\n${expandedDocContent}`;
// Spawn agent with combined prompt + document
const result = await spawnAgent(session.cwd, finalPrompt);
const elapsedMs = Date.now() - taskStartTime;