Skip to main content

FileSpecContractor (fsc)

Python Typer Pydantic Rich httpx python-dotenv License Tests Ruff Pyright

Token-saving contracts for your codebase.

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.

Why?

Free 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.

Born from the frustration of trying to vibe-code on a student laptop.

Installation

# Install with uv
uv tool install file_spec_contractor

# Or with pip
pip install file_spec_contractor

After installation, the fsc command is available globally:

fsc --help

For Scala users

The fsc command may conflict with the Scala Fast Offline Compiler which also uses the fsc name. If both are installed, use the full package name instead:

file-spec-contractor init
file-spec-contractor generate
file-spec-contractor --help

# or with underscore
file_spec_contractor init
file_spec_contractor generate

fsc automatically detects Scala environments and shows a warning if a conflict is possible.

Usage

# Set up configuration with defaults
fsc init

# Init in a specific directory
fsc init /path/to/project

# Init with custom settings
fsc init --extensions .py --extensions .kt --language ru

# Init with a different provider
fsc init --provider deepseek

# Recreate from scratch (removes existing .fsc/ and specs)
fsc init --force

# Same without confirmation prompt
fsc init --force -y

# Remove all fsc artifacts (.fsc/ and *.fsc.md files)
fsc deinit

# Remove only generated specs, keep config
fsc clean

# Skip confirmation
fsc clean -y

# Recreate configuration from scratch (deinit + init)
fsc reinit

# Reinit with custom flags
fsc reinit --extensions .py --extensions .kt --language ru

# Init with custom model
fsc init --model deepseek-reasoner

# Generate specifications for current directory (scan mode, per-file by default)
fsc generate

# Generate with a specific model
fsc generate --model openai/gpt-4o-mini

# Generate for specific files
fsc generate --files src/machine.py

# Generate with custom extensions
fsc generate --extensions .py .kt

# Force per-file mode with parallel requests
fsc generate --gen-mode per-file -c 5

# Preview what would be generated (no files written)
fsc generate --dry-run --verbose

# Regenerate all specs ignoring cache
fsc generate -f

# Check version
fsc --version

Generation Modes

Mode Flag Behaviour
per-file (default) Each file separately, one at a time.
bulk --gen-mode bulk All files in a single LLM request. Consistent, cross-referenced specifications.
per-file parallel -c N N files simultaneously via thread pool. Fastest for large projects.

If bulk mode fails to produce parsable output, fsc automatically falls back to per-file generation.

Commands

Command Description
init [dir] Create .fsc/ with config and prompt. Accepts optional target directory and all config flags. Use --force to recreate from scratch.
clean [dir] Remove *.fsc.md files, keep .fsc/ configuration.
deinit [dir] Remove .fsc/ and all *.fsc.md files. Prompts for confirmation unless -y.
reinit [dir] init --force equivalent. Removes all artifacts, then creates fresh .fsc/. Prompts for confirmation unless -y.
generate Generate *.fsc.md specifications.

Options

All options below are available on generate, init, and reinit (except --files, --dry-run, --verbose which are generate-only).

Option Description
--force Recreate config from scratch (init/reinit)
-y, --yes Skip confirmation prompts on destructive operations
--files Specific files to generate specs for (generate only, repeatable)
--extensions File extensions to include (default: .py)
--exclude-dirs Directories to skip
--exclude-files File patterns to skip
--provider LLM provider: openrouter (default) or deepseek
--model Model name for the selected provider
--api-key API key for the selected provider
--output-mode mirror (default), adjacent, or batch
--output-dir Output directory for mirror/batch mode (default: .fsc/specs)
--batch-size Files per folder in batch mode (default: 50)
--prompt-file Custom system prompt file
--language Prompt language: en (default) or ru (init/reinit only)
-c, --concurrency Parallel requests for per-file mode (default: 3)
--gen-mode Generation mode: per-file (default), bulk, per-file-parallel
-f, --force Regenerate all specs, ignoring cache
--dry-run Preview without writing files or calling API (generate only)
--verbose Detailed output (generate only)
--version Show version and exit

