Source code for ralph.onboarding

"""Shared onboarding copy for CLI, validation, and docs-facing messaging."""

from __future__ import annotations

from typing import Final

from ralph.skills._agent_paths import sibling_agent_skill_roots

GETTING_STARTED_DOC: Final[str] = "docs/sphinx/getting-started.md"
PROMPT_FILE: Final[str] = "PROMPT.md"
INIT_COMMAND: Final[str] = "ralph --init"
INIT_LOCAL_CONFIG_COMMAND: Final[str] = "ralph --init-local-config"
DIAGNOSE_COMMAND: Final[str] = "ralph --diagnose"
RUN_COMMAND: Final[str] = "ralph"
PROJECT_CANONICAL_SKILLS_PATH: Final[str] = "./.opencode/skills/"
PROJECT_SIBLING_SKILL_PATHS: Final[tuple[str, ...]] = (
    "./.claude/skills/",
    "./.codex/skills/",
    "./.gemini/antigravity-cli/skills/",
)
CODEBERG_REPO: Final[str] = "https://codeberg.org/RalphWorkflow/Ralph-Workflow"
ERROR_REPORTING_DISCLOSURE: Final[str] = (
    "Error reporting: Ralph Workflow sends anonymous crash data and performance metrics. "
    "No personal data is collected. See ~/.config/ralph-workflow-user.ini for details."
)
CODEBERG_STAR_CTA: Final[str] = (
    f"⭐ Star {CODEBERG_REPO} so we know you're using it — "
    "stars drive development priority. Run `ralph star` to star from your terminal."
)
RUN_COMPLETION_STAR_CTA: Final[str] = (
    f"⭐ If Ralph Workflow saved you time, star the repo: {CODEBERG_REPO}"
)
STARTER_PROMPT_SENTINEL: Final[str] = (
    "<!-- ralph:starter-prompt: edit this file before running `ralph` -->"
)


