agentsclimarketplace

Config environment

Skill batur/ts-baseline-docs/.agents/skills/config-environment

TypeScript software architecture baseline with VS Code Agent Skills for AI coding, review, security, testing, API design, and delivery standards.

Install
npx -y skills add batur/ts-baseline-docs --skill config-environment

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Enforce TypeScript config and environment rules. Use when adding or reviewing env variables, process.env usage, Zod env validation, server/client config separation, secrets, public env exposure, feature flags, provider config, .env.example, or adapter initialization.

The file declares its own license as Proprietary. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

11.8 KB, as published. Nobody here has run it

Config / Environment Skill

When to use this skill

Use this skill when work touches:

  • env variables, .env files, .env.example
  • process.env
  • Zod env validation
  • server-only config or browser/client config
  • secrets, tokens, API keys, DSNs, service-role keys
  • feature flags, boolean/number/list env parsing
  • provider setup: database, Supabase, Firebase, OpenAI, Stripe, Sentry, mail, storage, auth, logger
  • deployment config for local, test, staging, production, preview

Goal

Create one typed configuration boundary. Application code must consume validated config objects, not raw environment variables.

Environment variables are external input.
External input is untrusted until validated.
Invalid critical configuration fails fast at startup.

Core rules

  • Use Zod for env validation.
  • Read process.env only inside env/config modules, tooling config, or explicit test setup.
  • Validate env during startup.
  • Application code consumes typed config objects.
  • Separate server-only env from client-exposed env.
  • Secrets never enter client bundles.
  • Secrets are never logged.
  • .env.example documents required variables.
  • .env.local, production env files, and secret env files are not committed.
  • Env names use SCREAMING_SNAKE_CASE.
  • Boolean env values are parsed explicitly from "true" or "false".
  • Number env values are parsed into numbers before use.
  • List env values are parsed into arrays before use.
  • NODE_ENV and APP_ENV are different concepts.
  • Adapters receive config through constructor/factory arguments.
  • Providers and SDKs must not read process.env directly.
  • Runtime application config is separate from tooling config.

Folder standard

Backend/server:

src/shared/config/server-env.ts
src/shared/config/app-config.ts
src/shared/config/config-error.ts

Frontend/browser:

src/shared/config/client-env.ts
src/shared/config/app-config.ts

Fullstack:

src/shared/config/server-env.ts
src/shared/config/client-env.ts
src/shared/config/app-config.ts

Rules:

  • server-env.ts may read server-only secrets.
  • client-env.ts exposes only browser-safe/public values.
  • client-env.ts must not import server-env.ts.
  • server-env.ts must not be imported into browser/client code.

Naming standard

Use SCREAMING_SNAKE_CASE for schema constants and parsed env objects:

export const SERVER_ENV_SCHEMA = z.object({});
export const SERVER_ENV = SERVER_ENV_SCHEMA.parse(process.env);

export const CLIENT_ENV_SCHEMA = z.object({});
export const CLIENT_ENV = CLIENT_ENV_SCHEMA.parse({});

Use PascalCase for inferred types:

export type ServerEnv = z.infer<typeof SERVER_ENV_SCHEMA>;
export type ClientEnv = z.infer<typeof CLIENT_ENV_SCHEMA>;

Server env pattern

import { z } from "zod";

export const SERVER_ENV_SCHEMA = z.object({
  NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
  APP_ENV: z.enum(["local", "development", "staging", "production", "test"]).default("local"),
  PORT: z.coerce.number().int().min(1).max(65535).default(3000),
  DATABASE_URL: z.string().url(),
  LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
  CORS_ORIGINS: z.string().optional(),
  OPENAI_API_KEY: z.string().min(1).optional(),
  SENTRY_DSN: z.string().url().optional(),
});

export type ServerEnv = z.infer<typeof SERVER_ENV_SCHEMA>;
export const SERVER_ENV = SERVER_ENV_SCHEMA.parse(process.env);

Use safeParse only when you need a custom startup message:

const parsed = SERVER_ENV_SCHEMA.safeParse(process.env);

