# How gemini-eval.mjs Enables Standalone API Evaluation Without CLI Installation

> Understand how gemini-eval.mjs allows standalone API evaluation. No CLI installation needed for this self-contained Node.js module, simplifying your job-offer analysis.

- Repository: [Santiago Fernández de Valderrama/career-ops](https://github.com/santifer/career-ops)
- Tags: internals
- Published: 2026-06-07

---

**The `gemini-eval.mjs` script is a self-contained Node.js module that performs complete job-offer evaluations using the Google Gemini API without requiring the main career-ops CLI or any external command-line tools.**

The `gemini-eval.mjs` script in the `santifer/career-ops` repository provides **standalone API evaluation** capabilities that operate independently of the broader career-ops CLI infrastructure. By bundling environment configuration, argument parsing, and report generation into a single executable file, users can run sophisticated AI-powered job evaluations with only Node.js installed. This approach eliminates CLI dependencies while maintaining full access to the repository's evaluation context and prompting logic.

## Self-Contained Environment Bootstrapping

The script handles its own environment setup by loading configuration variables directly at startup. In `gemini-eval.mjs` lines 38-44, the script attempts to dynamically import `dotenv` to load a local `.env` file, falling back to `process.env` if the package is unavailable:

```javascript
try {
  const { config } = await import('dotenv');
  config();
} catch {
  // Fallback to process.env
}

```

This ensures the `GEMINI_API_KEY` is available without relying on the CLI's configuration management system.

## Built-In CLI Argument Parsing

Rather than depending on an external CLI framework, `gemini-eval.mjs` implements a lightweight argument parser internally (lines 68-84). The script recognizes flags for `--file`, `--model`, and `--no-save`, allowing users to specify input sources directly:

- **Inline JD text**: Pass the job description as a positional argument
- **File input**: Use `--file ./path/to/jd.txt` to read from disk
- **Model selection**: Override the default model with `--model gemini-2.5-flash-lite`
- **Report suppression**: Skip file output using `--no-save`

This internal parsing removes the need for command-line wrappers or the main `career-ops` CLI binary.

## Local Context Loading and Prompt Assembly

The script reads all evaluation context from the repository's file system using Node's native `fs` module (lines 79-84). It loads:

- [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) - System-wide evaluation rules
- [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md) - Structured evaluation flow (blocks A-G)
- [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) - Candidate resume content
- [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml) and [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md) - User profile and narrative settings

Lines 88-136 concatenate these files into a single `systemPrompt` string. This mirrors the prompt-routing logic used by Claude-based implementations but generates everything on-the-fly without external routing services.

## Direct Gemini SDK Integration

After instantiating `GoogleGenerativeAI` with the API key (lines 42-45), the script creates a model instance and invokes the API directly:

```javascript
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY);
const model = genAI.getGenerativeModel({ 
  model: modelName, 
  systemInstruction: systemPrompt 
});

const result = await model.generateContent({
  contents: [{ role: 'user', parts: [{ text: jobDescription }] }]
});

```

The complete request-response cycle executes within the script (lines 52-57), requiring no intermediary CLI tools or API gateways.

## Safe Error Handling and Sanitization

Error management occurs entirely within the script (lines 58-66). API errors are caught, **sanitized to remove sensitive API keys** from messages, and surfaced directly to the user. This built-in error handling eliminates dependency on external logging or error-reporting utilities while protecting credential security.

## Local Report Generation

When the `--no-save` flag is absent, the script autonomously manages report creation (lines 13-45). It performs the following actions using Node's native `fs` module:

1. Creates a `reports/` directory if missing (lines 15-17)
2. Calculates the next report number using `nextReportNumber`
3. Writes a Markdown report to disk
4. Prints a tracker-entry reminder

All file-system operations use native Node.js APIs, removing requirements for external file management tools.

## Usage Examples

Because every dependency (`@google/generative-ai` and optional `dotenv`) is declared in the project's [`package.json`](https://github.com/santifer/career-ops/blob/main/package.json), users execute evaluations directly with Node after a single `npm install`:

```bash

# Evaluate inline job description

node gemini-eval.mjs "Senior Data Engineer – 5+ years experience with Postgres, Python, and cloud pipelines."

# Evaluate from file with specific model

node gemini-eval.mjs --file ./jds/openai-swe.txt --model gemini-2.5-flash-lite

# Run without saving report

node gemini-eval.mjs --no-save "Lead ML Engineer – remote"

```

The script also provides built-in help (lines 70-98) when invoked with `--help` or no arguments, displaying usage examples that require only `node`.

## Summary

- **Self-contained execution**: `gemini-eval.mjs` operates as a standalone Node.js script without requiring the `career-ops` CLI installation.
- **Internal dependencies**: Handles environment loading, argument parsing, and file I/O using only Node.js built-ins and the Gemini SDK.
- **Complete evaluation pipeline**: Loads local context files ([`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md), [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md), [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md), [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml)), constructs prompts, and calls the Gemini API directly.
- **Zero external tooling**: Generates Markdown reports and handles errors internally using native `fs` modules and built-in sanitization.
- **Direct invocation**: Execute with `node gemini-eval.mjs` after installing repository dependencies via `npm install`.

## Frequently Asked Questions

### Can I run gemini-eval.mjs without installing the full career-ops CLI?

Yes. The script is designed for **standalone API evaluation without CLI installation**. You only need Node.js and the repository's npm dependencies (`@google/generative-ai` and optionally `dotenv`). Clone the repository, run `npm install`, and execute the script directly with `node gemini-eval.mjs`.

### How does the script handle API authentication without CLI configuration?

The script bootstraps its own environment by loading a `.env` file at lines 38-44 using dynamic import. If `dotenv` is not available, it falls back to `process.env`. As long as `GEMINI_API_KEY` is set in your environment or `.env` file, the script authenticates directly with Google's API without the CLI's configuration layer.

### What evaluation context does the script load locally?

The script reads five key files from the repository to build the evaluation prompt: [`modes/_shared.md`](https://github.com/santifer/career-ops/blob/main/modes/_shared.md) (system rules), [`modes/oferta.md`](https://github.com/santifer/career-ops/blob/main/modes/oferta.md) (evaluation workflow), [`cv.md`](https://github.com/santifer/career-ops/blob/main/cv.md) (candidate resume), [`config/profile.yml`](https://github.com/santifer/career-ops/blob/main/config/profile.yml) (user settings), and [`modes/_profile.md`](https://github.com/santifer/career-ops/blob/main/modes/_profile.md) (personal archetype). These are concatenated into the system prompt at lines 88-136.

### Does the script require external tools to save evaluation reports?

No. The script uses Node's native `fs` module to create the `reports/` directory, calculate report numbers, and write Markdown files autonomously (lines 13-45). The `--no-save` flag allows you to skip file generation entirely if you only need console output.