Skip to main content

sp

The Spare Parts command line.

pip install "spareparts-cli[anthropic]"  # or [openai], [gemini], or [all]
sp

Modules

Module What it does
sp build Turn an ingested issue into validated pull requests
sp ec Install engineering-context commands for coding agents
sp ingest Enrich GitHub issue events into Core ingestion records
sp lgtm Prove you read a diff before you merge it
sp plugin Discover and install Spare Parts agent plugins
sp pr Reconcile and safely merge one managed pull request

sp build

sp build issue resolves authoritative affected repositories from Core, applies explicit organization/repository allowlists and a fan-out cap, prepares all approved repositories in one isolated workspace, and installs the published Spare Parts engineering context. One Codex or Claude invocation uses the workspace huddle command to drive every repository through specify, plan, tasks, and implement before the orchestrator validates and publishes one deterministically marked pull request per changed target:

export SPAREPARTS_API_KEY=...
export GITHUB_TOKEN=...           # short-lived GitHub App installation token
export OPENAI_API_KEY=...         # or supported Claude/subscription credential

sp build issue \
  --source-repository sparepartslabs/spareparts-distributor \
  --issue-number 42 --trigger-delivery-id "$GITHUB_DELIVERY_ID" \
  --build-job-id "$SP_BUILD_JOB_ID" --odl-run-id "$SP_ODL_RUN_ID" \
  --core-url "$SP_CORE_URL" --agent codex --allowed-org sparepartslabs \
  --max-fanout 3 --validation-command "python -m pytest"

Credentials are environment-only. The coding agent never commits, pushes, or creates PRs; the orchestrator does so only after policy and validation pass. The orchestrator mirrors canonical huddle status and repository stages into one marker-backed source-issue comment as the huddle changes. GitHub/Core mutation credentials remain outside the coding-agent process. After every intended target reaches pr_opened or no_change, Build submits one canonical merge instruction to Core. The default is a conservative serial topology with stop_dependents; --merge-strategy and --merge-failure-policy make a different explicit topology opt-in. A Core submission failure makes the Build result a partial failure, leaving opened PRs unmanaged and ineligible for PR Runner auto-merge until exact replay is accepted. Build commits use the repository-local identity Spare Parts Assembler <assembler@sparepartslabs.com> by default. Override it with BUILD_GIT_USER_NAME and BUILD_GIT_USER_EMAIL; global Git configuration is never modified. Safe tracked and untracked product files are published. Workspace huddles, specs/, .sp/ state, and generated agent commands remain local and are never included in product commits. Publication fails closed when the huddle or its repository task lists are incomplete. Use --dry-run to validate Core/GitHub plan authorization without cloning or publishing. The runner container/platform must enforce non-root filesystem, resource, process, and egress isolation for trusted repositories.


sp pr

sp pr reconcile is the provider-free, noninteractive entrypoint for a one-shot PR Runner task. It accepts only a Core-issued job identity, claims the authoritative merge-instruction entry, surfaces current required GitHub gates, and exits without polling when work is waiting:

export SP_CORE_URL=https://api.sparepartslabs.com
export SP_PR_JOB_CREDENTIAL=...  # short-lived bootstrap bearer
sp pr reconcile --job-id 11111111-1111-4111-8111-111111111111

The claim supplies a repository-scoped GitHub installation token and a separate completion credential. Neither is written to disk or included in stdout, stderr, gate evidence, or completion results. The command fails closed on incomplete order, Build blockers, stale heads, inaccessible policy, or unknown required gates; immediately before mutation it re-reads the PR head. It prefers a required merge queue, then repository-native auto-merge, and uses an ordinary allowed merge method only when appropriate. It never requests an administrator bypass or changes repository protection.

Completion and post-claim failure reports include only reconciliation timing, GitHub request count, and normalized gate/blocker counts. They never include tokens, authorization headers, raw GitHub responses, pull-request content, or provider output. A post-claim error is reported through Core with the separate completion credential and a bounded retryable/permanent category.

Build-side code can use the typed create_merge_instruction producer after all intended pull requests have stable repository, PR-node, head, base, dependency, preferred-method, and blocker identities. Canonical hashing makes exact publication replay deterministic.


sp ec

Installs engineering-context slash commands and their shared .sp/ working area into a repo.

sp ec install                    # auto-detect the coding agent
sp ec install --agent codex
sp ec install --agent claude
sp ec install --all
sp ec install --dir path/to/repo

When --dir is a folder of git repos, each repo gets its own commands and .sp/ area. The workspace root gets the cross-repo /huddle command and a workspace constitution, but no pooled repo scaffold.

The installer appends ignore rules for generated .sp state and templates, while keeping .sp/memory/constitution.md trackable. Blanket .sp/ ignores are left untouched and reported as warnings.

