项目文件夹

文件
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

525 行
20 KiB
Python

"""Hierarchical public capability map.
This module is the architectural companion to ``smoke.features``. The feature
inventory says which public features must be covered; this map records the
subfeature contracts, owning module boundary, and coverage owners that protect
them while the internals are refactored.
"""
from dataclasses import dataclass
@dataclass(frozen=True, slots=True)
class CapabilityContract:
capability: str
subfeature: str
feature_id: str
owner: str
inputs: str
outputs: str
failure: str
pytest_tests: tuple[str, ...]
smoke_tests: tuple[str, ...] = ()
CAPABILITY_CONTRACTS: tuple[CapabilityContract, ...] = (
CapabilityContract(
"api_compatibility",
"routes_and_probes",
"anthropic_api_routes",
"free_claude_code.api.routes",
"Anthropic-compatible HTTP requests",
"JSON or Anthropic SSE responses",
"HTTPException or Anthropic error payload",
("tests/api/test_api.py",),
("test_probe_and_models_routes", "test_stop_endpoint_reports_no_messaging"),
),
CapabilityContract(
"api_compatibility",
"head_options_probes",
"probe_routes",
"free_claude_code.api.routes",
"HEAD/OPTIONS probe requests",
"204 with Allow header",
"auth failure when configured",
("tests/api/test_api.py::test_probe_endpoints_return_204_with_allow_headers",),
("test_probe_and_models_routes",),
),
CapabilityContract(
"api_compatibility",
"client_request_shapes",
"drop_in_claude_code_replacement",
"free_claude_code.api.handlers.messages.MessagesHandler",
"Claude Code, VS Code, and JetBrains shaped requests",
"compatible streaming response",
"provider or validation error in Anthropic shape",
("tests/api/test_api.py", "tests/cli/test_cli.py"),
("test_vscode_and_jetbrains_shaped_requests",),
),
CapabilityContract(
"api_compatibility",
"responses_api",
"drop_in_codex_replacement",
"free_claude_code.api.handlers.responses.ResponsesHandler",
"OpenAI Responses requests from Codex",
"Responses SSE or JSON response",
"OpenAI-shaped error or conversion error",
(
"tests/api/test_openai_responses.py",
"tests/core/openai_responses/test_sse.py",
"tests/cli/test_entrypoints.py",
"tests/cli/test_codex_model_catalog.py",
),
(
"test_probe_and_models_routes",
"test_provider_codex_responses_text_e2e",
),
),
CapabilityContract(
"api_compatibility",
"client_extensions",
"vscode_extension",
"free_claude_code.api.handlers.messages.MessagesHandler",
"VS Code beta query/header variants",
"accepted Anthropic-compatible response",
"HTTP error only for auth/provider failures",
(),
("test_vscode_and_jetbrains_shaped_requests",),
),
CapabilityContract(
"api_compatibility",
"client_extensions",
"intellij_extension",
"free_claude_code.api.handlers.messages.MessagesHandler",
"JetBrains/ACP request variants",
"accepted Anthropic-compatible response",
"HTTP error only for auth/provider failures",
(),
("test_vscode_and_jetbrains_shaped_requests",),
),
CapabilityContract(
"auth",
"bearer_authorization",
"optional_authentication",
"free_claude_code.api.dependencies.require_proxy_auth",
"authorization: bearer token",
"request accepted or 401",
"401 missing/invalid proxy authentication token",
("tests/api/test_auth.py",),
("test_bearer_auth_is_the_only_supported_header_shape",),
),
CapabilityContract(
"provider_routing",
"provider_runtime",
"provider_matrix",
"free_claude_code.runtime.provider_manager.ProviderRuntimeManager",
"provider id and Settings",
"configured BaseProvider instance",
"503 for missing credentials; invalid_request_error for unknown provider",
("tests/api/test_dependencies.py", "tests/providers/test_provider_runtime.py"),
(
"test_configured_provider_models_stream_successfully",
"test_provider_matrix_presence_e2e",
),
),
CapabilityContract(
"provider_routing",
"claude_model_resolution",
"per_model_mapping",
"free_claude_code.application.routing.ModelRouter",
"Claude model name and configured MODEL_* values",
"provider id plus provider model name",
"settings validation rejects invalid provider prefixes",
("tests/application/test_routing.py", "tests/config/test_config.py"),
("test_model_mapping_configuration_is_consistent",),
),
CapabilityContract(
"provider_routing",
"mixed_provider_resolution",
"mixed_provider_mapping",
"free_claude_code.application.routing.ModelRouter",
"Fable/Opus/Sonnet/Haiku/fallback model config",
"distinct provider selections per request",
"skip live execution when providers are not configured",
("tests/application/test_routing.py", "tests/config/test_config.py"),
("test_mixed_provider_model_mapping_when_configured",),
),
CapabilityContract(
"provider_routing",
"provider_runtime_config",
"provider_proxy_timeout_config",
"free_claude_code.providers.runtime.ProviderRuntime",
"provider proxy, timeout, and rate-limit settings",
"provider client and generation-owned admission config",
"provider construction failure",
(
"tests/api/test_dependencies.py",
"tests/providers/test_provider_runtime.py",
"tests/providers/test_provider_admission.py",
),
),
CapabilityContract(
"provider_routing",
"zero_cost_backends",
"zero_cost_provider_access",
"free_claude_code.providers.runtime.ProviderRuntime",
"configured free/local provider",
"streaming response from selected backend",
"missing env or upstream unavailable skip in smoke",
("tests/api/test_dependencies.py", "tests/providers/"),
("test_configured_provider_models_stream_successfully",),
),
CapabilityContract(
"streaming_conversion",
"anthropic_sse_lifecycle",
"streaming_error_mapping",
"free_claude_code.core.failures.ExecutionFailure",
"provider stream failures before or after the HTTP commit boundary",
"protocol-specific JSON failure or terminal stream event",
"ordinary request failures remain distinct from terminal execution",
(
"tests/api/test_execution_failure_contract.py",
"tests/api/test_ordinary_error_phases.py",
"tests/core/test_failure_protocol_mapping.py",
"tests/providers/test_execution_failure_boundary.py",
"tests/providers/test_failure_policy.py",
),
),
CapabilityContract(
"streaming_conversion",
"thinking_blocks",
"thinking_token_support",
"free_claude_code.core.anthropic.thinking",
"reasoning_content, reasoning_details, <think> text, native thinking",
"Claude thinking blocks or suppression",
"thinking hidden when disabled",
(
"tests/contracts/test_stream_contracts.py",
"tests/providers/test_converter.py",
"tests/providers/test_deepseek.py",
"tests/providers/test_nvidia_nim_request.py",
"tests/providers/test_open_router.py",
),
(
"test_route_reasoning_config_e2e",
"test_provider_reasoning_tool_continuation_e2e",
),
),
CapabilityContract(
"streaming_conversion",
"heuristic_tools",
"heuristic_tool_parser",
"free_claude_code.core.anthropic.tools",
"textual tool-call output",
"structured Anthropic tool_use blocks",
"text fallback when malformed",
("tests/providers/test_parsers.py", "tests/contracts/test_stream_contracts.py"),
(
"test_live_tool_use_when_configured_model_supports_tools",
"test_provider_reasoning_tool_continuation_e2e",
),
),
CapabilityContract(
"streaming_conversion",
"subagent_task_control",
"subagent_control",
"free_claude_code.core.anthropic.streaming.AnthropicStreamLedger",
"Task tool call arguments",
"run_in_background=false",
"invalid JSON flushed as safe object",
("tests/providers/test_subagent_interception.py",),
),
CapabilityContract(
"request_behavior",
"local_optimizations",
"request_optimization",
"free_claude_code.api.optimization_handlers",
"Claude Code probe/title/suggestion/filepath requests",
"local MessagesResponse without provider call",
"falls through to provider when unmatched/disabled",
(
"tests/api/test_optimization_handlers.py",
"tests/api/test_routes_optimizations.py",
),
("test_optimization_fast_paths_do_not_need_provider",),
),
CapabilityContract(
"request_behavior",
"token_counting",
"count_tokens_contract",
"free_claude_code.core.anthropic.get_token_count",
"messages, system blocks, tools, images, thinking, tool results",
"positive input token estimate",
"500 with readable detail on unexpected failure",
("tests/api/test_request_utils.py",),
("test_count_tokens_accepts_thinking_tools_and_results",),
),
CapabilityContract(
"config",
"env_precedence",
"config_env_precedence",
"free_claude_code.config.settings.Settings",
"process env, user env file, repo env file, FCC_ENV_FILE",
"deterministic settings values",
"validation error for invalid settings",
("tests/config/test_config.py",),
),
CapabilityContract(
"config",
"removed_env_migration",
"removed_env_migration",
"free_claude_code.config.settings.Settings",
"retired ENABLE_MODEL_THINKING or tier override in an FCC-owned dotenv",
"owned dotenv is migrated to the typed reasoning policy",
"explicit dotenv receives an actionable warning",
("tests/config/test_env_migrations.py", "tests/config/test_config.py"),
("test_removed_env_migration_e2e",),
),
CapabilityContract(
"provider_runtime",
"rate_limit_and_disconnect",
"smart_rate_limiting",
"free_claude_code.providers.admission.ProviderAdmissionController",
"concurrent provider requests and transient upstream/disconnect failures",
"provider-specific classification, proactive throttle, coordinated retry, cleanup",
"mapped provider error or smoke skip for upstream disconnect",
(
"tests/providers/test_provider_admission.py",
"tests/providers/test_nvidia_nim_degraded_retry.py",
),
("test_client_disconnect_mid_stream_does_not_crash_server",),
),
CapabilityContract(
"provider_runtime",
"generation_hot_swap",
"provider_hot_swap",
"free_claude_code.runtime.provider_manager.ProviderRuntimeManager",
"validated Admin provider config and concurrent request streams",
"atomic generation publication with old-stream completion",
"failed candidate or persistence leaves current generation unchanged",
(
"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",),
),
CapabilityContract(
"local_providers",
"lmstudio_messages",
"lmstudio_endpoint",
"free_claude_code.providers.lmstudio.LMStudioProvider",
"Anthropic Messages request and local LM Studio URL",
"Anthropic SSE converted from an OpenAI Chat upstream",
"typed provider failure for local upstream errors",
("tests/providers/test_lmstudio.py",),
("test_lmstudio_messages_e2e",),
),
CapabilityContract(
"local_providers",
"llamacpp_openai_chat",
"llamacpp_endpoint",
"free_claude_code.providers.openai_chat.OpenAIChatProvider",
"OpenAI Chat request and llama.cpp URL",
"Anthropic SSE converted from OpenAI Chat chunks",
"typed provider failure for local upstream errors",
("tests/providers/test_llamacpp.py",),
("test_llamacpp_models_endpoint_when_available",),
),
CapabilityContract(
"local_providers",
"ollama_openai_chat",
"ollama_endpoint",
"free_claude_code.providers.openai_chat.OpenAIChatProvider",
"OpenAI Chat request and local Ollama root URL",
"Anthropic SSE converted from OpenAI Chat chunks",
"typed provider failure for local upstream errors",
("tests/providers/test_ollama.py",),
("test_ollama_models_endpoint_when_available",),
),
CapabilityContract(
"openrouter",
"openai_chat_provider",
"provider_matrix",
"free_claude_code.providers.open_router.OpenRouterProvider",
"OpenAI Chat request for OpenRouter",
"OpenAI stream mapped to Anthropic SSE",
"Anthropic SSE error shape",
(
"tests/providers/test_open_router.py",
"tests/providers/test_openai_compat_5xx_retry.py",
),
("test_configured_provider_models_stream_successfully",),
),
CapabilityContract(
"messaging",
"discord_telegram_platforms",
"discord_telegram_bot",
"free_claude_code.messaging.workflow.MessagingWorkflow",
"Discord/Telegram incoming messages and API callbacks",
"live progress edits and final transcript",
"platform retry/fallback or user-facing error",
(
"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"),
),
CapabilityContract(
"messaging",
"commands",
"messaging_commands",
"free_claude_code.messaging.commands",
"/stop, /clear, /stats command messages",
"chat-wide cleanup or literal reply-subtree deletion",
"terminal status edit, no-op feedback, or best-effort platform cleanup",
(
"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",
),
),
CapabilityContract(
"messaging",
"tree_threading",
"tree_threading",
"free_claude_code.messaging.trees.TreeQueueManager",
"reply-based message graph",
"queued branches and scoped cancellation",
"node error propagation",
(
"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",
),
),
CapabilityContract(
"persistence",
"restart_restore",
"restart_restore",
"free_claude_code.messaging.session.SessionStore",
"stored tree JSON",
"restored scoped reply lookup",
"stale pending work marked lost",
("tests/messaging/test_restart_reply_restore.py",),
),
CapabilityContract(
"persistence",
"session_store",
"session_persistence",
"free_claude_code.messaging.session.SessionStore",
"scoped tree data and message log",
"JSON persistence compatible with existing files",
"best-effort flush error logging",
("tests/messaging/test_session_store_edge_cases.py",),
),
CapabilityContract(
"voice",
"voice_transcription",
"voice_notes",
"free_claude_code.messaging.voice.Transcriber",
"Discord/Telegram audio file and voice backend settings",
"transcribed prompt routed to handler",
"missing optional extra or backend error shown to user",
(
"tests/messaging/test_voice_handlers.py",
"tests/messaging/test_transcription.py",
"tests/messaging/test_transcription_nim.py",
"tests/messaging/test_platform_voice_flow.py",
),
("test_voice_transcription_backend_when_explicitly_enabled",),
),
CapabilityContract(
"cli",
"package_entrypoints",
"package_cli_entrypoints",
"free_claude_code.cli.entrypoints",
"installed console scripts",
"package version or uvicorn server startup",
"process cleanup in finally",
("tests/cli/test_entrypoints.py", "tests/core/test_version.py"),
(
"test_fcc_server_entrypoint_starts_server",
"test_entrypoint_version_e2e",
),
),
CapabilityContract(
"cli",
"claude_cli_drop_in",
"claude_cli_drop_in",
"free_claude_code.cli.managed.session.ManagedClaudeSession",
"Claude CLI binary and proxy env",
"stream-json events and session id mapping",
"stderr/error event and process cleanup",
("tests/cli/test_cli.py",),
(
"test_claude_cli_prompt_when_available",
"test_claude_cli_provider_error_e2e",
"test_nvidia_nim_cli_matrix_e2e",
"test_openrouter_free_cli_matrix_e2e",
),
),
CapabilityContract(
"cli",
"codex_cli_drop_in",
"drop_in_codex_replacement",
"free_claude_code.cli.launchers.codex",
"Codex CLI binary and fcc provider env",
"Responses config, auth env, and native /model catalog injection",
"proxy preflight and catalog fail-open warning",
(
"tests/cli/test_codex_model_catalog.py",
"tests/cli/test_entrypoints.py",
),
(),
),
CapabilityContract(
"cli",
"pi_cli_integration",
"pi_cli_integration",
"free_claude_code.cli.launchers.pi",
"Pi CLI binary, bundled extension, and FCC child-process environment",
"Anthropic Messages provider with an FCC-scoped live model catalog",
"proxy preflight, missing extension, or catalog failure exits without fallback",
("tests/cli/test_entrypoints.py",),
("test_pi_cli_prompt_e2e",),
),
CapabilityContract(
"extensibility",
"provider_platform_abcs",
"extensible_provider_platform_abcs",
"free_claude_code.providers.runtime and free_claude_code.messaging.platforms.factory",
"new provider/platform implementations",
"registered BaseProvider or messaging component bundle",
"unknown platform returns None; unknown provider errors",
(
"tests/contracts/test_feature_manifest.py",
"tests/providers/test_provider_runtime.py",
),
),
)
def capability_names() -> set[str]:
return {contract.capability for contract in CAPABILITY_CONTRACTS}
def contracted_feature_ids() -> set[str]:
return {contract.feature_id for contract in CAPABILITY_CONTRACTS}