Configuration

fsc looks for configuration in this order (later sources override earlier ones):

  1. CLI arguments (highest priority)
  2. .fsc/config.toml in your project root
  3. ~/.config/fsc/config.toml for user-wide settings

Creating config

fsc init

This creates:

  • .fsc/config.toml - project configuration
  • .fsc/PROMPT.md - custom system prompt (optional, built-in prompt is used as fallback)

Example .fsc/config.toml

# Which files to scan and which to skip
[project]
extensions = [".py", ".kt"]
exclude_dirs = [".venv", "venv", ".git", "__pycache__", "tests"]
exclude_files = ["setup.py", "conftest.py"]

# Output language and mode
[output]
language = "en"          # "en" or "ru"
output_mode = "mirror"   # "mirror", "adjacent", or "batch"
output_dir = ".fsc/specs"
batch_size = 50          # files per folder (batch mode)

# LLM provider
[api]
provider = "openrouter"        # "openrouter" or "deepseek"
# model = "deepseek-chat"      # optional; omit to use provider default

# Custom system prompt file (relative to project root)
[prompt]
file = ".fsc/PROMPT.md"

# Generation runtime settings
[runtime]
concurrency = 3            # parallel threads for per-file mode
generation_mode = "per-file" # "per-file", "bulk", or "per-file-parallel"

API Key

API keys are never stored in config files. Three ways to provide them (in priority order):

  1. CLI flag - --api-key (highest priority)
  2. Environment variable - OPEN_ROUTER_API_KEY / DEEPSEEK_API_KEY
  3. .env file in project root (lowest priority)

OpenRouter (default):

# Option 1: CLI flag
fsc generate --api-key sk-or-v1-...

# Option 2: environment variable
export OPEN_ROUTER_API_KEY=sk-or-v1-...

# Option 3: .env file
echo "OPEN_ROUTER_API_KEY=sk-or-v1-..." > .env

DeepSeek (alternative):

fsc generate --provider deepseek --api-key sk-...
# or: export DEEPSEEK_API_KEY=sk-...
# or: echo "DEEPSEEK_API_KEY=sk-..." > .env

Providers

Provider Model Free Env var
OpenRouter (default) openai/gpt-oss-120b:free yes OPEN_ROUTER_API_KEY
DeepSeek deepseek-chat no DEEPSEEK_API_KEY

Switch provider via config or CLI:

fsc generate --provider deepseek

Output Modes

Mode Behaviour
adjacent Saves file.fsc.md right next to file.py
mirror Saves to output_dir, preserving directory structure (e.g., src/machine.py → .fsc/specs/src/machine.fsc.md)
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).

Batch mode example (batch_size = 50, 120 files):

.fsc/batches/
├── batch-1/
│   ├── src__controllers__UserController.fsc.md
│   └── ... (49 more files)
├── batch-2/
│   ├── src__models__Product.fsc.md
│   └── ... (49 more files)
└── batch-3/
    └── ... (20 files)

Configure via CLI or config:

fsc init --output-mode batch --batch-size 100
fsc generate --output-mode batch --batch-size 50

When output mode is changed, existing specs are automatically moved to the new location instead of being regenerated.

# .fsc/config.toml
[output]
output_mode = "batch"
output_dir = ".fsc/batches"
batch_size = 50

Prompt

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:

  1. --prompt-file CLI argument
  2. .fsc/PROMPT.md in project root
  3. Built-in prompt from the package

If no prompt file is found, a warning is shown and the built-in prompt is used.

How It Works

  1. Scans your project for files matching configured extensions
  2. Sends files to the LLM one-by-one (per-file mode, default) or all in a single request (bulk mode)
  3. The LLM generates structured .fsc.md specifications
  4. Saves the specifications as file.<ext>.fsc.md - ready to be fed to any LLM agent
  5. On subsequent runs, skips unchanged files. If output mode changed, moves specs instead of regenerating.