[docs] def getting_started_pointer_sentence() -> str: """Return the canonical getting-started docs pointer sentence.""" return f"New to Ralph Workflow? Read {GETTING_STARTED_DOC} for a step-by-step walkthrough."
[docs] def init_local_config_override_explanation() -> str: """Return the canonical explanation for the local override command.""" return "optional full project-local override copy of the user-global config set"
[docs] def init_help_text() -> str: """Return top-level help text for the canonical init command.""" explanation = init_local_config_override_explanation() return ( "Initialize Ralph Workflow in the current directory (scaffolds PROMPT.md plus " "project-local MCP/pipeline/artifact files copied from the user-global config set). " f"Use `{INIT_LOCAL_CONFIG_COMMAND}` only when you want an {explanation}. " "Also seeds project-scope skills and the batteries-included `.gitignore` when missing. " "Labels are deprecated and ignored; use `--init` without a label." )
[docs] def init_local_config_help_text() -> str: """Return top-level help text for the optional local override command.""" explanation = init_local_config_override_explanation() return f"Create .agent/ config files as an {explanation} for this repo."
[docs] def fresh_workspace_next_steps() -> tuple[str, ...]: """Return the minimal next steps for a completely fresh workspace.""" return ( "From a human-operated shell outside any Ralph-managed agent session, " f"run {INIT_COMMAND} to scaffold {PROMPT_FILE} and .agent/ configs", f"Edit {PROMPT_FILE} with your task", f"From that same human-operated shell, run {RUN_COMMAND} to start the pipeline", )
[docs] def welcome_panel_next_steps() -> tuple[str, ...]: """Return the richer onboarding steps shown after initialization succeeds.""" explanation = init_local_config_override_explanation() siblings = ", ".join(sibling.agent for sibling in sibling_agent_skill_roots()) project_siblings = ", ".join(PROJECT_SIBLING_SKILL_PATHS) return ( f"Edit {PROMPT_FILE} with your implementation task", "Skills and a batteries-included .gitignore are auto-seeded on every `ralph` run when " "missing (project scope: .opencode/skills/ canonical + symlinks to .claude/skills/, " ".codex/skills/, .gemini/antigravity-cli/skills/). Run `ralph --force-init-skills` to " "repair or overwrite a conflict.", "Install AI agents if missing (e.g., `claude`, `opencode`, `nanocoder`, `agy`, `cursor`)", f"Skills were installed to ~/.claude/skills/ and symlinked to {siblings}", f"Project-local skills were seeded to {PROJECT_CANONICAL_SKILLS_PATH}and symlinked to " f"{project_siblings} so every supported agent finds the same baseline. " f"Edit the SKILL.md in that canonical directory to customize; sibling symlinks are " f"preserved across `ralph` re-runs.", "The default .gitignore was seeded with patterns for Python, Node, Rust, Go, " "Ruby, PHP, Java/Kotlin, .NET, Dart/Flutter, Elixir, Scala, Terraform, " "and common IDE/OS files", f"(Optional) Run {INIT_LOCAL_CONFIG_COMMAND} when this repo needs an {explanation}", "(Recommended) From a human-operated shell outside any Ralph-managed agent " f"session, run {DIAGNOSE_COMMAND} to verify agents, MCP servers, and config " "before the first real run", f"From that same human-operated shell, run {RUN_COMMAND} to start the pipeline", "Run `ralph --regenerate-config` to reset configs", CODEBERG_STAR_CTA, )
[docs] def fallback_next_steps() -> tuple[str, ...]: """Return rerun guidance after init when files already exist.""" explanation = init_local_config_override_explanation() project_siblings = ", ".join(PROJECT_SIBLING_SKILL_PATHS) return ( f"Edit {PROMPT_FILE} with your implementation task", f"(Optional) Read {GETTING_STARTED_DOC} for a step-by-step first-run walkthrough", "Re-running init is idempotent; skills were re-checked and the default " ".gitignore was updated to cover common project structures (Python, Node, " "Rust, Go, Ruby, PHP, Java/Kotlin, .NET, Dart/Flutter, Elixir, Scala, " "Terraform, IDE/OS). A normal `ralph` run also auto-seeds the project-scope " "skills and .gitignore when missing, without requiring `ralph --init`.", f"Project-local skills under {PROJECT_CANONICAL_SKILLS_PATH}and the sibling symlinks " f"{project_siblings} were re-checked; re-running `ralph` is idempotent and will " f"not overwrite SKILL.md files you have edited.", f"(Optional) Run {INIT_LOCAL_CONFIG_COMMAND} when this repo needs an {explanation}", "(Optional) Configure MCP servers in `.agent/mcp.toml` or " "`~/.config/ralph-workflow-mcp.toml`", "(Optional) Review `.agent/pipeline.toml` and `.agent/artifacts.toml` " "if you need advanced workflow overrides", "(Recommended) From a human-operated shell outside any Ralph-managed agent " f"session, run {DIAGNOSE_COMMAND} to verify agents, MCP servers, and config " "before the first real run", f"From that same human-operated shell, run {RUN_COMMAND} to start the pipeline", )
[docs] def starter_prompt_template() -> str: """Return the canonical starter PROMPT.md template.""" return ( STARTER_PROMPT_SENTINEL + "\n\n" + "PROMPT.md is the goal and acceptance-criteria document that Ralph Workflow reads " + "as its task input. Replace the example content below with YOUR task description, " + "then remove the sentinel comment at the top before running `ralph`.\n\n" + "# Goal\n\n" + "Add a /health endpoint to the example API that returns HTTP 200 with a JSON body" + ' `{"status": "ok"}`.\n' + "This endpoint should be unauthenticated and return a Content-Type of" + " application/json.\n" + "It is used by load balancers and uptime monitors to verify the service is" + " running.\n\n" + "## Context\n\n" + "- Main API entry point: `src/api/app.py`\n" + "- Existing route examples: `src/api/routes/`\n" + "- Dependencies and external services: see `README.md`\n\n" + "## Acceptance criteria\n\n" + "- GET /health returns HTTP 200\n" + "- Response body is valid JSON with `status` == `ok`\n" + "- A new test in `tests/` covers the new endpoint\n\n" + "## Notes\n\n" + "- Keep the prompt scoped — one user-visible outcome per run works best.\n" + "- Describe constraints (language, framework, test style) in Context above.\n\n" + "---\n\n" + "**Next steps**\n\n" + "1. Edit the sections above to describe YOUR task and remove the sentinel comment.\n" + f"2. Run `{DIAGNOSE_COMMAND}` to verify agents, MCP servers, and config " + "before the first real run.\n" + f"3. Run `{RUN_COMMAND}` to start the planning → development pipeline.\n" )
[docs] def missing_prompt_validation_hint() -> str: """Return canonical validation guidance when PROMPT.md is missing.""" return ( "PROMPT.md is the goal/acceptance-criteria document Ralph Workflow reads as its " f"task input. Run `{INIT_COMMAND}` to scaffold PROMPT.md and project config files, " "then edit PROMPT.md with the task you want Ralph Workflow to run. " f"{getting_started_pointer_sentence()}" )
[docs] def starter_prompt_validation_hint() -> str: """Return canonical validation guidance when the starter sentinel is still present.""" return ( "Edit PROMPT.md to describe YOUR task and remove the `<!-- ralph:starter-prompt " "... -->` marker once you have replaced the example content. " f"{DIAGNOSE_COMMAND} is a recommended verification step after initialization. " f"Then re-run `{RUN_COMMAND}`. New to Ralph Workflow? See {GETTING_STARTED_DOC} " "for a walkthrough, or docs/sphinx/concepts.md for what a good PROMPT.md should " "contain." )