feat(setup-instructions): preflight checks both git and claude
Renames `_git_check_block` to `_preflight_block` and adds a `claude --version` check beside `git --version`. Both binaries are required by the marketplace step — git for the clone fallback, claude for `claude plugin marketplace add` / `claude plugin install` — so checking them together gives one clear failure instead of two confusing downstream errors. Install hints: `npm i -g @anthropic-ai/claude-code` for Linux / WSL plus a doc URL (https://docs.claude.com/claude-code) for the native macOS / Windows installers. We don't try to one-line a native installer; the canonical instructions live upstream. Plan: docs/superpowers/plans/2026-05-04-unified-setup-prompt.md task 3.
This commit is contained in:
parent
e16698c3cc
commit
74b7f6e254
2 changed files with 66 additions and 16 deletions
|
|
@ -430,34 +430,48 @@ def _finale_lines(*, confirm_step_num: str, has_ca: bool, has_marketplace: bool)
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
def _git_check_block(step_num: str) -> list[str]:
|
def _preflight_block(step_num: str) -> list[str]:
|
||||||
"""Git pre-flight check — runs before the marketplace clone.
|
"""Pre-flight check — runs before the marketplace clone.
|
||||||
|
|
||||||
`claude plugin marketplace add` (and our git-clone fallback) shells out
|
`claude plugin marketplace add` (and our git-clone fallback) shells out
|
||||||
to `git`, so a missing git binary fails the marketplace step with a
|
to `git`, AND the marketplace step calls `claude` itself, so a missing
|
||||||
confusing error. Cross-platform install commands cover the three
|
binary on either side fails the step with a confusing error. We check
|
||||||
supported workstation OSes:
|
both here so the user gets a single clear "install X" message instead
|
||||||
|
of debugging a downstream error.
|
||||||
|
|
||||||
|
Cross-platform install commands cover the three supported workstation
|
||||||
|
OSes:
|
||||||
- macOS: Homebrew (`brew install git`). The Xcode CLT bundle also
|
- macOS: Homebrew (`brew install git`). The Xcode CLT bundle also
|
||||||
ships git; we prefer brew because it's non-interactive.
|
ships git; we prefer brew because it's non-interactive.
|
||||||
- Windows: winget (`winget install --id Git.Git -e ...`). Bundled
|
- Windows: winget (`winget install --id Git.Git -e ...`). Bundled
|
||||||
with Windows 10 1809+ and Windows 11; non-interactive with --silent.
|
with Windows 10 1809+ and Windows 11; non-interactive with --silent.
|
||||||
- Linux: apt or dnf, depending on distro family.
|
- Linux: apt or dnf, depending on distro family.
|
||||||
|
|
||||||
|
For `claude` we point at the official platform installer docs rather
|
||||||
|
than vendoring an install one-liner — Anthropic ships per-platform
|
||||||
|
installers (npm on Linux, native binary on macOS/Windows) and the
|
||||||
|
canonical instructions live at https://docs.claude.com/claude-code.
|
||||||
|
|
||||||
`step_num` is parameterized because step ordering shifted between
|
`step_num` is parameterized because step ordering shifted between
|
||||||
layouts (the marketplace block now runs before diagnose/skills, so
|
layouts (the marketplace block now runs before diagnose/skills, so
|
||||||
git-check + marketplace are steps 4-5 instead of 6-7).
|
preflight + marketplace are steps 4-5 instead of 6-7).
|
||||||
"""
|
"""
|
||||||
return [
|
return [
|
||||||
"",
|
"",
|
||||||
f"{step_num}) Make sure git is installed (required for the marketplace clone):",
|
f"{step_num}) Make sure git and claude are installed (required for the marketplace clone):",
|
||||||
" git --version",
|
" git --version",
|
||||||
|
" claude --version",
|
||||||
"",
|
"",
|
||||||
" If that fails (\"command not found\" or similar), install git:",
|
" If `git --version` fails (\"command not found\" or similar), install git:",
|
||||||
" - macOS: brew install git",
|
" - macOS: brew install git",
|
||||||
" - Windows: winget install --id Git.Git -e --source winget --silent",
|
" - Windows: winget install --id Git.Git -e --source winget --silent",
|
||||||
" - Linux: sudo apt-get install git OR sudo dnf install git",
|
" - Linux: sudo apt-get install git OR sudo dnf install git",
|
||||||
"",
|
"",
|
||||||
" Then re-run `git --version` to confirm before continuing.",
|
" If `claude --version` fails, install Claude Code:",
|
||||||
|
" - npm (Linux / WSL): npm i -g @anthropic-ai/claude-code",
|
||||||
|
" - macOS / Windows native installer: see https://docs.claude.com/claude-code",
|
||||||
|
"",
|
||||||
|
" Then re-run both `--version` checks to confirm before continuing.",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -733,7 +747,7 @@ def resolve_lines(
|
||||||
lines.extend(_install_cli_lines(has_ca=has_ca)) # 1
|
lines.extend(_install_cli_lines(has_ca=has_ca)) # 1
|
||||||
lines.extend(_init_lines()) # 2, 3
|
lines.extend(_init_lines()) # 2, 3
|
||||||
if has_marketplace:
|
if has_marketplace:
|
||||||
lines.extend(_git_check_block(steps["preflight"])) # 4
|
lines.extend(_preflight_block(steps["preflight"])) # 4
|
||||||
lines.extend(_marketplace_block( # 5
|
lines.extend(_marketplace_block( # 5
|
||||||
names, effective_self_signed, has_ca=has_ca, step_num=steps["marketplace"],
|
names, effective_self_signed, has_ca=has_ca, step_num=steps["marketplace"],
|
||||||
))
|
))
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ def test_resolve_lines_no_plugins_unified_six_step_layout():
|
||||||
assert "claude plugin marketplace add" not in joined
|
assert "claude plugin marketplace add" not in joined
|
||||||
assert "claude plugin install" not in joined
|
assert "claude plugin install" not in joined
|
||||||
# No preflight step when there's no marketplace block to gate.
|
# No preflight step when there's no marketplace block to gate.
|
||||||
assert "Make sure git is installed" not in joined
|
assert "Make sure git and claude are installed" not in joined
|
||||||
# Legacy `git config sslVerify=false` downgrade must NOT be emitted.
|
# Legacy `git config sslVerify=false` downgrade must NOT be emitted.
|
||||||
# Match the specific config line, not the bare substring (which appears
|
# Match the specific config line, not the bare substring (which appears
|
||||||
# in the preamble as a "don't do this" example).
|
# in the preamble as a "don't do this" example).
|
||||||
|
|
@ -292,9 +292,10 @@ def test_resolve_lines_with_plugins_uses_install_first_diagnose_last_layout():
|
||||||
server_host="agnes.example.com",
|
server_host="agnes.example.com",
|
||||||
)
|
)
|
||||||
joined = "\n".join(lines)
|
joined = "\n".join(lines)
|
||||||
# Step 4 — git pre-flight, with all three platforms' install commands.
|
# Step 4 — pre-flight, with all three platforms' install commands.
|
||||||
assert "4) Make sure git is installed" in joined
|
assert "4) Make sure git and claude are installed" in joined
|
||||||
assert "git --version" in joined
|
assert "git --version" in joined
|
||||||
|
assert "claude --version" in joined
|
||||||
assert "brew install git" in joined
|
assert "brew install git" in joined
|
||||||
assert "winget install --id Git.Git -e --source winget --silent" in joined
|
assert "winget install --id Git.Git -e --source winget --silent" in joined
|
||||||
assert "sudo apt-get install git" in joined or "sudo dnf install git" in joined
|
assert "sudo apt-get install git" in joined or "sudo dnf install git" in joined
|
||||||
|
|
@ -318,7 +319,7 @@ def test_resolve_lines_with_plugins_uses_install_first_diagnose_last_layout():
|
||||||
install_idx = joined.index("1) Install the CLI")
|
install_idx = joined.index("1) Install the CLI")
|
||||||
init_idx = joined.index("2) Bootstrap your Agnes workspace")
|
init_idx = joined.index("2) Bootstrap your Agnes workspace")
|
||||||
catalog_idx = joined.index("3) Verify the data is queryable:")
|
catalog_idx = joined.index("3) Verify the data is queryable:")
|
||||||
git_idx = joined.index("4) Make sure git is installed")
|
git_idx = joined.index("4) Make sure git and claude are installed")
|
||||||
market_idx = joined.index("5) Register the Agnes Claude Code marketplace")
|
market_idx = joined.index("5) Register the Agnes Claude Code marketplace")
|
||||||
diag_idx = joined.index("6) Run diagnostics:")
|
diag_idx = joined.index("6) Run diagnostics:")
|
||||||
skills_idx = joined.index("7) Skills")
|
skills_idx = joined.index("7) Skills")
|
||||||
|
|
@ -333,6 +334,41 @@ def test_resolve_lines_with_plugins_uses_install_first_diagnose_last_layout():
|
||||||
assert "{token}" in joined
|
assert "{token}" in joined
|
||||||
|
|
||||||
|
|
||||||
|
def test_preflight_checks_both_git_and_claude():
|
||||||
|
"""Pre-flight (step 4 when marketplace is gated on) checks BOTH binaries
|
||||||
|
before the marketplace clone — `git --version` is needed for the clone
|
||||||
|
itself, `claude --version` is needed for the `claude plugin
|
||||||
|
marketplace add` / `claude plugin install` calls. Either missing
|
||||||
|
breaks the marketplace step in a confusing way, so we surface the
|
||||||
|
failure before we get there.
|
||||||
|
"""
|
||||||
|
from app.web.setup_instructions import resolve_lines
|
||||||
|
|
||||||
|
joined = "\n".join(
|
||||||
|
resolve_lines(
|
||||||
|
"agnes.whl",
|
||||||
|
plugin_install_names=["foo"],
|
||||||
|
server_host="agnes.example.com",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
# Both version checks present.
|
||||||
|
assert "git --version" in joined
|
||||||
|
assert "claude --version" in joined
|
||||||
|
# Header mentions both tools.
|
||||||
|
assert "Make sure git and claude are installed" in joined
|
||||||
|
# Install hints for claude — npm one-liner for Linux/WSL plus a doc URL
|
||||||
|
# for native installers on macOS / Windows. We don't try to one-line a
|
||||||
|
# native installer; the canonical instructions live upstream.
|
||||||
|
assert "npm i -g @anthropic-ai/claude-code" in joined
|
||||||
|
assert "https://docs.claude.com/claude-code" in joined
|
||||||
|
# Both checks come BEFORE the marketplace add line.
|
||||||
|
git_check_idx = joined.index("git --version")
|
||||||
|
claude_check_idx = joined.index("claude --version")
|
||||||
|
market_idx = joined.index("claude plugin marketplace add")
|
||||||
|
assert git_check_idx < market_idx
|
||||||
|
assert claude_check_idx < market_idx
|
||||||
|
|
||||||
|
|
||||||
def test_resolve_lines_self_signed_legacy_path_adds_git_config_line():
|
def test_resolve_lines_self_signed_legacy_path_adds_git_config_line():
|
||||||
"""Legacy fallback (no ca_pem on disk + self_signed_tls=True): the host-scoped
|
"""Legacy fallback (no ca_pem on disk + self_signed_tls=True): the host-scoped
|
||||||
`git config sslVerify=false` downgrade is still emitted so existing
|
`git config sslVerify=false` downgrade is still emitted so existing
|
||||||
|
|
@ -365,8 +401,8 @@ def test_resolve_lines_self_signed_no_op_without_plugins():
|
||||||
# Legacy downgrade line not present.
|
# Legacy downgrade line not present.
|
||||||
assert "git config --global" not in joined
|
assert "git config --global" not in joined
|
||||||
assert "claude plugin" not in joined
|
assert "claude plugin" not in joined
|
||||||
# No git pre-flight either when there's no marketplace step.
|
# No pre-flight either when there's no marketplace step.
|
||||||
assert "Make sure git is installed" not in joined
|
assert "Make sure git and claude are installed" not in joined
|
||||||
assert "6) Confirm:" in joined # original layout intact
|
assert "6) Confirm:" in joined # original layout intact
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue