fix(web): setup script step 2 checks pwd, no auto-mkdir (#344)
The /home onboarding page already has a visible manual "Step 3 — create your workspace folder" instructing the user to `mkdir -p ~/<dir> && cd ~/<dir>` BEFORE pasting the install script into Claude Code. The pasted script's step 2 then re-ran the same mkdir+cd, which silently overrode an intentional alternate install path (e.g. user cd'd to ~/work/agnes-prod on purpose) and was redundant on the default path. Step 2 now verifies the user is in `$HOME/<workspace_dir>` via `pwd`. On mismatch it stops and asks the user to either re-paste from the correct folder or reply `install here` to accept the current cwd. Never auto-creates a folder. Step 9 (restart Claude Code) references the install directory confirmed in step 2 instead of a hardcoded `~/<workspace_dir>`, so users on a custom path see accurate guidance.
This commit is contained in:
parent
c6c72b9c00
commit
bd90485dbd
4 changed files with 152 additions and 17 deletions
|
|
@ -11,6 +11,15 @@ CalVer image tags (`stable-YYYY.MM.N`, `dev-YYYY.MM.N`) are produced for every C
|
|||
## [Unreleased]
|
||||
|
||||
### Changed
|
||||
- Setup script no longer auto-creates the workspace folder. Step 2 of
|
||||
the pasted prompt now runs `pwd`, compares it to `$HOME/<workspace_dir>`
|
||||
(the folder the /home page's visible Step 3 told the user to create
|
||||
manually), and on mismatch warns + asks the user to either re-paste
|
||||
from the right folder or reply `install here` to accept the current
|
||||
cwd. Respects an intentional alternate install path instead of
|
||||
silently switching the user back to the default. Step 9 (restart
|
||||
Claude Code) now references the install directory confirmed in step 2
|
||||
rather than a hardcoded `~/<workspace_dir>`.
|
||||
- **BREAKING (marketplace identifier)**: synthetic plugin bundling flea
|
||||
skills + agents renamed from `agnes-store-bundle` to `flea`. The
|
||||
served `marketplace.json` now lists `flea` (previously
|
||||
|
|
|
|||
|
|
@ -324,19 +324,29 @@ def _install_cli_lines(*, has_ca: bool, server_url_placeholder: str = "{server_u
|
|||
|
||||
|
||||
def _init_lines(server_url_placeholder: str = "{server_url}") -> list[str]:
|
||||
"""Steps 2-4 — workspace folder bootstrap, then `agnes init` + smoke verify.
|
||||
"""Steps 2-4 — workspace folder check, then `agnes init` + smoke verify.
|
||||
|
||||
Step 2 (new) explicitly creates the workspace folder and cd's into it.
|
||||
Previously the script assumed Claude Code was already cd'd to a sensible
|
||||
location and ran `agnes init --workspace .` against whatever that
|
||||
happened to be. With the new explicit mkdir+cd the workspace path is
|
||||
deterministic — `~/{workspace_dir}` — and the visible /home step block
|
||||
+ this scripted step stay in lockstep.
|
||||
Step 2 verifies the user is already cd'd into the workspace folder
|
||||
that the /home onboarding page's visible "Step 3 — create your
|
||||
workspace folder" told them to create manually (`mkdir -p
|
||||
~/{workspace_dir} && cd ~/{workspace_dir}`). The pasted script
|
||||
DOES NOT auto-create the folder — that would silently override an
|
||||
intentional choice to install at a different path (e.g. the user
|
||||
cd'd to `~/work/agnes-prod` on purpose). Instead we `pwd`, compare
|
||||
to `$HOME/{workspace_dir}`, and on mismatch warn loudly and ask the
|
||||
user to either re-paste from the right folder or explicitly confirm
|
||||
"install here" in the current cwd.
|
||||
|
||||
`{workspace_dir}` and `{instance_brand}` are placeholders pre-substituted
|
||||
by :func:`resolve_lines` from the operator-configured brand. Defaults
|
||||
keep `~/Agnes` behavior for instances that don't set the brand knob.
|
||||
|
||||
`agnes init --workspace .` continues to use the current cwd, so once
|
||||
step 2 has confirmed (or the user has explicitly accepted) the
|
||||
install dir, step 3 lands in the right place. Step 9's restart-claude
|
||||
cue references "this same directory" so users on a custom path see
|
||||
accurate guidance.
|
||||
|
||||
`agnes init` is the workspace-rails delivery mechanism for everyone:
|
||||
it authenticates with the PAT, fetches CLAUDE.md (RBAC-filtered),
|
||||
writes AGNES_WORKSPACE.md (human-facing docs), installs Claude Code
|
||||
|
|
@ -352,14 +362,36 @@ def _init_lines(server_url_placeholder: str = "{server_url}") -> list[str]:
|
|||
"""
|
||||
return [
|
||||
"",
|
||||
"2) Create the workspace folder in the user's home directory and cd into it:",
|
||||
" # POSIX (macOS / Linux / WSL):",
|
||||
" mkdir -p \"$HOME/{workspace_dir}\" && cd \"$HOME/{workspace_dir}\"",
|
||||
" # Windows (PowerShell) — only run if the user is on Windows:",
|
||||
" # New-Item -ItemType Directory -Force -Path \"$HOME\\{workspace_dir}\" | Out-Null",
|
||||
" # Set-Location \"$HOME\\{workspace_dir}\"",
|
||||
"2) Verify the user is already in the workspace folder.",
|
||||
" The /home page's visible \"Step 3 — create your workspace folder\"",
|
||||
" already asked the user to run",
|
||||
" mkdir -p ~/{workspace_dir} && cd ~/{workspace_dir}",
|
||||
" in their terminal BEFORE pasting this script. Do NOT silently",
|
||||
" re-create the folder here — the user may have intentionally cd'd",
|
||||
" to a different location to install {instance_brand} there instead.",
|
||||
"",
|
||||
" The remaining steps run inside this directory.",
|
||||
" Run:",
|
||||
" pwd",
|
||||
" Expected: $HOME/{workspace_dir} (i.e. ~/{workspace_dir})",
|
||||
"",
|
||||
" If `pwd` matches the expected path: continue silently to step 3.",
|
||||
"",
|
||||
" If `pwd` does NOT match, STOP and tell the user verbatim:",
|
||||
"",
|
||||
" \"You are in <current-pwd>, but {instance_brand} is normally",
|
||||
" installed in ~/{workspace_dir} (see /home Step 3). Either run",
|
||||
" mkdir -p ~/{workspace_dir} && cd ~/{workspace_dir}",
|
||||
" in your terminal now and re-paste this setup script, OR reply",
|
||||
" 'install here' to install {instance_brand} in <current-pwd>",
|
||||
" instead. Reply 'abort' to stop.\"",
|
||||
"",
|
||||
" Wait for the user's reply.",
|
||||
" - 'install here' → continue to step 3 in the current cwd.",
|
||||
" The cwd you saw from `pwd` is the install",
|
||||
" directory; remember it for step 9.",
|
||||
" - 'abort' / anything else → stop without making any changes.",
|
||||
" Do NOT run `mkdir`, do NOT `cd`, do NOT",
|
||||
" continue to step 3.",
|
||||
"",
|
||||
"3) Bootstrap your {instance_brand} workspace in this directory:",
|
||||
f" agnes init --server-url \"{server_url_placeholder}\" --token \"{{token}}\" --workspace .",
|
||||
|
|
@ -490,7 +522,7 @@ def _restart_claude_lines(step_num: str) -> list[str]:
|
|||
return [
|
||||
"",
|
||||
f"{step_num}) Restart Claude Code so every plugin, MCP server, and SessionStart hook installed above actually loads:",
|
||||
" Tell me to type `/exit` (or close the Claude Code session entirely), then run `claude` again from this same `~/{workspace_dir}` directory.",
|
||||
" Tell me to type `/exit` (or close the Claude Code session entirely), then run `claude` again from this same directory — the install dir confirmed in step 2 (`~/{workspace_dir}` on the default path, or whatever cwd the user explicitly accepted with 'install here').",
|
||||
" The next session boots with all marketplace plugins, every connector's keychain entries / OAuth grants, and the agnes-welcome + refresh-marketplace SessionStart hooks active. This is the last action before the Confirm summary — once I'm back in Claude Code, setup is complete.",
|
||||
]
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,10 @@ class TestInstanceBrand:
|
|||
workspace_dir="FoundryAI",
|
||||
))
|
||||
assert "Set up the Foundry AI CLI on this machine." in joined
|
||||
assert "mkdir -p \"$HOME/FoundryAI\"" in joined
|
||||
# Step 2 is a pwd-check now (no auto-mkdir); brand + workspace_dir
|
||||
# thread through the warning copy + expected-path string.
|
||||
assert "$HOME/FoundryAI" in joined
|
||||
assert "mkdir -p ~/FoundryAI && cd ~/FoundryAI" in joined
|
||||
assert "Bootstrap your Foundry AI workspace" in joined
|
||||
assert "Foundry AI workspace is ready" in joined
|
||||
# No raw placeholders survive substitution.
|
||||
|
|
@ -150,7 +153,10 @@ class TestInstanceBrand:
|
|||
from app.web.setup_instructions import resolve_lines
|
||||
joined = "\n".join(resolve_lines("agnes.whl"))
|
||||
assert "Set up the Agnes CLI on this machine." in joined
|
||||
assert "mkdir -p \"$HOME/Agnes\"" in joined
|
||||
# Step 2 is a pwd-check (no auto-mkdir); default path threads
|
||||
# through as `$HOME/Agnes` + the warning's manual-mkdir example.
|
||||
assert "$HOME/Agnes" in joined
|
||||
assert "mkdir -p ~/Agnes && cd ~/Agnes" in joined
|
||||
assert "Bootstrap your Agnes workspace" in joined
|
||||
assert "Agnes workspace is ready" in joined
|
||||
mod._instance_config = None
|
||||
|
|
|
|||
|
|
@ -1000,3 +1000,91 @@ def test_gws_prompt_emits_pass_fail_contract():
|
|||
body = gws_prompt(gws_oauth_configured=False)
|
||||
assert "✅ Google Workspace ready" in body
|
||||
assert "❌ Google Workspace setup failed" in body
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Step 2 — workspace folder check (no auto-mkdir).
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_step_2_checks_pwd_does_not_auto_mkdir():
|
||||
"""Step 2 must verify the user is already in the workspace folder
|
||||
(the /home page Step 3 manual mkdir+cd) instead of silently re-running
|
||||
`mkdir -p "$HOME/<dir>" && cd …` which would override an intentional
|
||||
alternate install path.
|
||||
|
||||
Contract:
|
||||
- Step 2 runs `pwd` and compares against `$HOME/<workspace_dir>`.
|
||||
- Emits a warning message naming both the expected path and the
|
||||
manual mkdir line the user already saw on /home.
|
||||
- Does NOT emit `mkdir -p "$HOME/<workspace_dir>"` or the equivalent
|
||||
PowerShell `New-Item -ItemType Directory -Force` line.
|
||||
- Asks the user to reply 'install here' to proceed in a non-default
|
||||
cwd, or 'abort' to stop. No automatic folder creation.
|
||||
"""
|
||||
from app.web.setup_instructions import resolve_lines
|
||||
|
||||
joined = "\n".join(resolve_lines("agnes.whl"))
|
||||
|
||||
# The new check-only step header.
|
||||
assert "2) Verify the user is already in the workspace folder." in joined
|
||||
|
||||
# `pwd` check + expected-path comparison must be present.
|
||||
assert "pwd" in joined
|
||||
assert "$HOME/Agnes" in joined # default workspace_dir
|
||||
assert "~/Agnes" in joined
|
||||
|
||||
# Warning copy that references the /home Step 3 manual mkdir line.
|
||||
assert "/home Step 3" in joined
|
||||
assert "mkdir -p ~/Agnes && cd ~/Agnes" in joined
|
||||
|
||||
# Explicit "install here" / "abort" decision tree.
|
||||
assert "'install here'" in joined
|
||||
assert "'abort'" in joined
|
||||
|
||||
# The auto-mkdir lines from the previous step 2 must be GONE.
|
||||
assert 'mkdir -p "$HOME/Agnes"' not in joined
|
||||
assert 'mkdir -p "$HOME/{workspace_dir}"' not in joined
|
||||
assert 'New-Item -ItemType Directory -Force -Path "$HOME\\Agnes"' not in joined
|
||||
assert "Set-Location \"$HOME\\Agnes\"" not in joined
|
||||
|
||||
|
||||
def test_step_2_warning_substitutes_custom_brand():
|
||||
"""Custom brand + workspace_dir must thread through the new step 2
|
||||
warning copy — no leftover placeholders, expected path matches the
|
||||
operator's configured workspace dir."""
|
||||
from app.web.setup_instructions import resolve_lines
|
||||
|
||||
joined = "\n".join(resolve_lines(
|
||||
"agnes.whl",
|
||||
instance_brand="Foundry AI",
|
||||
workspace_dir="FoundryAI",
|
||||
))
|
||||
assert "2) Verify the user is already in the workspace folder." in joined
|
||||
assert "$HOME/FoundryAI" in joined
|
||||
assert "~/FoundryAI" in joined
|
||||
assert "mkdir -p ~/FoundryAI && cd ~/FoundryAI" in joined
|
||||
# Brand + workspace_dir thread through the warning copy (the text
|
||||
# wraps across two lines so we check the substrings separately).
|
||||
assert "but Foundry AI is normally" in joined
|
||||
assert "installed in ~/FoundryAI" in joined
|
||||
# No placeholders survive into the rendered text.
|
||||
assert "{workspace_dir}" not in joined
|
||||
assert "{instance_brand}" not in joined
|
||||
|
||||
|
||||
def test_step_9_restart_references_install_dir_not_hardcoded():
|
||||
"""Step 9 must describe the restart cwd as the directory confirmed in
|
||||
step 2 (mentioning the default path) rather than a bare hardcoded
|
||||
`~/{workspace_dir}`. This keeps the wording accurate when the user
|
||||
chose an alternate install path via 'install here' in step 2."""
|
||||
from app.web.setup_instructions import resolve_lines
|
||||
|
||||
joined = "\n".join(resolve_lines("agnes.whl"))
|
||||
assert "9) Restart Claude Code" in joined
|
||||
# Wording references the step-2 confirmation.
|
||||
assert "install dir confirmed in step 2" in joined
|
||||
# Default path still mentioned as the expected baseline.
|
||||
assert "~/Agnes" in joined
|
||||
# The "install here" callout in the restart step keeps the user-flow
|
||||
# connection visible.
|
||||
assert "'install here'" in joined
|
||||
|
|
|
|||
Loading…
Reference in a new issue