if (!parsed.success) {
  console.error("Invalid environment configuration", parsed.error.format());
  process.exit(1);
}

export const SERVER_ENV = parsed.data;

Client env pattern

Client env may contain only public values.

Vite-style:

export const CLIENT_ENV_SCHEMA = z.object({
  APP_ENV: z.enum(["local", "development", "staging", "production", "test"]).default("local"),
  PUBLIC_API_BASE_URL: z.string().url(),
});

export type ClientEnv = z.infer<typeof CLIENT_ENV_SCHEMA>;

export const CLIENT_ENV = CLIENT_ENV_SCHEMA.parse({
  APP_ENV: import.meta.env.MODE,
  PUBLIC_API_BASE_URL: import.meta.env.VITE_PUBLIC_API_BASE_URL,
});

Next.js-style:

export const CLIENT_ENV = CLIENT_ENV_SCHEMA.parse({
  APP_ENV: process.env.NODE_ENV,
  PUBLIC_API_BASE_URL: process.env.NEXT_PUBLIC_API_BASE_URL,
});

Rules:

  • Public env uses framework public prefixes such as VITE_ or NEXT_PUBLIC_ when required.
  • Secret env must never use public prefixes.
  • Client config may expose only values safe to ship to a browser.

process.env rule

Allowed direct access:

  • server-env.ts
  • client-env.ts
  • framework/tooling config files
  • explicit test setup files

Forbidden direct access:

  • domain code
  • use-cases/application services
  • controllers/routes
  • repositories
  • adapters/SDK clients
  • UI components
  • validation schemas outside config modules

Wrong:

export class OpenAiAdapter {
  private readonly apiKey = process.env.OPENAI_API_KEY;
}

Right:

export type OpenAiConfig = { API_KEY: string };

export class OpenAiAdapter {
  constructor(private readonly config: OpenAiConfig) {}
}

Composition root:

const openAiAdapter = new OpenAiAdapter({
  API_KEY: SERVER_ENV.OPENAI_API_KEY,
});

NODE_ENV vs APP_ENV

NODE_ENV = runtime/tooling mode: development | test | production
APP_ENV  = deployment environment: local | development | staging | production | test

Examples:

  • Staging usually runs NODE_ENV=production and APP_ENV=staging.
  • Tests use NODE_ENV=test and APP_ENV=test.
  • Do not use NODE_ENV as the only deployment environment switch.

Boolean, number, and list env parsing

Do not parse booleans with Boolean(value). Boolean("false") is true.

const BOOLEAN_ENV_SCHEMA = z
  .enum(["true", "false"])
  .default("false")
  .transform((value) => value === "true");

Number values must become numbers:

PORT: z.coerce.number().int().min(1).max(65535).default(3000),
REQUEST_TIMEOUT_MS: z.coerce.number().int().positive().default(10_000),

Comma-separated values must become arrays:

const COMMA_SEPARATED_LIST_SCHEMA = z
  .string()
  .transform((value) =>
    value.split(",").map((item) => item.trim()).filter(Boolean),
  );

CORS_ORIGINS: COMMA_SEPARATED_LIST_SCHEMA.default(""),

Application code should consume normalized values, not raw strings.

Secrets

Treat these as sensitive by default:

*_SECRET
*_TOKEN
*_KEY
*_PRIVATE_KEY
DATABASE_URL
JWT_SECRET
OPENAI_API_KEY
STRIPE_SECRET_KEY
SUPABASE_SERVICE_ROLE_KEY
WEBHOOK_SECRET
COOKIE_SECRET
SESSION_SECRET

Rules:

  • Do not commit secrets.
  • Do not log secrets.
  • Do not expose secrets to client bundles.
  • Do not include secrets in examples, docs, tests, snapshots, screenshots, or OpenAPI examples.
  • If a secret leaks, rotate it; deleting the commit is not enough.

Public values such as PUBLIC_API_BASE_URL, PUBLIC_APP_URL, PUBLIC_SUPABASE_URL, and PUBLIC_SUPABASE_ANON_KEY still require backend authorization, RLS, or provider security rules.

Config objects

