{"id":25627179,"url":"https://github.com/theogravity/stainless-tools","last_synced_at":"2026-02-23T13:08:28.878Z","repository":{"id":277478240,"uuid":"931894906","full_name":"theogravity/stainless-tools","owner":"theogravity","description":"Unofficial CLI tool for interacting with the Stainless SDK generation service (stainless.com)","archived":false,"fork":false,"pushed_at":"2025-02-20T19:56:04.000Z","size":292,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-25T08:39:02.796Z","etag":null,"topics":["cli","generator","sdk","stainless","tool"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/theogravity.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2025-02-13T03:01:41.000Z","updated_at":"2025-02-20T19:55:40.000Z","dependencies_parsed_at":"2025-02-22T17:33:05.527Z","dependency_job_id":null,"html_url":"https://github.com/theogravity/stainless-tools","commit_stats":null,"previous_names":["theogravity/stainless-tools"],"tags_count":18,"template":false,"template_full_name":"theogravity/boilerplate-typescript-package","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fstainless-tools","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fstainless-tools/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fstainless-tools/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/theogravity%2Fstainless-tools/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/theogravity","download_url":"https://codeload.github.com/theogravity/stainless-tools/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248889971,"owners_count":21178332,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","generator","sdk","stainless","tool"],"created_at":"2025-02-22T17:32:36.757Z","updated_at":"2026-02-23T13:08:23.844Z","avatar_url":"https://github.com/theogravity.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Stainless Tools\n\n[![NPM version](https://img.shields.io/npm/v/stainless-tools.svg?style=flat-square)](https://www.npmjs.com/package/stainless-tools)\n![NPM Downloads](https://img.shields.io/npm/dm/stainless-tools)\n[![TypeScript](https://img.shields.io/badge/%3C%2F%3E-TypeScript-%230074c1.svg)](http://www.typescriptlang.org/)\n\nA TypeScript library and CLI tool for managing [Stainless](https://www.stainless.com/) config and generated SDKs. This tool helps you generate, monitor, and sync SDK repositories as you update your OpenAPI / Stainless config files.\n\n## Table of Contents\n\n- [Features](#features)\n- [About this project](#about-this-project)\n- [Installation](#installation)\n- [Configuration](#configuration)\n  - [Configuration Schema](#configuration-schema)\n  - [Target Directory Templates](#target-directory-templates)\n  - [Example Configuration](#example-configuration)\n  - [Lifecycle Hooks](#lifecycle-hooks)\n- [Generate Command](#generate-command)\n  - [Usage](#usage)\n  - [Branch Configuration and Environments](#branch-configuration-and-environments)\n  - [How It Works](#how-it-works)\n- [Publish Specs Command](#publish-specs-command)\n  - [Usage](#usage-1)\n  - [How It Works](#how-it-works-1)\n\n## Features\n\n- 🔄 Clone and monitor SDK repositories for changes\n- 📄 Automatic OpenAPI specification file handling\n- ⚙️ Flexible configuration using cosmiconfig\n- 🔒 Support for both HTTPS and SSH git URLs\n- 🚀 Easy-to-use CLI interface\n- 📦 Can be used as a library in your own projects\n- 🎯 Default configurations for easier usage\n- 🔑 Integration with Stainless API for publishing changes\n- 🔄 Lifecycle hooks for post-clone automation\n\n## About this project\n\nThis `src` files were entirely built using [Cursor](https://www.cursor.com/). It took an entire day with careful prompting to cover various use-cases and write the appropriate tests and documentation. No human wrote any part of the `src` code, although the README has been hand-polished.\n\nIt has only been tested on MacOS under Node 22 (although built for 18). Other platforms may not be supported.\n\n## Installation\n\nYou must have at least Node 18 installed to use this tool.\n\n```bash\nnpm install stainless-tools -g\n```\n\n### Prerequisites\n\nEnsure you have:\n- Write access to the SDK repository you want to generate\n- A Stainless API key. This is the same API key used for automating updates via [GitHub Action](https://app.stainlessapi.com/docs/guides/automate-updates#option-1-github-action-recommended)\n- Have access to the generated SDK Repo as it will be checked out: `https://github.com/stainless-sdks/\u003cproject_name\u003e-\u003clanguage\u003e`\n\n#### Environment Setup\n\nBefore using the tool, you need to set up your environment variables. You can do this in two ways:\n\n1. Create a `.env` file (or `.env.override` if you auto-generate your `.env` file) in your project root:\n```bash\nSTAINLESS_API_KEY=your_api_key_here\n```\n\n2. Or export them in your shell:\n```bash\nexport STAINLESS_API_KEY=your_api_key_here\n```\n\n## Configuration\n\nThe tool uses [cosmiconfig](https://github.com/davidtheclark/cosmiconfig) for configuration management. You can define your configuration in any of these ways:\n\n- A `stainless-tools` property in package.json\n- A `.stainless-toolsrc` file in JSON or YAML format\n- A `.stainless-toolsrc.json`, `.stainless-toolsrc.yaml`, `.stainless-toolsrc.yml`, `.stainless-toolsrc.js`, or `.stainless-toolsrc.cjs` file\n- A `stainless-tools.config.js` or `stainless-tools.config.cjs` CommonJS module\n\n### Configuration Schema\n\n```typescript\ninterface StainlessConfig {\n  // Map of SDK names to their repository URLs\n  // Each SDK can have a staging and/or production URL\n  // By default, the staging URL is used, but you can use --prod to use the production URL\n  stainlessSdkRepos: {\n    [key: string]: {\n      // The staging URL is used by default\n      staging?: string;\n      // The production URL is used when --prod is specified\n      prod?: string;\n    };\n  };\n\n  // Optional lifecycle hooks for each SDK\n  // These commands are executed at specific points in the SDK lifecycle\n  lifecycle?: {\n    [key: string]: {\n      postClone: string;\n      postUpdate: string;\n      prePublishSpec: string;\n    };\n  };\n\n  defaults?: {\n    // Default branch name for all SDKs (optional)\n    // If not specified, will use existing branch in target directory or generate a new cli/ branch\n    // Typically use 'main' for production and '\u003cusername\u003e/dev' for staging\n    // See: https://app.stainlessapi.com/docs/guides/branches\n    branch?: string;\n\n    // Default target directory for generated SDKs. Supports the following template variables:\n    // - {sdk}: The name of the SDK being generated\n    // - {env}: The environment (staging/prod) being used\n    // - {branch}: The git branch name (forward slashes converted to hyphens)\n    targetDir?: string;\n\n    // Default OpenAPI specification file location\n    openApiFile?: string;\n\n    // Default Stainless configuration file\n    stainlessConfigFile?: string;\n\n    // Whether to use the \"Guess with AI\" command from the Stainless Studio for the Stainless Config\n    guessConfig?: boolean;\n\n    // Default project name\n    projectName?: string;\n  };\n}\n```\n\n### Target Directory Templates\n\nThe `targetDir` configuration supports template variables that are dynamically replaced when generating SDKs:\n\n- `{sdk}`: Replaced with the name of the SDK being generated\n- `{env}`: Replaced with the current environment (`staging` or `prod`)\n- `{branch}`: Replaced with the git branch name (forward slashes are converted to hyphens for filesystem compatibility)\n\nThis allows you to organize your SDKs in a structured way. For example:\n\n```javascript\n{\n  defaults: {\n    targetDir: './sdks/{env}/{sdk}/{branch}'\n  }\n}\n```\n\nWould generate directories like:\n- `./sdks/staging/typescript/main`\n- `./sdks/prod/typescript/main`\n- `./sdks/staging/python/theogravity-dev` (from branch `theogravity/dev`)\n\nThis is particularly useful when working with multiple SDKs, environments, and branches simultaneously. Note that forward slashes in branch names are automatically converted to hyphens to ensure filesystem compatibility across different platforms.\n\n### Example Configuration\n\n```javascript\n// stainless-tools.config.js\nmodule.exports = {\n  stainlessSdkRepos: {\n    typescript: {\n      // Used by default\n      staging: 'git@github.com:stainless-sdks/yourproject-typescript-staging.git',\n      // Used when --prod is specified\n      prod: 'git@github.com:stainless-sdks/yourproject-typescript.git',\n    },\n  },\n  defaults: {\n    branch: 'main',\n    // Organize SDKs by environment, name, and branch\n    targetDir: './sdks/{env}/{sdk}/{branch}',\n    openApiFile: './specs/openapi.yml',\n    stainlessConfigFile: './stainless-tools.config.yml',\n    projectName: 'my-project',\n    guessConfig: false\n  }\n};\n```\n\n### Lifecycle Hooks\n\nThe tool supports lifecycle hooks that allow you to automate tasks after certain operations. Currently supported hooks:\n\n#### prePublishSpec\n\nThe `prePublishSpec` hook runs before:\n- Publishing OpenAPI and Stainless configuration files to Stainless\n\nThis hook is useful for:\n- Validating OpenAPI specifications\n- Running linters\n- Transforming specifications\n- Running pre-publish checks\n\n#### postClone\n\nThe `postClone` hook runs after:\n- Initial clone of an SDK repository\n\nThis hook is useful for:\n- Installing dependencies\n- Running initial build scripts\n- Setting up the development environment\n\n#### postUpdate\n\nThe `postUpdate` hook runs after:\n- Pulling new changes from the remote repository\n- And after restoring any stashed local changes (if there were any)\n\nThis hook is useful for:\n- Rebuilding after updates\n- Running database migrations\n- Updating dependencies\n- Running tests against new changes\n\n### Lifecycle Hook Configuration\n\nHooks are configured in the `lifecycle` section of your configuration file:\n\n```javascript\nmodule.exports = {\n  stainlessSdkRepos: {\n    typescript: {\n      staging: 'git@github.com:stainless-sdks/test-typescript.git',\n      prod: 'git@github.com:test-org/test-typescript.git',\n    },\n  },\n\n  // Lifecycle hooks allow you to automate tasks at specific points in the SDK lifecycle\n  // Each SDK can have its own set of hooks with different commands\n  // All hooks have access to these environment variables:\n  // - STAINLESS_TOOLS_SDK_PATH: Full path to the SDK repository\n  // - STAINLESS_TOOLS_SDK_BRANCH: Current branch name\n  // - STAINLESS_TOOLS_SDK_REPO_NAME: Name of the SDK (e.g., 'typescript')\n  lifecycle: {\n    typescript: {\n      // Runs before publishing specs to Stainless\n      // Use this to validate or transform your OpenAPI spec\n      // Example: Run OpenAPI linter, validate against schema, etc.\n      prePublishSpec: 'npm run validate-spec',\n\n      // Runs after initial clone of the SDK repository\n      // Use this to set up the development environment\n      // Example: Install dependencies, configure git hooks, etc.\n      postClone: 'cd $STAINLESS_TOOLS_SDK_PATH \u0026\u0026 npm install \u0026\u0026 npm run build',\n\n      // Runs after pulling new changes from remote\n      // Use this to ensure the SDK is ready after updates\n      // Example: Rebuild, update dependencies, run migrations, etc.\n      postUpdate: 'npm run build',\n    },\n    python: {\n      // Run Python-specific validation before publishing\n      // Example: Validate against custom rules, check formatting, etc.\n      prePublishSpec: 'python scripts/validate_spec.py',\n\n      // Set up Python virtual environment and install package\n      // Example: Create venv, install dependencies, etc.\n      postClone: 'python -m venv venv \u0026\u0026 source venv/bin/activate \u0026\u0026 pip install -e .',\n\n      // Update dependencies and reinstall package after changes\n      // Example: Update pip packages, rebuild extensions, etc.\n      postUpdate: 'source venv/bin/activate \u0026\u0026 pip install -e .',\n    },\n  },\n\n  // Optional default configurations\n  defaults: {\n    branch: 'main',\n    targetDir: './sdks/{env}/{sdk}/{branch}',\n    openApiFile: './specs/openapi.yml',\n    stainlessConfigFile: './stainless-tools.config.yml',\n    guessConfig: false,\n    projectName: 'my-project'\n  }\n};\n```\n\nThe hook commands:\n- Run from the SDK repository directory\n- Have access to a shell (so you can use \u0026\u0026, ||, etc.)\n- Will cause the tool to exit with an error if the command fails\n- Have access to the following environment variables:\n  - `STAINLESS_TOOLS_SDK_PATH`: Full path to the cloned SDK repository\n  - `STAINLESS_TOOLS_SDK_BRANCH`: Name of the current branch\n  - `STAINLESS_TOOLS_SDK_REPO_NAME`: Name of the SDK repository (e.g., \"typescript\", \"python\")\n\nExample using environment variables:\n\n```javascript\nmodule.exports = {\n  lifecycle: {\n    typescript: {\n      // Use environment variables to avoid hardcoding paths\n      postClone: 'cd $STAINLESS_TOOLS_SDK_PATH \u0026\u0026 npm install \u0026\u0026 npm run build',\n      // Log the current branch during updates\n      postUpdate: 'echo \"Updating SDK on branch $STAINLESS_TOOLS_SDK_BRANCH\" \u0026\u0026 npm run build'\n    }\n  }\n};\n```\n\nYou can also use external scripts:\n\n```javascript\nmodule.exports = {\n  lifecycle: {\n    typescript: {\n      postClone: './scripts/setup-sdk.sh',  // Script has access to env vars\n      postUpdate: './scripts/update-sdk.sh'\n    }\n  }\n};\n```\n\nExample setup script (`setup-sdk.sh`):\n```bash\n#!/bin/bash\necho \"Setting up $STAINLESS_TOOLS_SDK_REPO_NAME SDK in $STAINLESS_TOOLS_SDK_PATH\"\ncd \"$STAINLESS_TOOLS_SDK_PATH\"\n\n# Install dependencies\nnpm install\n\n# Run build\nnpm run build\n\n# Additional setup steps...\n```\n\n## Generate Command\n\nThe `generate` command is the primary feature of Stainless Tools. It will clone (or update if exists) an SDK repository to a target directory, publish the OpenAPI file and Stainless Config file to the Stainless Config repo, and continuously monitor for changes to the SDK repository, pulling in new changes when detected.\n\n### Usage\n\n```bash\nstainless-tools generate [options] \u003csdk-name\u003e\n\nArguments:\n  sdk-name     Name of the SDK to generate\n\nOptions:\n  -b, --branch \u003cname\u003e                Branch name to use (optional)\n  -t, --target-dir \u003cdir\u003e             Target directory for the SDK (required if not in config)\n  -o, --open-api-file \u003cfile\u003e         OpenAPI specification file (required if not in config)\n  -c, --config \u003cfile\u003e                Configuration file path\n  -s, --stainless-config-file \u003cfile\u003e Stainless configuration file (required if not in config)\n  -p, --project-name \u003cname\u003e          Project name for Stainless API (required if not in config)\n  -g, --guess-config                 Uses the \"Guess with AI\" command from the Stainless Studio for the Stainless Config if enabled\n  --prod                             Use production URLs instead of staging URLs\n  -h, --help                         Display help for command\n\n### Examples\n\n```bash\n# Using staging URL (default) with config file fully defined\n# Will use branch from config, env var, existing directory, or generate new cli/ branch\nstainless-tools generate typescript\n\n# Using production URL with main branch\nstainless-tools generate typescript \\\n  --prod \\\n  --branch main\n\n# Minimal required options (when no defaults defined in config file)\nstainless-tools generate \\\n  --open-api-file ./api-spec.json \\\n  --project-name my-project \\\n  typescript\n\n# Using all CLI options with production URL\nstainless-tools generate \\\n  --prod \\\n  --branch main \\\n  --target-dir ./sdks/typescript \\\n  --open-api-file ./api-spec.json \\\n  --project-name my-project \\\n  --config ./stainless-tools.config.js \\\n  --guess-config \\\n  typescript\n```\n\n### Branch Configuration and Environments\n\nEach SDK supports two environments with separate repositories:\n- `staging`: For development and testing, typically using `yourusername/dev` branch\n- `prod`: For production releases, typically using `main` branch\n\nThe branch name is completely optional and will be determined in this order:\n1. Command line: `--branch yourusername/dev`\n2. Environment: `STAINLESS_SDK_BRANCH=yourusername/dev`\n3. Config defaults: `defaults.branch` in your config file\n4. Current branch in target directory (if it exists)\n5. Generate a new random `cli/\u003crandom-hex\u003e` branch\n\nExample configuration:\n```json5\n{\n  \"stainlessSdkRepos\": {\n    \"typescript\": {\n      \"staging\": \"git@github.com:stainless-sdks/my-api-typescript.git\",\n      \"prod\": \"git@github.com:my-org/my-api-typescript.git\"\n    }\n  },\n  \"defaults\": {\n    \"branch\": \"yourusername/dev\"  // Optional default branch\n  }\n}\n```\n\nBasic usage:\n```bash\n# Development: Uses staging URL (default)\n# Will use branch from config, env var, or generate a new cli/ branch\nstainless-tools generate typescript\n\n# Production: Uses prod URL with explicit branch\nstainless-tools generate typescript \\\n  --prod \\\n  --branch main\n\n# Using existing branch from target directory\n# If ./sdks/typescript exists and is on branch 'feature/new-api',\n# this will continue using that branch\nstainless-tools generate typescript\n```\n\n### How It Works\n\nWhen you run `generate`:\n\n1. Branch discovery:\n   - Checks for branch name in this order:\n     1. Command-line flag (`--branch`)\n     2. Environment variable (`STAINLESS_SDK_BRANCH`)\n     3. Configuration default (`defaults.branch`)\n     4. Current branch in target directory (if it exists)\n     5. Generates a new random `cli/\u003crandom-hex\u003e` branch\n\n2. If you provide an OpenAPI file (`--open-api-file`) or Stainless config file (`--stainless-config-file`):\n   - Publishes these files to the Stainless Config repo via the Stainless API\n   - The API processes the files and generates the SDK in the specified branch\n\n3. Repository initialization:\n   - If the directory doesn't exist:\n     - Creates and initializes a new git repository\n     - Waits for the branch to exist if it doesn't yet\n     - Once the branch exists, checks it out fresh\n   - If the directory exists:\n     - Verifies it's the correct repository\n     - If on a different branch:\n       - Stashes any local changes\n       - Waits for the target branch if it doesn't exist\n       - Switches to the branch once it exists\n       - Restores any stashed changes\n     - Pulls latest changes\n\n4. Continuous monitoring:\n   - Watches for OpenAPI and config file changes:\n     - Automatically publishes changes to Stainless\n     - Waits for SDK regeneration\n   - Monitors SDK repository for updates:\n     - Pulls new changes when detected\n     - Handles local changes by:\n       - Stashing before updates\n       - Reapplying after updates\n       - Providing clear instructions if conflicts occur\n   - Executes lifecycle hooks:\n     - Runs `postClone` after initial setup\n     - Runs `postUpdate` after pulling changes\n\n5. Handles interruptions gracefully:\n   - Ctrl+C stops the monitoring process\n   - Restores any stashed changes before exit\n   - Cleans up watchers and temporary state\n\nThis workflow ensures:\n- Seamless coordination with CI/CD systems that create branches\n- Safe handling of local changes during updates\n- Proper sequencing of publish and branch operations\n- Clear feedback during long-running operations\n- Graceful handling of all error cases\n\n## Publish Specs Command\n\nThe `publish-specs` command allows you to publish your OpenAPI specification and Stainless configuration directly to Stainless, making it easy to update your SDK configuration without using the web interface.\n\n### Usage\n\n```bash\nstainless-tools publish-specs [options] \u003csdk-name\u003e\n\nArguments:\n  sdk-name     Name of the SDK to publish specifications for\n\nOptions:\n  -b, --branch \u003cbranch\u003e                Git branch to use (optional)\n  -t, --target-dir \u003cdir\u003e               Directory where the SDK will be generated\n  -o, --open-api-file \u003cfile\u003e           Path to OpenAPI specification file\n  -c, --config \u003cfile\u003e                  Path to configuration file\n  -s, --stainless-config-file \u003cfile\u003e   Path to Stainless-specific configuration\n  -p, --project-name \u003cname\u003e            Name of the project in Stainless\n  -g, --guess-config                   Use AI to guess configuration\n  --prod                               Use production URLs instead of staging\n```\n\n### Examples\n\n```bash\n# Basic usage - will use branch from config, env var, or generate new cli/ branch\nstainless-tools publish-specs typescript\n\n# With custom configuration and explicit branch\nstainless-tools publish-specs typescript \\\n  --branch main \\\n  --open-api-file openapi.json \\\n  --config config.json\n\n# Minimal required options\nstainless-tools publish-specs \\\n  --project-name my-project \\\n  --open-api-file openapi.json \\\n  typescript\n\n# With Stainless-specific configuration\nstainless-tools publish-specs \\\n  --stainless-config-file stainless.config.json \\\n  --open-api-file openapi.json \\\n  typescript\n\n# Production use with explicit branch\nstainless-tools publish-specs typescript \\\n  --prod \\\n  --branch main \\\n  --open-api-file openapi.json\n```\n\nWhen you run `publish-specs`:\n\n1. The command validates your input and configuration\n2. Reads the OpenAPI specification file\n3. Reads the Stainless configuration file (if provided)\n4. Validates the configuration format\n5. Publishes the files to Stainless using your API key\n\nThe publish-specs command is particularly useful for:\n- CI/CD pipelines where you want to automate SDK updates\n- Local development when you want to quickly update your SDK configuration\n- Testing new API changes before committing them to production\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheogravity%2Fstainless-tools","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftheogravity%2Fstainless-tools","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftheogravity%2Fstainless-tools/lists"}