eve-app-cli

Build domain-specific CLIs for Eve-compatible apps so agents interact via commands instead of raw REST calls.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "eve-app-cli" with this command: npx skills add incept5/eve-skillpacks/incept5-eve-skillpacks-eve-app-cli

Eve App CLI

Build domain-specific CLIs for Eve-compatible apps so agents interact via commands instead of raw REST calls.

Why

Agents waste 3-5 LLM calls per REST interaction on URL construction, JSON quoting, auth headers, and error parsing. A CLI reduces this to 1 call:

Before (3-5 calls, error-prone)

curl -X POST "$EVE_APP_API_URL_API/projects/$PID/changesets"
-H "Content-Type: application/json"
-H "Authorization: Bearer $EVE_JOB_TOKEN"
-d @/tmp/changeset.json

After (1 call, self-documenting)

eden changeset create --project $PID --file /tmp/changeset.json

Quick Start

  1. Create the CLI Package

your-app/ cli/ src/ index.ts # Entry point client.ts # API client (reads env vars) commands/ projects.ts # Domain commands bin/ your-app # Built artifact (single-file bundle) package.json tsconfig.json

  1. Implement the API Client

// cli/src/client.ts — Copy this, change SERVICE name const SERVICE = 'API';

export function getApiUrl(): string { const url = process.env[EVE_APP_API_URL_${SERVICE}]; if (!url) { console.error(Error: EVE_APP_API_URL_${SERVICE} not set.); console.error('Are you running inside an Eve job with with_apis: [api]?'); process.exit(1); } return url; }

export async function api<T = unknown>( method: string, path: string, body?: unknown, ): Promise<T> { const url = getApiUrl(); const token = process.env.EVE_JOB_TOKEN; const res = await fetch(${url}${path}, { method, headers: { 'Content-Type': 'application/json', ...(token ? { Authorization: Bearer ${token} } : {}), }, body: body ? JSON.stringify(body) : undefined, }); if (!res.ok) { const err = await res.json().catch(() => ({} as Record<string, string>)); console.error(${method} ${path} → ${res.status}: ${err.message || res.statusText}); process.exit(1); } return res.json() as Promise<T>; }

  1. Define Commands

// cli/src/index.ts import { Command } from 'commander'; import { api } from './client.js'; import { readFile } from 'node:fs/promises';

const program = new Command(); program.name('myapp').description('My App CLI').version('1.0.0');

program.command('items') .command('list') .option('--json', 'JSON output') .action(async (opts) => { const items = await api('GET', '/items'); if (opts.json) return console.log(JSON.stringify(items, null, 2)); for (const i of items) console.log(${i.id} ${i.name}); });

program.command('items') .command('create') .requiredOption('--file <path>', 'JSON file') .action(async (opts) => { const body = JSON.parse(await readFile(opts.file, 'utf8')); const result = await api('POST', '/items', body); console.log(Created: ${result.id}); });

program.parse();

  1. Bundle for Zero-Dependency Distribution

Create a build script (cli/build.mjs ):

import { build } from 'esbuild'; import { readFile, writeFile, chmod } from 'node:fs/promises';

await build({ entryPoints: ['cli/src/index.ts'], bundle: true, platform: 'node', target: 'node20', format: 'cjs', // CJS — commander uses require() internally outfile: 'cli/bin/myapp', });

// Prepend shebang (esbuild banner escapes the !) const code = await readFile('cli/bin/myapp', 'utf8'); await writeFile('cli/bin/myapp', '#!/usr/bin/env node\n' + code); await chmod('cli/bin/myapp', 0o755);

Add to package.json :

{ "scripts": { "build": "node build.mjs" } }

Important: Do NOT set "type": "module" in package.json — it causes require() errors at runtime. Use .mjs extension for the build script instead.

  1. Declare in Manifest

.eve/manifest.yaml

services: api: build: context: ./apps/api ports: [3000] x-eve: api_spec: type: openapi cli: name: myapp # Binary name on $PATH bin: cli/bin/myapp # Path relative to repo root

The platform automatically makes the CLI available to agents that have with_apis: [api] .

Design Rules

Command Structure

Map CLI commands to your domain, not HTTP endpoints:

Good — domain vocabulary

eden map show eden changeset create --file data.json eden changeset accept CS-45

Bad — HTTP vocabulary

eden get /projects/123/map eden post /changesets --body data.json

Output Contract

  • Default: human-readable (tables, summaries)

  • --json : machine-readable JSON on stdout

  • Errors: stderr, exit code 1, actionable message

eden projects list # Table: ID NAME CREATED eden projects list --json # [{"id":"...","name":"..."}] eden changeset accept BAD-ID # stderr: "Changeset BAD-ID not found"

Auto-Detection Pattern

When only one resource exists, auto-detect instead of requiring flags:

async function autoDetectProject(): Promise<string> { const projects = await api('GET', '/projects'); if (projects.length === 1) return projects[0].id; if (projects.length === 0) { console.error('No projects found.'); process.exit(1); } console.error('Multiple projects. Use --project <id>:'); for (const p of projects) console.error( ${p.id} ${p.name}); process.exit(1); }

Progressive Help

Every command and subcommand has --help :

$ eden --help Eden story map CLI

Commands: projects Manage projects map View story map changeset Create and review changesets persona Manage personas question Manage questions search Search the map export Export project data

$ eden changeset --help Commands: create Create a changeset from JSON file accept Accept a pending changeset reject Reject a pending changeset list List changesets for a project

Environment Variables

The CLI reads these from the environment (injected automatically by Eve):

Variable Purpose Set By

EVE_APP_API_URL_{SERVICE}

Base URL of the app API Platform (--with-apis )

EVE_JOB_TOKEN

Bearer auth token Platform (per job)

EVE_PROJECT_ID

Eve platform project ID Platform

EVE_ORG_ID

Eve platform org ID Platform

The CLI never requires manual configuration.

Testing Locally

Set env vars and run directly:

export EVE_APP_API_URL_API=http://localhost:3000 export EVE_JOB_TOKEN=$(eve auth token)

Test individual commands

./cli/bin/myapp projects list ./cli/bin/myapp items create --file test-data.json

Bundling Details

Use esbuild to produce a single file with zero runtime dependencies:

  • --bundle inlines all imports (including commander )

  • --platform=node targets Node.js built-ins

  • --target=node20 matches Eve runner environment

  • --format=cjs uses CommonJS (commander uses require() internally)

  • Shebang prepended separately (esbuild --banner escapes ! in #!/usr/bin/env )

  • Result: 50-200KB single file, no node_modules needed at runtime

Commit cli/bin/myapp to the repo so it's available immediately after clone.

Image-Based Distribution (Compiled CLIs)

For Go, Rust, or other compiled CLIs:

services: api: x-eve: cli: name: myapp image: ghcr.io/org/myapp-cli:latest

Build a Docker image with the CLI binary at /cli/bin/myapp :

FROM rust:1.77 AS build COPY . . RUN cargo build --release

FROM busybox:stable COPY --from=build /app/target/release/myapp /cli/bin/myapp

The platform injects it via init container (same pattern as toolchains, ~2-5s latency).

See Also

  • references/app-cli.md in eve-read-eve-docs for the full technical reference

  • references/manifest.md for manifest schema details

  • references/eve-sdk.md for the Eve Auth SDK (server-side token verification)

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

eve-local-dev-loop

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

eve-cli-primitives

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

eve-web-ui-testing-agent-browser

No summary provided by upstream source.

Repository SourceNeeds Review