## Problem
FCC still publishes `fcc-init` and the `free-claude-code` server alias
from an obsolete installation flow. Configuration creation now belongs
to the Admin UI, while `fcc-server` owns startup and legacy migration.
## Changes
| Before | After |
| --- | --- |
| The package installs six console commands. | The package installs only
`fcc-server`, `fcc-claude`, `fcc-codex`, and `fcc-pi`. |
| `fcc-init` duplicates configuration creation outside the Admin UI. |
The Admin UI exclusively creates managed configuration, and server
startup retains legacy migration. |
| Smoke contracts and architecture describe the retired commands. |
Smoke contracts and architecture describe the supported command surface.
|
| Installer safety lists contain unexplained retired names. | Installer
safety lists document why retired names remain detectable during updates
and uninstall. |
| The package version is `4.11.4`. | The package version is `4.11.5`. |
<!-- greptile_comment -->
<details open><summary><h3>Greptile Summary</h3></summary>
This PR retires the obsolete initialization command and server alias.
The main changes are:
- Removes `fcc-init` and `free-claude-code` from package scripts.
- Moves fresh configuration creation exclusively to the Admin UI.
- Keeps legacy environment migration in `fcc-server` startup.
- Updates smoke coverage, installer comments, documentation, and package
metadata.
</details>
<h3>Confidence Score: 4/5</h3>
The CLI removal needs a compatible release version or temporary aliases
before merging.
Existing automation can lose `fcc-init` and `free-claude-code` after a
patch upgrade. Fresh installs can still start the server and create
configuration through the Admin UI.
pyproject.toml and uv.lock; legacy configuration migration remains
before settings loading.
<details><summary><h3><a href="https://www.greptile.com/trex"><img
alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="20" align="absmiddle"></a> T-Rex Logs</h3></summary>
**What T-Rex did**
- I reproduced the patch upgrade path by installing base revision
d37b34b02f in an isolated Python 3.14
environment and verified that fcc-init --version and free-claude-code
--version reported 4.11.4 with exit code 0. After upgrading to head
revision 9a65f654f5e06c0ca96a31d3ad03db8c63d8dec7, the installed version
changed to 4.11.5 and both entrypoints disappeared, with subsequent
invocations yielding command-not-found errors (exit code 127).
- Local smoke tests and server migrations completed successfully: the
CLI local smoke test exited with code 0, the installed-entrypoints
checks passed, and the server migrated to a healthy state with the
health endpoint responding.
<a
href="https://app.greptile.com/trex/runs/15196662/artifacts"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifactsDark.svg?v=4"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"><img
alt="View all artifacts"
src="https://greptile-static-assets.s3.amazonaws.com/badges/ViewAllArtifacts.svg?v=4"></picture></a>
<sub><a href="https://www.greptile.com/trex"><img alt="T-Rex"
src="https://greptile-static-assets.s3.amazonaws.com/trex/trex_green.svg"
height="14" align="absmiddle"></a> Ran code and verified through
T-Rex</sub>
</details>
<details open><summary><h3>Important Files Changed</h3></summary>
| Filename | Overview |
|----------|----------|
| pyproject.toml | Removes two public console commands while applying a
patch version bump. |
| src/free_claude_code/cli/commands.py | Removes template-based
initialization while preserving startup migration. |
| src/free_claude_code/cli/entrypoints.py | Removes the retired
initialization entrypoint and retains server version handling. |
| smoke/lib/child_process.py | Removes the init helper and renames the
server helper consistently. |
| scripts/install.sh | Keeps retired names in process detection for safe
upgrades. |
| scripts/uninstall.sh | Keeps retired names in removal checks for older
installations. |
| uv.lock | Tracks the patch version that must change if the command
removal remains breaking. |
</details>
<a
href="https://app.greptile.com/api/ide/codex?prompt=IMPORTANT%3A%20Work%20in%20the%20repository%20%22alishahryar1%2Ffree-claude-code%22%20on%20the%20existing%20branch%20%22remove-obsolete-cli-commands%22.%20Checkout%20that%20branch%20%E2%80%94%20do%20NOT%20create%20a%20new%20branch%20or%20open%20a%20new%20PR.%20Push%20your%20changes%20to%20%22remove-obsolete-cli-commands%22.%0A%0AFix%20the%20following%201%20code%20review%20issue.%20Work%20through%20them%20one%20at%20a%20time%2C%20proposing%20concise%20fixes.%0A%0A---%0A%0A%23%23%23%20Issue%201%20of%201%0Apyproject.toml%3A7%0A**Breaking%20Commands%20Ship%20as%20Patch**%0A%0AUpgrading%20from%204.11.4%20removes%20the%20published%20%60fcc-init%60%20and%20%60free-claude-code%60%20commands%2C%20so%20existing%20scripts%20fail%20with%20%E2%80%9Ccommand%20not%20found.%E2%80%9D%20The%20repository%20policy%20classifies%20incompatible%20CLI%20removals%20as%20major%20changes%3B%20keep%20compatibility%20aliases%20for%204.x%20or%20publish%20this%20removal%20as%205.0.0.%0A%0A&repo=alishahryar1%2Ffree-claude-code&pr=1220&platform=github"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodexDark.svg?v=6"><source
media="(prefers-color-scheme: light)"
srcset="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"><img
alt="Fix All in Codex"
src="https://greptile-static-assets.s3.amazonaws.com/badges/FixAllInCodex.svg?v=6"></picture></a>
<sub>Reviews (1): Last reviewed commit: ["Remove obsolete CLI entry
points"](https://github.com/alishahryar1/free-claude-code/commit/9a65f654f5e06c0ca96a31d3ad03db8c63d8dec7)
| [Re-trigger
Greptile](https://app.greptile.com/api/retrigger?id=45848525)</sub>
> Greptile also left **1 inline comment** on this PR.
<!-- /greptile_comment -->
Product E2E Smoke Tests
smoke/ is local-only. It can launch subprocesses, call real providers, touch
local model servers, and optionally send/delete bot messages. Hermetic contracts
belong under tests/ and must stay green with plain uv run pytest.
Taxonomy
smoke/prereq/: liveness checks that prove the server, routes, auth, CLI scripts, provider pings, local/models, and bot permissions are reachable. These are prerequisites only.smoke/product/: end-to-end product scenarios. Feature smoke coverage comes from these tests, not from route/header/provider pings.smoke/features.py: source-of-truth feature map: feature -> subfeature -> scenario -> env -> expected behavior -> failure class.
Required Local Commands
uv run pytest smoke --collect-only -q
uv run pytest smoke -n 0 -s --tb=short
The second command skips everything unless FCC_LIVE_SMOKE=1 is set, but still
writes skip entries to .smoke-results/.
Product Smoke Run
$env:FCC_LIVE_SMOKE = "1"
uv run pytest smoke -n 0 -s --tb=short
Provider smoke scenarios can run providers in parallel while preserving sequential execution within each provider:
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_TARGETS = "providers"
uv run pytest smoke -n auto --dist=loadgroup -s --tb=short
Provider product E2E runs once per configured provider, independent of MODEL,
MODEL_FABLE, MODEL_OPUS, MODEL_SONNET, and MODEL_HAIKU. Defaults come from the provider
catalog/docs and can be overridden with FCC_SMOKE_MODEL_<PROVIDER>, for example
FCC_SMOKE_MODEL_DEEPSEEK=deepseek-v4-pro (or deepseek-v4-flash). If no provider smoke model is
configured, live product smoke fails as missing_env unless you explicitly set
FCC_ALLOW_NO_PROVIDER_SMOKE=1.
Targets
Default targets do not send real bot messages or load voice backends:
| Target | Product scenarios | Required environment |
|---|---|---|
api |
messages, count_tokens full payload, errors, /stop, optimizations |
configured provider only for streaming messages |
auth |
canonical bearer auth, conflicting legacy headers, invalid/missing auth | none; test sets an isolated token |
cli |
server entrypoint, Claude CLI adaptive thinking, session cleanup | Claude CLI binary and provider only for real CLI |
clients |
VS Code and JetBrains protocol payloads | configured provider |
config |
env precedence, removed-env migration, proxy/timeouts | none |
extensibility |
provider runtime and platform factory construction | none |
messaging |
fake Discord/Telegram full flow, literal clear scopes, trees, persistence, voice cancel | none |
providers |
multi-turn text, adaptive thinking history, tools, disconnect, errors | configured providers, optional FCC_SMOKE_MODEL_* |
tools |
forced tool_use and tool_result continuation | tool-capable configured provider |
rate_limit |
disconnect cleanup and follow-up request | configured provider |
lmstudio |
local /models plus OpenAI-chat-backed Messages through proxy |
running LM Studio server |
llamacpp |
local /models plus OpenAI-chat-backed Messages through proxy |
running llama-server |
ollama |
local /v1/models plus OpenAI-chat-backed Messages through proxy |
running Ollama server |
Heavy/side-effectful targets are opt-in:
| Target | Product scenarios | Required environment |
|---|---|---|
nvidia_nim_cli |
Claude Code CLI feature matrix across NIM models | NVIDIA_NIM_API_KEY, Claude CLI |
openrouter_free_cli |
Claude Code CLI feature matrix across OpenRouter free models | OPENROUTER_API_KEY, Claude CLI |
telegram |
getMe, send, edit, delete, optional manual inbound | token and chat/user ID |
discord |
channel access, send, edit, delete, optional manual inbound | token and channel ID |
voice |
generated WAV through local Whisper or NVIDIA NIM transcription | VOICE_NOTE_ENABLED=true, FCC_SMOKE_RUN_VOICE=1 |
Examples
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_PROVIDER_MATRIX = "open_router,nvidia_nim,deepseek,lmstudio,llamacpp,ollama"
uv run pytest smoke/product -n 0 -s --tb=short
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_TARGETS = "ollama"
$env:OLLAMA_BASE_URL = "http://localhost:11434"
uv run pytest smoke/prereq smoke/product -n 0 -s --tb=short
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_TARGETS = "telegram,discord,voice"
$env:FCC_SMOKE_RUN_VOICE = "1"
uv run pytest smoke/product -n 0 -s --tb=short
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_TARGETS = "nvidia_nim_cli"
$env:FCC_SMOKE_NIM_MODELS = "z-ai/glm-5.2,moonshotai/kimi-k2.6,minimaxai/minimax-m2.7,nvidia/nemotron-3-super-120b-a12b,deepseek-ai/deepseek-v4-pro,deepseek-ai/deepseek-v4-flash"
uv run pytest smoke/product -n 0 -s --tb=short
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_TARGETS = "openrouter_free_cli"
$env:FCC_SMOKE_OPENROUTER_FREE_MODELS = "nvidia/nemotron-3-super-120b-a12b:free,openai/gpt-oss-120b:free,poolside/laguna-m.1:free"
uv run pytest smoke/product -n 0 -s --tb=short
$env:FCC_LIVE_SMOKE = "1"
$env:FCC_SMOKE_TARGETS = "messaging,config,extensibility"
uv run pytest smoke/product -n 0 -s --tb=short
Environment
FCC_ENV_FILE: explicit dotenv path for startup/config scenarios.FCC_LIVE_SMOKE=1: enables live smoke execution.FCC_ALLOW_NO_PROVIDER_SMOKE=1: permits no-provider live smoke for harness work.FCC_SMOKE_TARGETS: comma-separated targets, orall.FCC_SMOKE_PROVIDER_MATRIX: comma-separated provider prefixes to require.FCC_SMOKE_MODEL_NVIDIA_NIM,FCC_SMOKE_MODEL_OPEN_ROUTER,FCC_SMOKE_MODEL_MISTRAL,FCC_SMOKE_MODEL_MISTRAL_REASONING,FCC_SMOKE_MODEL_MISTRAL_CODESTRAL,FCC_SMOKE_MODEL_DEEPSEEK,FCC_SMOKE_MODEL_KIMI,FCC_SMOKE_MODEL_KIMI_CODE,FCC_SMOKE_MODEL_WAFER,FCC_SMOKE_MODEL_MINIMAX,FCC_SMOKE_MODEL_OPENCODE,FCC_SMOKE_MODEL_OPENCODE_GO,FCC_SMOKE_MODEL_BEDROCK,FCC_SMOKE_MODEL_ZAI,FCC_SMOKE_MODEL_FIREWORKS,FCC_SMOKE_MODEL_CLOUDFLARE,FCC_SMOKE_MODEL_GEMINI,FCC_SMOKE_MODEL_GROQ,FCC_SMOKE_MODEL_CEREBRAS,FCC_SMOKE_MODEL_OLLAMA_CLOUD,FCC_SMOKE_MODEL_LMSTUDIO,FCC_SMOKE_MODEL_LLAMACPP,FCC_SMOKE_MODEL_OLLAMA: optional per-provider smoke model overrides. Values may include the provider prefix or just the model name for that provider.FCC_SMOKE_MODEL_MISTRAL_REASONING: optional override for the dedicated Mistral native reasoning smoke, defaultmistral/mistral-medium-3-5.FCC_SMOKE_NIM_MODELS: optional comma-separated NVIDIA NIM CLI matrix models that replace the default characterization set.FCC_SMOKE_NIM_EXTRA_MODELS: optional comma-separated NVIDIA NIM CLI matrix models appended to the default or replacement set.FCC_SMOKE_OPENROUTER_FREE_MODELS: optional comma-separated OpenRouter free CLI matrix models that replace the default characterization set.FCC_SMOKE_OPENROUTER_FREE_EXTRA_MODELS: optional comma-separated OpenRouter free CLI matrix models appended to the default or replacement set.FCC_SMOKE_TIMEOUT_S: per-request/subprocess timeout, default45.FCC_SMOKE_CLAUDE_BIN: Claude CLI executable name, defaultclaude.FCC_SMOKE_TELEGRAM_CHAT_ID: Telegram chat/user ID for send/edit/delete.FCC_SMOKE_DISCORD_CHANNEL_ID: Discord channel ID for send/edit/delete.FCC_SMOKE_INTERACTIVE=1: enables manual inbound Telegram/Discord checks.FCC_SMOKE_RUN_VOICE=1: allows voice transcription backends to load/run.
Windows / nested uv run
Run smoke the same way you run tests (uv run pytest smoke from the repo). Child
processes use the same Python interpreter as the test runner, not nested
uv run, so Windows does not try to replace fcc-server.exe while it is
locked.
Failure Classes
Smoke artifacts are written to .smoke-results/ and redact env values whose
names contain KEY, TOKEN, SECRET, WEBHOOK, or AUTH.
missing_env: required credentials, binary, provider config, local provider server/model, or opt-in flag is absent.upstream_unavailable: a real provider or bot API is not reachable.probe_timeout: the smoke driver reached the target, but the CLI/probe did not complete within the smoke timeout.product_failure: the app accepted the scenario but returned the wrong shape, crashed, leaked state, or violated the product contract.harness_bug: the smoke test or driver made an invalid assumption.target_disabled: skipped becauseFCC_SMOKE_TARGETSintentionally selected a different target.
product_failure and harness_bug are failures. missing_env,
upstream_unavailable, and probe_timeout are skips except when the user
explicitly selected a provider in FCC_SMOKE_PROVIDER_MATRIX;
selected-but-missing providers fail.