Metadata-Version: 2.4
Name: coderpad-cli
Version: 2026.10.6
Summary: Upload starter code to existing CoderPad questions.
Author-email: Adam Dangoor <adamdangoor@gmail.com>
License-Expression: MIT
Project-URL: Documentation, https://adamtheturtle.github.io/coderpad-cli/
Project-URL: Issues, https://github.com/adamtheturtle/coderpad-cli/issues
Project-URL: Source, https://github.com/adamtheturtle/coderpad-cli
Keywords: cli,coderpad,starter-code
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Requires-Python: >=3.12
Description-Content-Type: text/x-rst
License-File: LICENSE
Requires-Dist: click<8.6,>=8.3
Requires-Dist: coderpad-py>=2026.10.1.1
Requires-Dist: httpx>=0.28
Requires-Dist: pathspec>=1
Requires-Dist: pydantic>=2.13.5
Dynamic: license-file

coderpad-cli
============

Upload starter code to an existing CoderPad Interview question using
``coderpad-py``.
This independent CLI preserves titles, descriptions, and other metadata by
updating only the supplied starter-code field.

Requires Python 3.12 or later.
No package release has been published yet.
Install the checkout for now:

.. code-block:: console

   uv sync --locked
   uv run coderpad --help
   uv run python -m coderpad_cli --version

After the first PyPI release, install with ``uv tool install coderpad-cli`` or
``pip install coderpad-cli``.

Usage
-----

Use synthetic question ID ``123456`` below in place of your own question ID.
Exactly one content source is required:

.. code-block:: console

   coderpad questions upload 123456 --directory ./starter --dry-run
   coderpad questions upload 123456 --directory ./starter --exclude '*.zip'
   coderpad questions upload 123456 --file ./starter.py

Real uploads read ``CODERPAD_API_KEY`` from the environment.
Get a key from your CoderPad dashboard settings.
Missing, empty, and whitespace-only keys fail clearly.
Dry runs need no key and make no network requests.

Single files must be UTF-8.
Their text is preserved exactly, including CRLF, blank lines, trailing
whitespace, a UTF-8 BOM, and documentation markers.
Empty files are valid.
Neither paths nor IDs are inferred from repository configuration.
Inputs are fully read or staged before a mutation is issued.
Errors exit nonzero.
API response bodies and credentials are never printed.
Uploads retain the SDK's retry behavior.

Question variants
-----------------

Create a variant once, using a language or project-template slug:

.. code-block:: console

   coderpad questions variants create 123456 --language python3 --dry-run
   coderpad questions variants create 123456 --language python3 > variant.json
   coderpad questions variants create 123456 --language javascript --file ./starter.js
   coderpad questions variants create 123456 --language multifile_python --directory ./starter --exclude '*.zip'

Successful creation writes only JSON to standard output, such as
``{"question_id": 123456, "variant_id": 7}``.
Save the returned ID in your upload configuration and use it for later updates.
The CLI does not save IDs or match existing variants by language or title.
Every real invocation creates a new variant and is never automatically retried.
Existing question metadata and other variants are not changed.

Omitting both sources uses the language or project template's default content.
Use ``--file`` for single-file starter code; an empty file explicitly creates
blank code instead of using the default.
Use ``--directory`` for project files, which are layered over the template at
creation rather than replacing all template files.
Source selection and UTF-8 validation match variant uploads.
The two sources are mutually exclusive; ``--exclude`` requires a directory.
CoderPad validates whether the supplied language or template is available.
Dry runs validate local inputs and print a JSON plan with the operation,
question ID, language, and selected files, but no invented variant ID.
They require neither credentials nor network access.
Errors go to standard error with a nonzero exit status.
If a creation response is lost or invalid, check the question before retrying
to avoid creating a duplicate.

To update an existing variant rather than the question's starter code, add
``--variant-id``:

.. code-block:: console

   coderpad questions upload 123456 --variant-id 7 --directory ./starter --exclude '*.zip' --dry-run
   coderpad questions upload 123456 --variant-id 7 --directory ./starter --exclude '*.zip'
   coderpad questions upload 123456 --variant-id 8 --file ./starter.py

Question and variant IDs must be positive decimal integers.
The variant must already exist.
Only its starter code changes; the question metadata, other variants, selected
language or project template, and solution are preserved.
Directory uploads replace the variant's files using the SDK's JSON API.
Every selected file must be UTF-8, including during a dry run.
Exclude binary assets explicitly or use a normal question upload, which
continues to support binary files through the SDK's ZIP importer.
File text is preserved exactly for both variant upload modes.
Dry runs identify the variant and selected files without requiring a key or
making a request.

Directory selection
-------------------

* Both Git and non-Git directories work.
  Paths are relative to the current working directory.
  A Git worktree's ``.git`` file also identifies its root.
* In a Git repository, rules are inherited from the nearest Git root down to
  the upload directory.
  Outside Git, rules start at the upload directory; unrelated parent
  ``.gitignore`` files are not used.
* Each traversed directory adds its own ``.gitignore`` rules.
  Later matching rules override earlier ones, and deeper files override
  ancestor rules.
  Patterns are relative to their owning directory.
  Directory-only rules and negations follow Gitignore semantics.
  Excluded directories are pruned, so a child cannot re-include itself unless
  its parent is re-included first.
  This also applies to ancestors of an explicitly selected nested upload root.
* Ignore rules apply to tracked files too.
  Global Git excludes and ``.git/info/exclude`` are not read.
  Hidden files, including ``.gitignore``, are uploaded unless excluded.
* ``.git`` files and directories are always excluded at every depth.
  Repeatable ``--exclude PATTERN`` options form a final Gitignore rule layer,
  relative to the upload root.
  This layer can exclude files re-included by ``.gitignore``; its own later
  negations can undo its earlier patterns.
  It cannot re-include a file excluded by ``.gitignore`` or Git metadata.
* ZIP files are included by default.
  Use ``--exclude '*.zip'`` to omit them.
  Add other exclusions explicitly, such as ``--exclude node_modules/``.
* Selected symlinks (including directory, broken, and external links) and
  symlinks in the source path are rejected rather than dereferenced.
  Ignored links are skipped.
  Symlinked ``.gitignore`` files are rejected when their rules would be read.
  Special files are rejected.
  Empty selections fail.
  File bytes are copied to a temporary directory, and the SDK independently
  validates and serializes that directory using its ZIP importer for normal
  question uploads.
  Variant uploads send the staged files as UTF-8 JSON content instead.

Development and distribution
----------------------------

Development checks, release setup, and build instructions for Docker, Nix, and
standalone binaries are in ``docs/source/development.rst``.
The generated CLI reference is in ``docs/source/cli.rst``.
There are no prebuilt artifacts yet.

See ``docs/source/migration.rst`` for migrating an existing shell uploader and
keeping repository-specific snippet preparation outside this CLI.