Specification Format

Each generated spec follows this structure:

  • Purpose - what this file does
  • Dependencies - external libs and internal modules
  • Public API - all public methods with signatures and notes
  • Implementation Notes - sentinels, patterns, non-obvious details
  • Handle with Care - contracts that are easy to break
  • Code Style - conventions used in this file

Requirements

  • Python 3.12+
  • uv for dependency management
  • OpenRouter or DeepSeek API key (see API Key)
  • .env file support via python-dotenv

Tech Stack

Component Library
CLI Typer
Validation Pydantic
Logging Rich
HTTP httpx
Config python-dotenv
Testing pytest

Development

# Clone and install in editable mode
git clone https://github.com/UmbrellaLeaf5/file_spec_contractor.git
cd file_spec_contractor
uv sync

# Run all tests (63 tests)
uv run pytest

# Run specific test file
uv run pytest tests/test_deepseek.py -v

# Run CLI in dev
uv run fsc --help

# Build package
uv build

Roadmap

  • Core CLI with init, generate, deinit, reinit commands
  • DeepSeek API integration
  • OpenRouter API integration (free gpt-oss-120b model)
  • Multi-provider support with --provider flag
  • Bulk generation mode (all files in one request with fallback)
  • Parallel per-file generation (--force-per-file -c N)
  • Spec caching with --force to regenerate
  • Configuration file support (TOML) with Pydantic validation
  • .env file support for API keys (via python-dotenv)
  • All config flags available on init, reinit, and generate
  • Batch output mode, mirror, and adjacent
  • Prompt resolution (project file → built-in fallback, per-language)
  • Multi-language prompt support (en, ru)
  • Installable CLI entry point (fsc)
  • Graceful shutdown on Ctrl+C
  • 63 tests (unit, integration, CLI)
  • Spec auto-move on output mode change (no wasted regeneration)
  • fsc --version and setuptools-scm versioning
  • fsc init <dir> - initialise in any directory
  • CI pipeline with GitHub Actions
  • --force / --yes / confirmation prompts for destructive commands
  • PyPI publish automation
  • --update flag for incremental regeneration
  • clean command just to delete all specs
  • Rich progress bars for large projects
  • Local model support (Ollama, LM Studio)
  • check if Scala is used and make warning not to use short name
  • add long name usage (file-spec-contractor or file_spec_contractor instead of fsc)
  • Publish to PyPI (pip install file_spec_contractor)
  • VS Code extension (generate specs from context menu / command palette)

Metadata

Release files for file-spec-contractor 0.3.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for file-spec-contractor 0.3.0
File Size Uploaded
file_spec_contractor-0.3.0.tar.gz 84.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for file-spec-contractor 0.3.0
File Interpreter ABI Platform
file_spec_contractor-0.3.0-py3-none-any.whl Python 3 none any Details

Total release size: 145.1 kB

Release files / file_spec_contractor-0.3.0.tar.gz

Download URL file_spec_contractor-0.3.0.tar.gz
Size 84.0 kB
Tags Source
SHA-256 checksum
How to use checksums
621c6c68c7f5118c4e78417f1dc0545f845d36b1d4d4594372fd543dea951352
BLAKE2b-256 checksum
How to use checksums
b257ab17bfb89c07fc078291d3636d7ae344fb73dfb17adeb4e1fc0f37237eb9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 29, 2026.

Transparency log

Release files / file_spec_contractor-0.3.0-py3-none-any.whl

Download URL file_spec_contractor-0.3.0-py3-none-any.whl
Size 61.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b230d1134d2b45c4470f67047e8dea60d0d9ad46dfd90a6f035f1545f171fca7
BLAKE2b-256 checksum
How to use checksums
9349292c8b4f64572dc83a48879a6774815f75b67d553ff0c339b9108c2c08a2
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Apr 29, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.3.0 This release

2 release files

0.2.12

2 release files

0.2.11

2 release files

0.2.10

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page