Codex commands are installed as repository skills under .agents/skills/<command>/SKILL.md. Codex is auto-detected from AGENTS.md, .agents/, or .codex/ in the target repository.

Guided integration setup

Run diagnostics after installation:

sp ec doctor
sp ec project setup

doctor reports the configured huddle store, GitHub CLI authentication, and whether a Linear MCP configuration or LINEAR_API_KEY is visible. Every missing capability includes the command or configuration needed to fix it.

Setup can also be scripted:

sp ec project setup --provider github --url https://github.com/orgs/example/projects/1
sp ec project setup --provider linear --url https://linear.app/example --team Engineering --transport mcp

GitHub Projects huddle store

Configure a workspace-level GitHub Project, then check access or synchronize a huddle manually:

sp ec project configure https://github.com/orgs/example/projects/1 --dir ..
sp ec project status --dir ..
sp ec project sync ../.sp/huddles/001-example/huddle.md --dry-run
sp ec project sync ../.sp/huddles/001-example/huddle.md

/huddle prefers installed GitHub MCP tools capable of managing Projects. It falls back to gh project through sp ec project sync. Each draft item carries a stable huddle-path marker, so later syncs update it instead of creating a duplicate. Markdown remains authoritative when remote synchronization fails.

Spare Parts workspace sync

Configure a workspace once at the workspace root. The ingest key stays in the environment and is never written to .sp/integrations.json:

sp ec workspace configure --workspace workspace_...
export SPAREPARTS_API_KEY=sp_...
sp ec workspace sync .sp/huddles/001-example/huddle.md
sp ec workspace sync --all

Download every huddle available to the configured workspace. Existing local files are preserved unless --force is passed:

export SPAREPARTS_READ_KEY=sp_...
sp ec workspace pull
sp ec workspace pull --force

Every revision includes the repository, branch, commit, dirty state, and the name/email resolved from git config user.name and git config user.email. Git identity is recorded as unverified until user login is introduced.

To record when artifacts reach main, run reconciliation from CI after every main-branch push:

on:
  push:
    branches: [main]
steps:
  - uses: actions/checkout@v7
    with:
      fetch-depth: 0
  - run: pipx install spareparts-cli
  - run: sp ec workspace reconcile --ref "$GITHUB_SHA" --dir .
    env:
      SPAREPARTS_API_KEY: ${{ secrets.SPAREPARTS_API_KEY }}

The local Markdown remains authoritative if synchronization fails. Repeated sync and reconciliation calls are idempotent.


sp ingest

Processes one GitHub issues event file, builds or reuses the organization's repository ontology, asks one configured model which catalog repositories are affected, gathers bounded pull-request approval evidence, and submits the safe, idempotent result to Core:

export GITHUB_TOKEN=...
export SPAREPARTS_API_KEY=...
export OPENAI_API_KEY=... # or ANTHROPIC_API_KEY / GEMINI_API_KEY

sp ingest issue "$GITHUB_EVENT_PATH" \
  --provider openai:gpt-5.5 \
  --core-url "$SP_CORE_URL" \
  --delivery-id "$GITHUB_DELIVERY_ID" \
  --writeback

--model overrides the model in vendor:model. --writeback posts an idempotent processing summary to the source issue only after Core accepts the ingestion record; it is off by default and requires a GitHub token with issue-write permission. Exactly one provider is used. Model, GitHub, and Core ingest keys are read from runtime environment variables only; they are never written to configuration, stdout, or Core records. Without --writeback, the command only reads GitHub and submits ingestion evidence. With it, the sole GitHub mutation is the managed summary comment; the command never assigns users, edits issue content, starts builds, or updates Project fields.

Routing resolves exact owner/repository, unique repository names, and known repository/component-path references before model inference. Explicit matches remain targets even when the model returns none. Ontology refreshes collect a stable, bounded set of README and manifest component summaries and hashes:

sp ingest issue "$GITHUB_EVENT_PATH" --core-url "$SP_CORE_URL" \
  --max-components 100 --max-component-requests 4 \
  --max-component-bytes 65536

Core owns semantic retrieval and its voyage-context-4 credentials/model. The CLI sends only ordered component metadata and the plain issue query—never embedding keys, models, or vectors. If semantic retrieval is unavailable, Core falls back lexically and ingestion continues.


sp plugin

Lists and installs the immutable plugin versions pinned by this sp release:

sp plugin list
sp plugin install lgtm
sp plugin install lgtm --refresh
sp plugin install lgtm --agent claude --dir path/to/repo
sp plugin install lgtm --agent cursor --agent gemini
sp plugin install lgtm --all --dir path/to/repo

Installation downloads the official archive and verifies its pinned SHA-256 digest. Use --agent for one or more project-native targets, or --all for the complete matrix supported by sp ec install: claude, codex, cursor, copilot, gemini, and opencode. Existing files are preserved unless --force is passed. Start a new session in each selected agent afterward.

