{"id":50299114,"url":"https://github.com/umbrellaleaf5/file_spec_contractor","last_synced_at":"2026-05-28T11:02:19.565Z","repository":{"id":352420299,"uuid":"1215070164","full_name":"UmbrellaLeaf5/file_spec_contractor","owner":"UmbrellaLeaf5","description":"Generate token-efficient code contracts for LLMs. Understand your codebase without burning through free-tier context limits. Built for developers who vibe-code on a budget.","archived":false,"fork":false,"pushed_at":"2026-05-04T15:30:50.000Z","size":273,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-04T17:29:04.750Z","etag":null,"topics":["ai","cli","codebase","developer-tools","documentation","documentation-generator","documentation-tool","generator","python","specification","token"],"latest_commit_sha":null,"homepage":"https://pypi.org/project/file-spec-contractor/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"unlicense","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/UmbrellaLeaf5.png","metadata":{"files":{"readme":"README.md","changelog":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-04-19T12:47:22.000Z","updated_at":"2026-05-04T15:30:54.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/UmbrellaLeaf5/file_spec_contractor","commit_stats":null,"previous_names":["umbrellaleaf5/file_spec_contractor"],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/UmbrellaLeaf5/file_spec_contractor","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmbrellaLeaf5%2Ffile_spec_contractor","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmbrellaLeaf5%2Ffile_spec_contractor/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmbrellaLeaf5%2Ffile_spec_contractor/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmbrellaLeaf5%2Ffile_spec_contractor/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/UmbrellaLeaf5","download_url":"https://codeload.github.com/UmbrellaLeaf5/file_spec_contractor/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/UmbrellaLeaf5%2Ffile_spec_contractor/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33605379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-28T02:00:06.440Z","response_time":99,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["ai","cli","codebase","developer-tools","documentation","documentation-generator","documentation-tool","generator","python","specification","token"],"created_at":"2026-05-28T11:02:17.422Z","updated_at":"2026-05-28T11:02:19.559Z","avatar_url":"https://github.com/UmbrellaLeaf5.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# FileSpecContractor (fsc)\n\n[![Python](https://img.shields.io/badge/Python-3.12+-blue?logo=python)](https://python.org)\n[![Typer](https://img.shields.io/badge/Typer-0.15+-blue)](https://typer.tiangolo.com/)\n[![Pydantic](https://img.shields.io/badge/Pydantic-2.0+-purple)](https://docs.pydantic.dev/)\n[![Rich](https://img.shields.io/badge/Rich-13.0+-brightgreen)](https://rich.readthedocs.io/)\n[![httpx](https://img.shields.io/badge/httpx-0.24+-orange)](https://www.python-httpx.org/)\n[![python-dotenv](https://img.shields.io/badge/dotenv-1.0+-yellow)](https://github.com/theskumar/python-dotenv)\n[![License](https://img.shields.io/badge/License-Unlicense-lightgrey)](https://unlicense.org)\n[![Tests](https://github.com/UmbrellaLeaf5/file_spec_contractor/workflows/Tests/badge.svg)](https://github.com/UmbrellaLeaf5/file_spec_contractor/actions/workflows/tests.yml)\n[![Ruff](https://github.com/UmbrellaLeaf5/file_spec_contractor/workflows/Ruff/badge.svg)](https://github.com/UmbrellaLeaf5/file_spec_contractor/actions/workflows/ruff.yml)\n[![Pyright](https://github.com/UmbrellaLeaf5/file_spec_contractor/workflows/Pyright/badge.svg)](https://github.com/UmbrellaLeaf5/file_spec_contractor/actions/workflows/pyright.yml)\n\n\u003e Token-saving contracts for your codebase.\n\n`fsc` is a command-line tool that generates descriptive specifications for your code files - compact \"contracts\" that help LLMs understand your project without burning through free-tier token limits.\n\n## Why?\n\nFree LLM models have strict context limits. Feeding them your entire codebase is expensive and often impossible. `fsc` creates lightweight `.fsc.md` files that capture the **public API and critical implementation details** of each file - enough for an agent to work with, small enough to fit in context.\n\nBorn from the frustration of trying to vibe-code on a student laptop.\n\n## Installation\n\n```bash\n# Install with uv\nuv tool install file_spec_contractor\n\n# Or with pip\npip install file_spec_contractor\n```\n\nAfter installation, the `fsc` command is available globally:\n\n```bash\nfsc --help\n```\n\n### For Scala users\n\nThe `fsc` command may conflict with the [Scala Fast Offline Compiler](https://www.scala-lang.org/) which also uses the `fsc` name. If both are installed, use the full package name instead:\n\n```bash\nfile-spec-contractor init\nfile-spec-contractor generate\nfile-spec-contractor --help\n\n# or with underscore\nfile_spec_contractor init\nfile_spec_contractor generate\n```\n\n`fsc` automatically detects Scala environments and shows a warning if a conflict is possible.\n\n## Usage\n\n```bash\n# Set up configuration with defaults\nfsc init\n\n# Init in a specific directory\nfsc init /path/to/project\n\n# Init with custom settings\nfsc init --extensions .py --extensions .kt --language ru\n\n# Init with a different provider\nfsc init --provider deepseek\n\n# Recreate from scratch (removes existing .fsc/ and specs)\nfsc init --force\n\n# Same without confirmation prompt\nfsc init --force -y\n\n# Remove all fsc artifacts (.fsc/ and *.fsc.md files)\nfsc deinit\n\n# Remove only generated specs, keep config\nfsc clean\n\n# Skip confirmation\nfsc clean -y\n\n# Recreate configuration from scratch (deinit + init)\nfsc reinit\n\n# Reinit with custom flags\nfsc reinit --extensions .py --extensions .kt --language ru\n\n# Init with custom model\nfsc init --model deepseek-reasoner\n\n# Generate specifications for current directory (scan mode, per-file by default)\nfsc generate\n\n# Generate with a specific model\nfsc generate --model openai/gpt-4o-mini\n\n# Generate for specific files\nfsc generate --files src/machine.py\n\n# Generate with custom extensions\nfsc generate --extensions .py .kt\n\n# Force per-file mode with parallel requests\nfsc generate --gen-mode per-file -c 5\n\n# Preview what would be generated (no files written)\nfsc generate --dry-run --verbose\n\n# Regenerate all specs ignoring cache\nfsc generate -f\n\n# Check version\nfsc --version\n```\n\n### Generation Modes\n\n| Mode                  | Flag              | Behaviour                                                                       |\n| --------------------- | ----------------- | ------------------------------------------------------------------------------- |\n| **per-file**          | _(default)_       | Each file separately, one at a time.                                            |\n| **bulk**              | `--gen-mode bulk` | All files in a single LLM request. Consistent, cross-referenced specifications. |\n| **per-file parallel** | `-c N`            | N files simultaneously via thread pool. Fastest for large projects.             |\n\nIf bulk mode fails to produce parsable output, `fsc` automatically falls back to per-file generation.\n\n### Commands\n\n| Command        | Description                                                                                                                            |\n| -------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `init [dir]`   | Create `.fsc/` with config and prompt. Accepts optional target directory and all config flags. Use `--force` to recreate from scratch. |\n| `clean [dir]`  | Remove `*.fsc.md` files, keep `.fsc/` configuration.                                                                                   |\n| `deinit [dir]` | Remove `.fsc/` and all `*.fsc.md` files. Prompts for confirmation unless `-y`.                                                         |\n| `reinit [dir]` | `init --force` equivalent. Removes all artifacts, then creates fresh `.fsc/`. Prompts for confirmation unless `-y`.                    |\n| `generate`     | Generate `*.fsc.md` specifications.                                                                                                    |\n\n### Options\n\nAll options below are available on `generate`, `init`, and `reinit` (except `--files`, `--dry-run`, `--verbose` which are `generate`-only).\n\n| Option                | Description                                                        |\n| --------------------- | ------------------------------------------------------------------ |\n| `--force`             | Recreate config from scratch (`init`/`reinit`)                     |\n| `-y`, `--yes`         | Skip confirmation prompts on destructive operations                |\n| `--files`             | Specific files to generate specs for (`generate` only, repeatable) |\n| `--extensions`        | File extensions to include (default: `.py`)                        |\n| `--exclude-dirs`      | Directories to skip                                                |\n| `--exclude-files`     | File patterns to skip                                              |\n| `--provider`          | LLM provider: `openrouter` (default) or `deepseek`                 |\n| `--model`             | Model name for the selected provider                               |\n| `--api-key`           | API key for the selected provider                                  |\n| `--output-mode`       | `mirror` (default), `adjacent`, or `batch`                         |\n| `--output-dir`        | Output directory for mirror/batch mode (default: `.fsc/specs`)     |\n| `--batch-size`        | Files per folder in batch mode (default: `50`)                     |\n| `--prompt-file`       | Custom system prompt file                                          |\n| `--language`          | Prompt language: `en` (default) or `ru` (`init`/`reinit` only)     |\n| `-c`, `--concurrency` | Parallel requests for per-file mode (default: `3`)                 |\n| `--gen-mode`          | Generation mode: `per-file` (default), `bulk`, `per-file-parallel` |\n| `--no-progress`       | Disable progress bars during generation                           |\n| `-f`, `--force`       | Regenerate all specs, ignoring cache                               |\n| `--dry-run`           | Preview without writing files or calling API (`generate` only)     |\n| `--verbose`           | Detailed output (`generate` only)                                  |\n| `--version`           | Show version and exit                                              |\n\n## Configuration\n\n`fsc` looks for configuration in this order (later sources override earlier ones):\n\n1. CLI arguments (highest priority)\n2. `.fsc/config.toml` in your project root\n3. `~/.config/fsc/config.toml` for user-wide settings\n\n### Creating config\n\n```bash\nfsc init\n```\n\nThis creates:\n\n- `.fsc/config.toml` - project configuration\n- `.fsc/PROMPT.md` - custom system prompt (optional, built-in prompt is used as fallback)\n\n### Example `.fsc/config.toml`\n\n```toml\n# Which files to scan and which to skip\n[project]\nextensions = [\".py\", \".kt\"]\nexclude_dirs = [\".venv\", \"venv\", \".git\", \"__pycache__\", \"tests\"]\nexclude_files = [\"setup.py\", \"conftest.py\"]\n\n# Output language and mode\n[output]\nlanguage = \"en\"          # \"en\" or \"ru\"\noutput_mode = \"mirror\"   # \"mirror\", \"adjacent\", or \"batch\"\noutput_dir = \".fsc/specs\"\nbatch_size = 50          # files per folder (batch mode)\n\n# LLM provider\n[api]\nprovider = \"openrouter\"        # \"openrouter\" or \"deepseek\"\n# model = \"deepseek-chat\"      # optional; omit to use provider default\n\n# Custom system prompt file (relative to project root)\n[prompt]\nfile = \".fsc/PROMPT.md\"\n\n# Generation runtime settings\n[runtime]\nconcurrency = 3            # parallel threads for per-file mode\ngeneration_mode = \"per-file\" # \"per-file\", \"bulk\", or \"per-file-parallel\"\nno_progress = false         # set to true to disable progress bars\n```\n\n### API Key\n\nAPI keys are **never stored in config files**. Three ways to provide them (in priority order):\n\n1. **CLI flag** - `--api-key` (highest priority)\n2. **Environment variable** - `OPEN_ROUTER_API_KEY` / `DEEPSEEK_API_KEY`\n3. **`.env` file** in project root (lowest priority)\n\n**OpenRouter** (default):\n\n```bash\n# Option 1: CLI flag\nfsc generate --api-key sk-or-v1-...\n\n# Option 2: environment variable\nexport OPEN_ROUTER_API_KEY=sk-or-v1-...\n\n# Option 3: .env file\necho \"OPEN_ROUTER_API_KEY=sk-or-v1-...\" \u003e .env\n```\n\n**DeepSeek** (alternative):\n\n```bash\nfsc generate --provider deepseek --api-key sk-...\n# or: export DEEPSEEK_API_KEY=sk-...\n# or: echo \"DEEPSEEK_API_KEY=sk-...\" \u003e .env\n```\n\n### Providers\n\n| Provider                 | Model                      | Free | Env var               |\n| ------------------------ | -------------------------- | ---- | --------------------- |\n| **OpenRouter** (default) | `openai/gpt-oss-120b:free` | yes  | `OPEN_ROUTER_API_KEY` |\n| DeepSeek                 | `deepseek-chat`            | no   | `DEEPSEEK_API_KEY`    |\n\nSwitch provider via config or CLI:\n\n```bash\nfsc generate --provider deepseek\n```\n\n### Output Modes\n\n| Mode       | Behaviour                                                                                                                                                                                                     |\n| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `adjacent` | Saves `file.fsc.md` right next to `file.py`                                                                                                                                                                   |\n| `mirror`   | Saves to `output_dir`, preserving directory structure (e.g., `src/machine.py` → `.fsc/specs/src/machine.fsc.md`)                                                                                              |\n| `batch`    | Groups specs into numbered folders `batch-1/`, `batch-2/`, etc. File names encode the original path (e.g., `src/machine.py` → `src__machine.fsc.md`). Folder size controlled by `batch_size` (default: `50`). |\n\n**Batch mode example** (`batch_size = 50`, 120 files):\n\n```\n.fsc/batches/\n├── batch-1/\n│   ├── src__controllers__UserController.fsc.md\n│   └── ... (49 more files)\n├── batch-2/\n│   ├── src__models__Product.fsc.md\n│   └── ... (49 more files)\n└── batch-3/\n    └── ... (20 files)\n```\n\nConfigure via CLI or config:\n\n```bash\nfsc init --output-mode batch --batch-size 100\nfsc generate --output-mode batch --batch-size 50\n```\n\nWhen output mode is changed, existing specs are automatically moved to the new location instead of being regenerated.\n\n```toml\n# .fsc/config.toml\n[output]\noutput_mode = \"batch\"\noutput_dir = \".fsc/batches\"\nbatch_size = 50\n```\n\n### Prompt\n\n`fsc` sends a system prompt to the LLM that defines the specification format. Built-in prompts are versioned per language: `fsc_en_5.md`, `fsc_ru_5.md`. The latest version is always used. Resolution order:\n\n1. `--prompt-file` CLI argument\n2. `.fsc/PROMPT.md` in project root\n3. Built-in prompt from the package\n\nIf no prompt file is found, a warning is shown and the built-in prompt is used.\n\n## How It Works\n\n1. Scans your project for files matching configured extensions\n2. Sends files to the LLM one-by-one (per-file mode, default) or all in a single request (bulk mode)\n3. The LLM generates structured `.fsc.md` specifications\n4. Saves the specifications as `file.\u003cext\u003e.fsc.md` - ready to be fed to any LLM agent\n5. On subsequent runs, skips unchanged files. If output mode changed, moves specs instead of regenerating.\n6. Shows progress bars by default (spinner for LLM wait, bar for file processing). Use `--no-progress` to disable.\n\n## Specification Format\n\nEach generated spec follows this structure:\n\n- **Purpose** - what this file does\n- **Dependencies** - external libs and internal modules\n- **Public API** - all public methods with signatures and notes\n- **Implementation Notes** - sentinels, patterns, non-obvious details\n- **Handle with Care** - contracts that are easy to break\n- **Code Style** - conventions used in this file\n\n## Requirements\n\n- Python 3.12+\n- [uv](https://docs.astral.sh/uv/) for dependency management\n- OpenRouter or DeepSeek API key (see [API Key](#api-key))\n- `.env` file support via [python-dotenv](https://github.com/theskumar/python-dotenv)\n\n## Tech Stack\n\n| Component  | Library                                                     |\n| ---------- | ----------------------------------------------------------- |\n| CLI        | [Typer](https://typer.tiangolo.com/)                        |\n| Validation | [Pydantic](https://docs.pydantic.dev/)                      |\n| Logging    | [Rich](https://rich.readthedocs.io/)                        |\n| HTTP       | [httpx](https://www.python-httpx.org/)                      |\n| Config     | [python-dotenv](https://github.com/theskumar/python-dotenv) |\n| Testing    | [pytest](https://docs.pytest.org/)                          |\n\n## Development\n\n```bash\n# Clone and install in editable mode\ngit clone https://github.com/UmbrellaLeaf5/file_spec_contractor.git\ncd file_spec_contractor\nuv sync\n\n# Run all tests (63 tests)\nuv run pytest\n\n# Run specific test file\nuv run pytest tests/test_deepseek.py -v\n\n# Run CLI in dev\nuv run fsc --help\n\n# Build package\nuv build\n```\n\n## Roadmap\n\n- [x] Core CLI with `init`, `generate`, `deinit`, `reinit` commands\n- [x] DeepSeek API integration\n- [x] OpenRouter API integration (free `gpt-oss-120b` model)\n- [x] Multi-provider support with `--provider` flag\n- [x] Bulk generation mode (all files in one request with fallback)\n- [x] Parallel per-file generation (`--force-per-file -c N`)\n- [x] Spec caching with `--force` to regenerate\n- [x] Configuration file support (TOML) with Pydantic validation\n- [x] `.env` file support for API keys (via python-dotenv)\n- [x] All config flags available on `init`, `reinit`, and `generate`\n- [x] Batch output mode, mirror, and adjacent\n- [x] Prompt resolution (project file → built-in fallback, per-language)\n- [x] Multi-language prompt support (en, ru)\n- [x] Installable CLI entry point (`fsc`)\n- [x] Graceful shutdown on Ctrl+C\n- [x] 63 tests (unit, integration, CLI)\n- [x] Spec auto-move on output mode change (no wasted regeneration)\n- [x] `fsc --version` and setuptools-scm versioning\n- [x] `fsc init \u003cdir\u003e` - initialise in any directory\n- [x] CI pipeline with GitHub Actions\n- [x] `--force` / `--yes` / confirmation prompts for destructive commands\n- [x] PyPI publish automation\n- [ ] `--update` flag for incremental regeneration\n- [x] clean command just to delete all specs\n- [ ] Rich progress bars for large projects\n- [ ] Local model support (Ollama, LM Studio)\n- [x] check if Scala is used and make warning not to use short name\n- [x] add long name usage (file-spec-contractor or file_spec_contractor instead of fsc)\n- [x] Publish to PyPI (`pip install file_spec_contractor`)\n- [ ] VS Code extension (generate specs from context menu / command palette)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fumbrellaleaf5%2Ffile_spec_contractor","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fumbrellaleaf5%2Ffile_spec_contractor","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fumbrellaleaf5%2Ffile_spec_contractor/lists"}