ilbertt/parsh/parsh-env

Claude Code and agent skill by ilbertt/parsh

Trust tier 3
  • community
Source check pending

Checking that the SKILL.md file is still in the repository.

How to use @parshjs/env for typed, lazy environment-variable access in a parsh CLI. Use when adding env vars to a CLI, or when you see a handler reading `process.env` directly. Pairs with the parsh skill — read that first if you don't know how parsh CLIs are structured.

Install

npx vskill@latest install ilbertt/parsh/parsh-env

SKILL.md

parsh-env

`@parshjs/env` gives a parsh CLI a typed, lazy ctx.context.env for environment variables. It validates each variable with Standard Schema v1 (Zod, Valibot, ArkType, …) the first time a handler reads it — subcommands that never touch a variable never pay the cost or throw on a missing one.

For the broader parsh workflow (commands, codegen, Register), see `../parsh/SKILL.md`.

When to use

  • The user asks to add an env var to a parsh CLI.
  • A handler is reading process.env.X directly — replace with ctx.context.env.X.
  • The CLI needs config that depends on the deployment environment (DB URL, ports, feature flags, secrets).

Do not use for CLI flags — those go in defineCommand({ options: … }). @parshjs/env is exclusively for process.env-style variables.

Install

sh
bun add @parshjs/env

Setup

Inject createEnvContext into createCli's context, and register the Cli so the types propagate.

ts
// src/main.ts
import { createCli } from '@parshjs/core';
import { createEnvContext } from '@parshjs/env';
import { z } from 'zod';
import { commandTree } from './commandTree.gen.ts';
const cli = createCli({
programName: 'mycli',
tree: commandTree,
context: {
env: createEnvContext({
vars: {
PORT: { schema: z.number().int().positive(), default: 3000 },
DATABASE_URL: { schema: z.url() },
NODE_ENV: { schema: z.enum(['development', 'production']) },
},
}),
},
});
declare module '@parshjs/core' {
interface Register {
cli: typeof cli;
}
}
await cli.main();

The declare module block is required — without it, ctx.context is invisible to TypeScript.

Reading variables in a handler

Registered context lives under ctx.context, so the typed env reads as ctx.context.env.<KEY>:

ts
defineCommand('serve', {
options: {},
handler: ({ context, print }) => {
context.env.PORT; // number
context.env.DATABASE_URL; // string (validated URL)
context.env.NODE_ENV; // 'development' | 'production'
context.env.FOO; // ❌ compile-time TypeScript error
print.info(`listening on ${context.env.PORT}`);
},
});

Each variable is validated lazily, on first access. Once validated, the value is cached for the lifetime of the run.

Coercion

@parshjs/env tries the raw string as-is, then numerically, then as a boolean — first success wins. So z.number() and z.boolean() work without z.coerce.*:

ts
vars: {
PORT: { schema: z.number().int().positive() }, // PORT=8080
ENABLED: { schema: z.boolean() }, // ENABLED=true
TAGS: { schema: z.string().transform((s) => s.split(',')) }, // TAGS=a,b,c
}

Defaults

Provide default to make a variable optional. The default is returned when the source has no value (undefined or empty string) and bypasses the schema — supply a value already in the schema's output type.

ts
vars: {
PORT: { schema: z.number().int().positive(), default: 3000 },
}

Without a default, reading a missing variable throws EnvMissingError. An invalid value throws EnvValidationError.

Remapping the variable name

If the in-code key should differ from the env var name, pass name:

ts
vars: {
databaseUrl: { name: 'DATABASE_URL', schema: z.url() },
}
// access: ctx.context.env.databaseUrl (reads process.env.DATABASE_URL)

Custom source

By default the source is process.env. Pass source to override — useful for tests or for reading from a parsed .env snapshot.

ts
createEnvContext({
source: { PORT: '8080', DATABASE_URL: 'postgres://…' },
vars: { … },
});

Common mistakes

  • Reading `process.env.X` directly inside a handler. Use ctx.context.env.X instead — typed, validated, lazy.
  • Looking on `ctx.env` instead of `ctx.context.env`. Registered context always lives under ctx.context. The flat ctx.env doesn't exist.
  • Forgetting the `Register` augmentation. Without it, ctx.context is invisible at the type level. See the parsh skill.
  • Using `z.coerce.*` for numbers/booleans. Not needed — @parshjs/env coerces automatically.
  • Putting CLI flags in `vars`. Flags go in defineCommand({ options: … }), not in env.

Security

  • Security scanPassed, 95 of 100Pattern checks on the skill's files.
  • Model reviewPassed

Quality evaluation

  • Task evaluationNot runNo evaluation yet. Run one in Skill Studio.

Source

Popularity

Repository stars
35
Repository forks
2
7-day trend score
18

Stars and forks are for the whole GitHub repository, not this skill alone. Refreshed 22 Jun 2026, 4 months ago.

Works with

Any agent vskill installs to: 56 targets in all. These 39 have a page on this site.

  • Claude Code
  • Cursor
  • GitHub Copilot
  • Windsurf
  • Codex
  • Gemini CLI
  • Cline
  • Amp
  • Roo Code
  • Goose
  • Continue
  • Kilo Code
  • Junie
  • Trae
  • OpenHands
  • Qwen Code
  • AI Agent
  • Tabnine Chat
  • Sourcegraph Cody
  • Amazon Q
  • Firebase Genkit
  • Sweep
  • Devin
  • Phind
  • Mentat
  • Claude Desktop
  • Aider
  • GPT Pilot
  • Plandex
  • bolt.new
  • v0
  • Replit Agent
  • Anse
  • TypingMind
  • BetterChatGPT
  • LibreChat
  • Warp AI
  • Pieces
  • Fig AI
Embed badge
vskill VERIFIED badge
[![vskill VERIFIED](https://verified-skill.com/api/v1/skills/ilbertt/parsh/parsh-env/badge)](https://verified-skill.com/skills/ilbertt/parsh/parsh-env)

Something wrong with this skill or its page? Tell us.