The archive contains one canonical, agent-neutral LGTM command. The CLI renders only the destination wrapper required by each selected agent; it does not carry separate behavior for every vendor. Installation does not add a Git hook, edit memory or instruction files, or create a preference. The separate sp lgtm install command manages the optional local Git hook.

For backward compatibility, the command without --agent or --all uses the Codex marketplace adapter and installs lgtm@sparepartslabs. --refresh downloads and verifies that same pinned version. Explicit Codex mode instead writes .agents/skills/lgtm/SKILL.md like the other project-native adapters.


sp lgtm

Generates a few multiple-choice questions about a range of commits and asks them, in your terminal. The questions are about what the change does — what a new guard prevents, what the error path now returns, which edit can touch existing rows — never about statistics, naming, or formatting.

sp lgtm                       # what this branch adds since it left main
sp lgtm main...feature/x      # someone else's branch, before you merge it
sp lgtm -n 3 -d hard
sp lgtm --dry-run             # what it would ask about, no model call

Generation is three model calls, so expect it to take a moment — this is a thing you run before a merge, not on every commit.

Providers

Anthropic, OpenAI and Gemini, and no vendor is the assumed one. Install the SDK for whichever you use:

pip install ".[anthropic]"     # or [openai], or [gemini], or [all]

Name nobody and sp uses whichever key you have set. With more than one set it picks in the order below, which is a tie-break rather than a ranking; name provider: in .github/lgtm.yml to decide it yourself.

Vendor Key Default model Typical run
anthropic ANTHROPIC_API_KEY claude-opus-5 ~55s
openai OPENAI_API_KEY gpt-5.5 ~65s
gemini GEMINI_API_KEY or GOOGLE_API_KEY gemini-pro-latest ~115s
sp lgtm -p openai
sp lgtm -p gemini --model gemini-3.6-flash
sp lgtm -p anthropic --verifier openai     # see below

All three are exercised against live APIs, on a 32KB diff, and the timings above are from those runs.

The defaults were chosen by listing each vendor's models with a live key. Gemini uses the tracking alias because the vendor currently publishes no plain gemini-3.x-pro — only -image variants — so pinning a pro model would mean pinning to 2.5 indefinitely. The other two pin, because a default that changes underneath a quiz changes what the quiz asks.

Two vendors are better than one

sp lgtm writes a question with one call and then asks a second call to refute it. A candidate that can't be refuted survives; everything else is dropped.

Both calls going to the same model is the weak version of that check — a model asked to find fault with its own reasoning mostly doesn't. If you have keys for two vendors, split them:

sp lgtm --provider anthropic --verifier openai

That is the strongest arrangement available, and it is why the provider layer exists rather than a bare --model flag. It costs one extra vendor's tokens and nothing else — the verifier sees the question and the diff, never the proposer's reasoning.

It also costs no extra wall-clock in practice: proposing and verifying are sequential either way, and the cross-vendor run above came in at 68s against 55s for Anthropic alone.

It is a self-check, not a gate

sp lgtm has no way to stop you doing anything. The answers live in the same process as the questions, on your machine, and you can skip the whole thing. That is on purpose: the version that actually gates is the GitHub Action, which asks the reviewer after they approve and holds a check run open until they answer.

What local gets in exchange is the thing the Action can't have: the code is checked out. Press ? on any question to print the hunk it came from. Wrong answers are never a failure — you get told which files to look at again, and it re-asks, as many times as you like.

Configuration

Reads .github/lgtm.yml from the repo you're in, the same file the Action uses, so a repo is configured once:

questions: 2          # 1-5
difficulty: medium    # easy | medium | hard
provider: anthropic   # anthropic | openai | gemini, or vendor:model
verifier: openai      # optional; defaults to the proposer
exemptPaths:
  - "docs/**"

provider and verifier are read here but validated by the provider layer, so a typo is reported with the list of known vendors rather than silently ignored.

Keys that only mean something to the Action (enforce, webConcepts, surfaceReading, answerQuestions, exemptReviewers) are accepted and ignored. -n and -d override the file.

Lockfiles, dist/, vendor/, *.pbxproj and friends are never quizzed.

As a git hook

sp lgtm install                       # pre-push, advisory
sp lgtm install --hook pre-commit     # earlier, and once per commit
sp lgtm install --blocking            # wrong answers stop the push
sp lgtm uninstall

pre-push is the default. The old framing for this tool was "the person answering didn't write the code" — that's why it quizzes a reviewer. That framing is dated: when a model wrote the diff, nobody in the loop wrote it, and the author is as much a reader as anyone. The useful question isn't who typed it, it's where the last cheap moment to catch it is — and that's before the code leaves your machine, which is left of anything the Action can do.

It also matches how the cost lands. Generation is three model calls and about a minute; per push that's fine, per commit it taxes every checkpoint you save. --hook pre-commit is there if you want it, and quizzes the staged diff.