Do not pass the entire env object everywhere. Create focused typed config objects.

export const DATABASE_CONFIG = {
  URL: SERVER_ENV.DATABASE_URL,
} as const;

export const LOGGER_CONFIG = {
  LEVEL: SERVER_ENV.LOG_LEVEL,
} as const;

export const HTTP_CONFIG = {
  PORT: SERVER_ENV.PORT,
  CORS_ORIGINS: SERVER_ENV.CORS_ORIGINS,
} as const;

Adapters receive focused config, not raw env. Keep config construction in config/composition-root code.

.env file standard

Required:

.env.example

Local or secret files are not committed:

.env.local
.env.production
.env.staging

Test env may be committed only if it contains no secrets.

.env.example must document required variables:

NODE_ENV=development
APP_ENV=local
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/app
LOG_LEVEL=info
CORS_ORIGINS=http://localhost:3000
OPENAI_API_KEY=

When env changes, update the env schema, config object, .env.example, deployment docs when affected, and critical config tests.

Test environment safety

Tests must not accidentally connect to production or staging services.

if (SERVER_ENV.NODE_ENV === "test" && SERVER_ENV.DATABASE_URL.toLowerCase().includes("prod")) {
  throw new Error("Test environment cannot use production database.");
}

Use explicit test config, local/test database URLs, and no production/staging API keys. Mock/fake external providers unless a real integration test is intentional.

Runtime config vs tooling config

Keep runtime app config under src/shared/config/. Tooling config files such as eslint.config.js, vite.config.ts, vitest.config.ts, playwright.config.ts, and drizzle.config.ts may read env when required by the tool. Business/application code still must not.

AI coding workflow

When adding or changing config:

  1. Classify the value as server-only, client-public, test-only, or tooling-only.
  2. Add it to the correct env schema.
  3. Validate and normalize it with Zod.
  4. Expose only a focused typed config object.
  5. Pass config into adapters/factories through constructor or factory args.
  6. Update .env.example.
  7. Update deployment docs when deployment changes.
  8. Add/adjust tests for critical config behavior.
  9. Ensure secrets are not logged, exposed, or used in examples.

Review checklist

Before approving config/environment changes, verify:

  • No process.env usage outside allowed files.
  • New env variable is validated with Zod.
  • Critical missing/invalid env fails fast.
  • Server-only secret is not exposed to client code.
  • Client env uses appropriate public prefix.
  • Boolean env parsing does not use Boolean(value).
  • Number/list env values are normalized before use.
  • NODE_ENV and APP_ENV are not confused.
  • Adapter receives config via constructor/factory, not direct env access.
  • .env.example is updated.
  • Secret values are not logged or committed.
  • Tests cannot connect to production/staging services accidentally.
  • Deployment documentation is updated when relevant.

Common anti-patterns

Do not introduce:

const apiKey = process.env.OPENAI_API_KEY;

outside config modules.

Do not parse booleans like this:

const enabled = Boolean(process.env.FEATURE_ENABLED);

Do not expose server secrets to client code:

export const CLIENT_ENV = {
  OPENAI_API_KEY: process.env.OPENAI_API_KEY,
};

Do not pass raw env everywhere:

new StripeAdapter(process.env);

Do not log env/config objects wholesale:

LOGGER.info({ env: process.env });
LOGGER.info({ config: SERVER_ENV });

Required tests

Add tests when config is critical or security-sensitive:

  • missing DATABASE_URL fails
  • invalid URL fails
  • boolean env accepts only "true" / "false"
  • number env parses to number and rejects invalid values
  • list env parses to array
  • test environment rejects production-looking database URLs
  • client env schema does not include server-only secrets

Example:

describe("SERVER_ENV_SCHEMA", () => {
  it("rejects missing DATABASE_URL", () => {
    const result = SERVER_ENV_SCHEMA.safeParse({
      NODE_ENV: "development",
      APP_ENV: "local",
      PORT: "3000",
    });

    expect(result.success).toBe(false);
  });
});

Final rule

If code needs configuration, it should receive a typed config object. If code reaches for process.env, stop and move that access to the config boundary.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.