项目文件夹

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

117 行
3.3 KiB
Python

import subprocess
from pathlib import Path
from free_claude_code.config.settings import Settings
from smoke.lib import child_process
from smoke.lib import server as smoke_server
from smoke.lib.child_process import (
cmd_fcc_server,
cmd_python_c,
run_captured_text,
)
from smoke.lib.config import SmokeConfig
def test_fcc_server_command_uses_cli_entrypoint() -> None:
assert cmd_fcc_server() == [
child_process.python_exe(),
"-c",
"from free_claude_code.cli.entrypoints import serve; serve()",
]
def test_start_server_disables_cli_admin_browser(monkeypatch, tmp_path: Path) -> None:
captured: dict[str, object] = {}
class FakeProcess:
def __init__(self, command: list[str], **kwargs: object) -> None:
captured["command"] = command
captured.update(kwargs)
def poll(self) -> int | None:
return None
config = SmokeConfig(
root=tmp_path,
results_dir=tmp_path / "results",
live=True,
interactive=False,
targets=frozenset(),
provider_matrix=frozenset(),
timeout_s=1.0,
prompt="",
claude_bin="claude",
worker_id="test",
settings=Settings(),
)
monkeypatch.setattr(smoke_server, "find_free_port", lambda: 4567)
monkeypatch.setattr(smoke_server.subprocess, "Popen", FakeProcess)
monkeypatch.setattr(
smoke_server, "_wait_for_health", lambda _server, *, timeout_s: None
)
monkeypatch.setattr(smoke_server, "_stop_process", lambda _process: None)
with smoke_server.start_server(config):
pass
env_obj = captured["env"]
assert isinstance(env_obj, dict)
env = {str(key): value for key, value in env_obj.items()}
assert env["FCC_OPEN_BROWSER"] == "0"
assert env["HOST"] == "127.0.0.1"
assert env["PORT"] == "4567"
def test_run_captured_text_uses_utf8_replacement(monkeypatch, tmp_path: Path) -> None:
calls: dict[str, object] = {}
def fake_run(
command: list[str],
**kwargs: object,
) -> subprocess.CompletedProcess[str]:
calls["command"] = command
calls.update(kwargs)
return subprocess.CompletedProcess(
args=command,
returncode=0,
stdout="ok",
stderr="",
)
monkeypatch.setattr(child_process.subprocess, "run", fake_run)
result = run_captured_text(
("cmd", "arg"),
cwd=tmp_path,
env={"FCC_TEST": "1"},
timeout=1.0,
)
assert result.stdout == "ok"
assert calls["command"] == ["cmd", "arg"]
assert calls["cwd"] == tmp_path
assert calls["env"] == {"FCC_TEST": "1"}
assert calls["capture_output"] is True
assert calls["text"] is True
assert calls["encoding"] == "utf-8"
assert calls["errors"] == "replace"
assert calls["timeout"] == 1.0
assert calls["check"] is False
def test_run_captured_text_replaces_invalid_utf8_bytes(tmp_path: Path) -> None:
result = run_captured_text(
cmd_python_c(
"import sys; "
"sys.stdout.buffer.write(bytes([0x8f])); "
"sys.stderr.buffer.write(bytes([0x8f]))"
),
cwd=tmp_path,
timeout=10.0,
)
assert result.returncode == 0
assert result.stdout == "\ufffd"
assert result.stderr == "\ufffd"