项目文件夹

文件
Ali Khokhar 5305bd7e08 Remove obsolete init and server alias commands (#1220)
## 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 -->
2026-07-20 21:55:49 -07:00

562 行
20 KiB
Python

"""Public feature inventory for contract, prerequisite, and product smoke tests.
The inventory is intentionally explicit. README-advertised behavior and exposed
public surface area must have deterministic pytest contract coverage plus a
product E2E scenario when that behavior is a user-facing product path. Liveness
and route probes live in ``smoke/prereq`` and do not count as product coverage.
"""
from dataclasses import dataclass
from typing import Literal
FeatureSource = Literal["readme", "public_surface"]
@dataclass(frozen=True, slots=True)
class FeatureCoverage:
feature_id: str
title: str
source: FeatureSource
pytest_contract_tests: tuple[str, ...]
live_prereq_tests: tuple[str, ...]
product_e2e_tests: tuple[str, ...]
smoke_targets: tuple[str, ...]
required_env: tuple[str, ...]
skip_policy: str
product_e2e_reason: str = ""
@property
def has_pytest_coverage(self) -> bool:
return bool(self.pytest_contract_tests)
README_FEATURES: tuple[str, ...] = (
"zero_cost_provider_access",
"drop_in_claude_code_replacement",
"drop_in_codex_replacement",
"pi_cli_integration",
"provider_matrix",
"per_model_mapping",
"thinking_token_support",
"heuristic_tool_parser",
"discord_telegram_bot",
"optional_authentication",
"vscode_extension",
"intellij_extension",
"voice_notes",
)
FEATURE_INVENTORY: tuple[FeatureCoverage, ...] = (
FeatureCoverage(
"zero_cost_provider_access",
"Configured provider accepts real conversation turns",
"readme",
("tests/api/test_dependencies.py", "tests/providers/"),
("test_configured_provider_models_stream_successfully",),
("test_provider_text_multiturn_e2e",),
("providers",),
("configured provider credentials or local provider endpoint",),
"missing providers are missing_env unless FCC_ALLOW_NO_PROVIDER_SMOKE=1",
),
FeatureCoverage(
"drop_in_claude_code_replacement",
"Claude-compatible API, CLI, and editor protocol flows work",
"readme",
("tests/api/test_api.py", "tests/cli/test_cli.py"),
("test_probe_and_models_routes", "test_claude_cli_prompt_when_available"),
(
"test_api_basic_conversation_e2e",
"test_claude_cli_adaptive_thinking_e2e",
"test_claude_cli_provider_error_e2e",
"test_nvidia_nim_cli_matrix_e2e",
"test_openrouter_free_cli_matrix_e2e",
"test_vscode_protocol_e2e",
"test_jetbrains_protocol_e2e",
),
("api", "cli", "clients", "nvidia_nim_cli", "openrouter_free_cli"),
(
"configured provider",
"FCC_SMOKE_CLAUDE_BIN for real Claude CLI",
"NVIDIA_NIM_API_KEY",
"OPENROUTER_API_KEY",
),
"skip real CLI when binary is absent; configured providers must pass",
),
FeatureCoverage(
"drop_in_codex_replacement",
"OpenAI Responses API and Codex CLI adapter route through the proxy",
"readme",
(
"tests/api/test_openai_responses.py",
"tests/cli/test_entrypoints.py",
"tests/cli/test_codex_model_catalog.py",
"tests/core/openai_responses/test_sse.py",
),
("test_probe_and_models_routes",),
("test_provider_codex_responses_text_e2e",),
("api", "providers"),
("configured provider credentials or local provider endpoint",),
"missing providers are missing_env unless FCC_ALLOW_NO_PROVIDER_SMOKE=1",
),
FeatureCoverage(
"pi_cli_integration",
"Pi discovers FCC models and sends Anthropic Messages through the proxy",
"readme",
("tests/cli/test_entrypoints.py",),
("test_probe_and_models_routes",),
("test_pi_cli_prompt_e2e",),
("clients",),
("Pi CLI", "configured provider credentials or local provider endpoint"),
"skip only when Pi is absent; configured providers must pass",
),
FeatureCoverage(
"provider_matrix",
"Every configured provider prefix can satisfy conversation scenarios",
"readme",
("tests/api/test_dependencies.py", "tests/providers/"),
("test_configured_provider_models_stream_successfully",),
("test_provider_matrix_presence_e2e", "test_provider_text_multiturn_e2e"),
("providers",),
("configured provider credentials/endpoints", "optional FCC_SMOKE_MODEL_*"),
"selected providers missing credentials are failing missing_env",
),
FeatureCoverage(
"per_model_mapping",
"Fable, Opus, Sonnet, Haiku, and fallback mappings route explicitly",
"readme",
("tests/application/test_routing.py", "tests/config/test_config.py"),
("test_model_mapping_configuration_is_consistent",),
("test_model_mapping_matrix_e2e",),
("providers",),
("MODEL", "MODEL_FABLE", "MODEL_OPUS", "MODEL_SONNET", "MODEL_HAIKU"),
"skip only when live smoke is intentionally allowed to run with no provider",
),
FeatureCoverage(
"mixed_provider_mapping",
"Model-specific overrides can route to different providers",
"public_surface",
("tests/application/test_routing.py", "tests/config/test_config.py"),
("test_mixed_provider_model_mapping_when_configured",),
("test_model_mapping_matrix_e2e",),
("providers",),
("MODEL", "MODEL_FABLE", "MODEL_OPUS", "MODEL_SONNET", "MODEL_HAIKU"),
"configured mixed-provider mappings must resolve consistently",
),
FeatureCoverage(
"thinking_token_support",
"Thinking history, adaptive thinking, and redacted blocks are accepted",
"readme",
(
"tests/contracts/test_stream_contracts.py",
"tests/providers/test_open_router.py",
),
(),
(
"test_provider_adaptive_thinking_history_e2e",
"test_provider_reasoning_tool_continuation_e2e",
"test_gemini_thought_signature_tool_continuation_e2e",
"test_claude_cli_adaptive_thinking_e2e",
"test_route_reasoning_config_e2e",
),
("providers", "cli", "config"),
("configured provider",),
"configured providers must not reject adaptive thinking payloads",
),
FeatureCoverage(
"heuristic_tool_parser",
"Tool use and tool result continuation survive provider/client paths",
"readme",
("tests/providers/test_parsers.py", "tests/contracts/test_stream_contracts.py"),
("test_live_tool_use_when_configured_model_supports_tools",),
(
"test_provider_interleaved_thinking_tool_e2e",
"test_provider_tool_result_continuation_e2e",
"test_gemini_thought_signature_tool_continuation_e2e",
"test_provider_reasoning_tool_continuation_e2e",
),
("tools", "providers"),
("configured tool-capable provider",),
"tool-capable configured providers must emit or continue tool results",
),
FeatureCoverage(
"request_optimization",
"Local request optimizations return product responses without providers",
"public_surface",
(
"tests/api/test_optimization_handlers.py",
"tests/api/test_routes_optimizations.py",
),
("test_optimization_fast_paths_do_not_need_provider",),
("test_api_request_optimizations_e2e",),
("api",),
(),
"always runnable once the local smoke server starts",
),
FeatureCoverage(
"smart_rate_limiting",
"Transient retries and disconnect cleanup preserve follow-up requests",
"public_surface",
(
"tests/providers/test_provider_admission.py",
"tests/providers/test_nvidia_nim_degraded_retry.py",
),
("test_client_disconnect_mid_stream_does_not_crash_server",),
("test_provider_disconnect_e2e",),
("rate_limit", "providers"),
("configured provider",),
"upstream disconnects are skips only when classified upstream_unavailable",
),
FeatureCoverage(
"provider_hot_swap",
"Provider config changes preserve active streams while new requests switch",
"public_surface",
(
"tests/runtime/test_provider_manager.py",
"tests/api/test_response_streams.py",
"tests/api/test_admin.py",
),
(),
("test_provider_hot_swap_preserves_inflight_stream_e2e",),
("api",),
(),
"credential-free local fake upstreams make the scenario always runnable",
),
FeatureCoverage(
"discord_telegram_bot",
"Discord and Telegram product flows render progress and transcripts",
"readme",
(
"tests/messaging/test_discord_platform.py",
"tests/messaging/test_telegram.py",
"tests/messaging/test_limiter.py",
"tests/messaging/test_platform_outbox.py",
"tests/runtime/test_application_runtime.py",
),
(
"test_telegram_bot_api_permissions",
"test_discord_bot_api_permissions",
),
(
"test_messaging_fake_full_flow_e2e",
"test_telegram_live_permissions_e2e",
"test_discord_live_permissions_e2e",
),
("messaging", "telegram", "discord"),
("bot tokens/channels only for side-effectful live platform tests",),
"fake platform is required; real platforms skip without explicit env",
),
FeatureCoverage(
"subagent_control",
"Task-like tool output is rendered and controlled as foreground work",
"public_surface",
("tests/providers/test_subagent_interception.py",),
(),
("test_messaging_subagent_control_e2e",),
("messaging",),
(),
"fake platform exercises tool transcript behavior without spawning agents",
),
FeatureCoverage(
"extensible_provider_platform_abcs",
"Provider and platform factories expose built-in extension points",
"public_surface",
(
"tests/contracts/test_feature_manifest.py",
"tests/providers/test_provider_runtime.py",
),
(),
("test_provider_runtime_config_e2e", "test_platform_factory_e2e"),
("extensibility",),
(),
"always runnable with isolated settings",
),
FeatureCoverage(
"optional_authentication",
"Canonical bearer proxy authentication is enforced",
"readme",
("tests/api/test_auth.py",),
("test_bearer_auth_is_the_only_supported_header_shape",),
("test_api_bearer_auth_contract_e2e",),
("auth",),
("ANTHROPIC_AUTH_TOKEN",),
"product test starts an isolated token-protected server",
),
FeatureCoverage(
"vscode_extension",
"VS Code protocol-shaped requests work against the proxy",
"readme",
(
"tests/core/anthropic/test_models.py::test_messages_request_accepts_adaptive_thinking_type",
),
("test_vscode_and_jetbrains_shaped_requests",),
("test_vscode_protocol_e2e",),
("clients",),
("configured provider",),
"extension source is external; protocol payload is covered here",
),
FeatureCoverage(
"intellij_extension",
"JetBrains/ACP protocol-shaped requests work against the proxy",
"readme",
(
"tests/core/anthropic/test_models.py::test_messages_request_accepts_adaptive_thinking_type",
),
("test_vscode_and_jetbrains_shaped_requests",),
("test_jetbrains_protocol_e2e",),
("clients",),
("configured provider",),
"extension source is external; protocol payload is covered here",
),
FeatureCoverage(
"voice_notes",
"Voice note intake, cancellation, and transcription backends work",
"readme",
(
"tests/messaging/test_voice_handlers.py",
"tests/messaging/test_transcription.py",
),
("test_voice_transcription_backend_when_explicitly_enabled",),
(
"test_voice_platform_fake_e2e",
"test_voice_local_backend_e2e",
"test_voice_nim_backend_e2e",
),
("messaging", "voice"),
("VOICE_NOTE_ENABLED", "FCC_SMOKE_RUN_VOICE", "WHISPER_DEVICE"),
"fake cancellation is required; backend transcription is opt-in",
),
FeatureCoverage(
"anthropic_api_routes",
"Messages, count_tokens, errors, and stop use Anthropic-compatible shapes",
"public_surface",
("tests/api/test_api.py",),
("test_probe_and_models_routes", "test_stop_endpoint_reports_no_messaging"),
(
"test_api_basic_conversation_e2e",
"test_api_count_tokens_full_payload_e2e",
"test_api_error_shape_e2e",
"test_api_stop_e2e",
),
("api",),
("configured provider for streaming messages",),
"route pings are prereqs; product route behavior is covered by E2E cases",
),
FeatureCoverage(
"probe_routes",
"HEAD and OPTIONS compatibility probes are accepted",
"public_surface",
("tests/api/test_api.py::test_probe_endpoints_return_204_with_allow_headers",),
("test_probe_and_models_routes",),
(),
("api",),
(),
"always runnable once the local smoke server starts",
"probe routes are compatibility prerequisites, not product behavior",
),
FeatureCoverage(
"count_tokens_contract",
"Token counting accepts full Claude content payloads",
"public_surface",
("tests/api/test_request_utils.py",),
("test_count_tokens_accepts_thinking_tools_and_results",),
("test_api_count_tokens_full_payload_e2e",),
("api",),
(),
"always runnable once the local smoke server starts",
),
FeatureCoverage(
"provider_proxy_timeout_config",
"Provider proxies and HTTP timeout settings reach provider config",
"public_surface",
("tests/api/test_dependencies.py", "tests/providers/test_provider_runtime.py"),
(),
("test_proxy_timeout_config_e2e",),
("config",),
(),
"always runnable with isolated env files",
),
FeatureCoverage(
"lmstudio_endpoint",
"LM Studio Messages and local no-key operation work when running",
"public_surface",
("tests/providers/test_lmstudio.py",),
("test_lmstudio_models_endpoint_when_available",),
("test_lmstudio_messages_e2e",),
("lmstudio",),
("LM_STUDIO_BASE_URL with running LM Studio server",),
"skip when local upstream is unavailable",
),
FeatureCoverage(
"llamacpp_endpoint",
"llama.cpp OpenAI Chat and local no-key operation work when running",
"public_surface",
("tests/providers/test_llamacpp.py",),
("test_llamacpp_models_endpoint_when_available",),
("test_llamacpp_openai_chat_e2e",),
("llamacpp",),
("LLAMACPP_BASE_URL with running llama-server",),
"skip when local upstream is unavailable",
),
FeatureCoverage(
"ollama_endpoint",
"Ollama OpenAI Chat and local no-key operation work when running",
"public_surface",
("tests/providers/test_ollama.py",),
("test_ollama_models_endpoint_when_available",),
("test_ollama_openai_chat_e2e",),
("ollama",),
("OLLAMA_BASE_URL with running Ollama server",),
"skip when local upstream is unavailable",
),
FeatureCoverage(
"package_cli_entrypoints",
"Installed package scripts report version and start the server",
"public_surface",
("tests/cli/test_entrypoints.py", "tests/core/test_version.py"),
("test_fcc_server_entrypoint_starts_server",),
(
"test_entrypoint_server_e2e",
"test_entrypoint_version_e2e",
),
("cli",),
(),
"always runnable once uv project dependencies are available",
),
FeatureCoverage(
"claude_cli_drop_in",
"Claude CLI can send adaptive thinking and tool-shaped history",
"public_surface",
("tests/cli/test_cli.py",),
("test_claude_cli_prompt_when_available",),
(
"test_claude_cli_adaptive_thinking_e2e",
"test_claude_cli_multiturn_tool_protocol_e2e",
"test_nvidia_nim_cli_matrix_e2e",
"test_openrouter_free_cli_matrix_e2e",
),
("cli", "nvidia_nim_cli", "openrouter_free_cli"),
(
"FCC_SMOKE_CLAUDE_BIN",
"configured provider",
"NVIDIA_NIM_API_KEY",
"OPENROUTER_API_KEY",
),
"skip only when Claude CLI binary is absent",
),
FeatureCoverage(
"messaging_commands",
"Messaging commands clear exact reply subtrees or whole managed chats",
"public_surface",
(
"tests/messaging/test_handler.py",
"tests/messaging/test_handler_integration.py",
"tests/messaging/test_tree_ownership_concurrency.py",
),
(),
(
"test_messaging_commands_stop_clear_stats_e2e",
"test_reply_clear_uses_literal_platform_subtree_e2e",
"test_messaging_startup_notice_is_clearable_e2e",
"test_messaging_active_stop_uses_status_only_e2e",
"test_messaging_queued_scoped_cancel_e2e",
),
("messaging",),
(),
"required fake-platform product flow",
),
FeatureCoverage(
"tree_threading",
"Reply-based branches fork sessions and stay scoped",
"public_surface",
(
"tests/messaging/test_tree_queue.py",
"tests/messaging/test_tree_concurrency.py",
"tests/messaging/test_message_tree_transitions.py",
"tests/messaging/test_tree_ownership_concurrency.py",
),
(),
(
"test_tree_threading_e2e",
"test_messaging_queued_scoped_cancel_e2e",
"test_same_message_ids_are_isolated_by_chat_e2e",
),
("messaging",),
(),
"required fake-platform product flow",
),
FeatureCoverage(
"restart_restore",
"Persisted tree state restores reply routing after restart",
"public_surface",
("tests/messaging/test_restart_reply_restore.py",),
(),
("test_restart_restore_and_session_persistence_e2e",),
("messaging",),
(),
"required fake-platform persistence flow",
),
FeatureCoverage(
"session_persistence",
"Session JSON preserves scoped trees and message logs",
"public_surface",
("tests/messaging/test_session_store_edge_cases.py",),
(),
("test_restart_restore_and_session_persistence_e2e",),
("messaging",),
(),
"required fake-platform persistence flow",
),
FeatureCoverage(
"config_env_precedence",
"FCC_ENV_FILE, dotenv, and process env precedence are deterministic",
"public_surface",
("tests/config/test_config.py",),
(),
("test_env_precedence_e2e",),
("config",),
(),
"always runnable with isolated env files",
),
FeatureCoverage(
"removed_env_migration",
"Removed thinking env vars are ignored without changing defaults",
"public_surface",
("tests/config/test_config.py",),
(),
("test_removed_env_migration_e2e",),
("config",),
(),
"always runnable with isolated env files",
),
FeatureCoverage(
"streaming_error_mapping",
"Canonical execution failures map to protocol-correct terminal output",
"public_surface",
(
"tests/api/test_execution_failure_contract.py",
"tests/core/test_failure_protocol_mapping.py",
"tests/providers/test_execution_failure_boundary.py",
"tests/providers/test_failure_policy.py",
),
(),
(
"test_api_error_shape_e2e",
"test_provider_error_e2e",
"test_claude_cli_provider_error_e2e",
),
("api", "providers", "cli"),
("configured provider for provider error scenario",),
"invalid request path is required; provider error path requires provider",
),
)
def feature_ids(*, source: FeatureSource | None = None) -> set[str]:
"""Return feature IDs covered by the inventory."""
return {
feature.feature_id
for feature in FEATURE_INVENTORY
if source is None or feature.source == source
}