Advisory by default. A hook that costs a minute and can stop you is one you delete within a week. --blocking is opt-in, and even then only a wrong answer (exit 1) blocks — "couldn't ask" (exit 2: no API key, vendor outage, nothing quizzable) never costs you a push.

Escape hatches, in the order you'll want them:

SP_LGTM_SKIP=1 git push ...    # skip this once
git push --no-verify ...       # skip every hook

What it reads

pre-push quizzes exactly what you're about to push. Git names the refs on stdin, so the hook takes <remote sha>..<local sha> — the commits the remote doesn't have yet. A branch the remote has never seen has no such range, so it falls back to what the branch adds since it left the default branch. A push that only deletes a remote branch reads nothing at all.

That ref list arrives on the same stdin the quiz needs for answers, so the hook consumes it before attaching /dev/tty.

It skips itself silently when there's no terminal — a rebase, a GUI client, CI. Git runs hooks with stdin closed; where there's no tty, nobody can answer and nobody failed.

sp lgtm install refuses to overwrite a hook it didn't write (--force overrides), and honours core.hooksPath — writing to an assumed .git/hooks when that's set installs a hook that never runs, which looks exactly like success.

Exit codes

Code Meaning
0 Confirmed
1 Not confirmed — wrong answers, or you quit
2 Couldn't ask — no API key, git failed, nothing quizzable

If you wire this into a git hook, treat only 1 as a failure. A tool that cannot run must not block a commit.

Development

python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest

lgtm's diff parsing, config, screening and generator are a port of the TypeScript in spareparts-lgtm. The prompts in generator.py are the part worth keeping identical — changing the wording here without changing it there produces two tools that disagree about the same diff.

SPAREPARTS_API_KEY is the canonical Core credential. During the compatibility window, commands accept SPAREPARTS_INGEST_KEY only when the canonical variable is absent.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

spareparts_cli-0.22.0.tar.gz (238.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

spareparts_cli-0.22.0-py3-none-any.whl (191.0 kB view details)

Uploaded Python 3

File details

Details for the file spareparts_cli-0.22.0.tar.gz.

File metadata

  • Download URL: spareparts_cli-0.22.0.tar.gz
  • Upload date:
  • Size: 238.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for spareparts_cli-0.22.0.tar.gz
Algorithm Hash digest
SHA256 4cf6cc493942a18c8e9776bfbab3a89381d8778ebe1cef1735b6a7ca4e77e9e1
MD5 704a5241c73c4e64b95f535f105551bf
BLAKE2b-256 458bea737df960e326206b6692798e030f21b633b7dd46c8ef6a71fa17e6c3b1

See more details on using hashes here.

Provenance

The following attestation bundles were made for spareparts_cli-0.22.0.tar.gz:

Publisher: publish.yml on sparepartslabs/spareparts-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file spareparts_cli-0.22.0-py3-none-any.whl.

File metadata

  • Download URL: spareparts_cli-0.22.0-py3-none-any.whl
  • Upload date:
  • Size: 191.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for spareparts_cli-0.22.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d52353f4bf1b296f246ec08739b8a1860af778cf2a58f2dd1efdb4cfc2653b71
MD5 650990ad303b6bc3940ed217a54c5dc8
BLAKE2b-256 4b4ef06eb4218071a7198c8f7315e86fdac962dc63e49aeb453decbea5d4cb24

See more details on using hashes here.

Provenance

The following attestation bundles were made for spareparts_cli-0.22.0-py3-none-any.whl:

Publisher: publish.yml on sparepartslabs/spareparts-cli

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.28.0

2 files

0.27.0

2 files

0.26.0

2 files

0.25.0

2 files

0.24.1

2 files

0.24.0

2 files

0.23.0

2 files

This release

0.22.0 This release

2 files

0.21.0

2 files

0.20.0

2 files

0.19.0

2 files

0.18.3

2 files

0.18.2

2 files

0.18.1

2 files

0.18.0

2 files

0.17.0

2 files

0.16.0

2 files

0.15.0

2 files

0.14.1

2 files

0.14.0

2 files

0.13.3

2 files

0.13.2

2 files

0.13.1

2 files

0.13.0

2 files

0.12.3

2 files

0.12.2

2 files

0.12.1

2 files

0.12.0

2 files

0.11.1

2 files

0.11.0

2 files

0.10.0

2 files

0.9.0

2 files

0.8.1

2 files

0.8.0

2 files

0.7.0

2 files

0.6.0

2 files

0.5.3

2 files

0.5.2

2 files

0.5.1

2 files

0.5.0

2 files

0.4.1

2 files

0.4.0

2 files

0.3.2

2 files

0.3.1

2 files

0.3.0

2 files

0.2.0

2 files

0.1.0

2 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