diff --git a/README.md b/README.md index f6993ca..e71c164 100644 --- a/README.md +++ b/README.md @@ -109,6 +109,18 @@ The package requires Python 3.10 or newer and pins the supported Base-CLI line to `>=0.4.3,<0.5`. This checkout targets demo release `v0.1.0`; demo release versioning remains separate from framework versioning. +## Documentation + +Follow the [documentation index](docs/README.md) for an ordered path from +adoption decision through the Northstar walkthrough, compatibility, and +release. To start with a minimal consumer rather than the full demo, use the +[copyable starter](docs/use-in-your-project.md). + +For framework-level material, start at the +[Base-CLI repository](https://github.com/basefoundry/base-cli), its +[getting-started guide](https://github.com/basefoundry/base-cli#quick-start), +or the [public API reference](https://github.com/basefoundry/base-cli/blob/main/docs/api-reference.md). + ## Repository shape - `src/base_cli_demo/cli.py` contains the consumer-owned Click tree and the diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..34d0866 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,28 @@ +# Documentation guide + +Use this path to decide whether Base-CLI fits, then move from a tiny starter to +the complete Northstar consumer: + +1. [Why Base-CLI?](why-base-cli.md) — the lifecycle work it supplies and what + Northstar keeps in the application. +2. [Should I use Base-CLI?](should-i-use-base-cli.md) — maturity, fit, costs, + and alternatives. +3. [Use Base-CLI in your project](use-in-your-project.md) — a minimal command + you can copy before exploring the larger demo. +4. [Five-minute learning path](learning-path.md) — install Northstar and follow + its human and automation scenarios. +5. [Lifecycle safety](lifecycle-safety.md) — dry-run, structured errors, + diagnostics, temporary files, and cleanup. +6. [Configuration](configuration.md) — the consumer-owned settings and + provenance boundary. +7. [Optional integrations](optional-integrations.md) — Typer, Rich, and + OpenTelemetry extras and fallback paths. +8. [Compatibility](compatibility.md) — the released Base-CLI range and + installed-wheel checks. +9. [Release process](release-process.md) — independent demo versioning, + reproducible packages, and guarded GitHub Release steps. + +For the framework's complete material, start at the +[Base-CLI repository](https://github.com/basefoundry/base-cli), its +[getting-started guide](https://github.com/basefoundry/base-cli#quick-start), +or the [public API reference](https://github.com/basefoundry/base-cli/blob/main/docs/api-reference.md). diff --git a/docs/use-in-your-project.md b/docs/use-in-your-project.md new file mode 100644 index 0000000..edf34b2 --- /dev/null +++ b/docs/use-in-your-project.md @@ -0,0 +1,52 @@ +# Use Base-CLI in your project + +Northstar is intentionally production-shaped. If you want the smallest +transferable pattern, start with one Click command and let Base-CLI own the +invocation lifecycle. + +Install the released framework line used by this demo: + +```bash +python -m pip install "base-cli>=0.4.3,<0.5" "click>=8.1,<9" +``` + +Save this as `hello.py`: + +```python +from __future__ import annotations + +import base_cli +import click + + +@click.command() +@click.option("--name", default="world", show_default=True) +def hello(name: str) -> None: + click.echo(f"hello {name}") + + +app = base_cli.App(name="hello", version="0.1.0") +command = app.attach(hello) + + +if __name__ == "__main__": + raise SystemExit(base_cli.run_app(command)) +``` + +Run it: + +```console +$ python hello.py --name Ada +hello Ada +``` + +The runnable copy in [`examples/minimal_cli.py`](../examples/minimal_cli.py) +is exercised by the test suite. From the example, continue with the +[five-minute Northstar tour](learning-path.md) to see nested commands, +consumer-owned configuration, and structured output. + +The `App` and `attach` calls establish the framework boundary; the Click +function remains your command. See the framework's +[consumer quickstart](https://github.com/basefoundry/base-cli/blob/main/docs/consumer-quickstart.md) +and [API reference](https://github.com/basefoundry/base-cli/blob/main/docs/api-reference.md) +for the public interfaces. diff --git a/examples/minimal_cli.py b/examples/minimal_cli.py new file mode 100644 index 0000000..68e2d84 --- /dev/null +++ b/examples/minimal_cli.py @@ -0,0 +1,28 @@ +"""Smallest runnable Click command attached to the Base-CLI lifecycle.""" + +from __future__ import annotations + +import base_cli +import click + + +@click.command() +@click.option("--name", default="world", show_default=True) +def hello(name: str) -> None: + """Greet one person.""" + + click.echo(f"hello {name}") + + +app = base_cli.App(name="hello", version="0.1.0") +command = app.attach(hello) + + +def main() -> int: + """Run the minimal example through Base-CLI.""" + + return base_cli.run_app(command) + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills.md b/skills.md index 91e1e81..2ec3afa 100644 --- a/skills.md +++ b/skills.md @@ -3,16 +3,41 @@ Use this file as the repo-local index for project-specific agent workflows. Keep entries short, concrete, and owned by this repository. -## Suggested Entries - -- Development workflow: issue selection, branch naming, validation, PR creation, - merge, and cleanup. -- Testing workflow: the commands that prove common changes are safe. -- Release workflow: read `docs/release-process.md` when the repository declares - release metadata, then follow its version, changelog, tag, release, and - package manager steps. -- Domain workflow: product-specific checks or demo expectations that agents - should not have to rediscover. +## Development workflow + +- Start from a GitHub issue and use its single category label in the branch: + `/--`. +- Use a dedicated worktree from `origin/main`; keep each PR scoped to one issue. +- Link a completing PR with `Fixes #`. Do not merge unless the user + explicitly asks for it. +- Preserve existing changes and leave the main checkout clean. + +## Validation workflow + +- Run `./tests/validate.sh` for the authoritative consumer gate. +- Run `uv run --extra dev pytest -q` for the complete default suite. +- Run `uv run --extra dev tests/package.sh` for wheel, sdist, and Twine checks. +- Run `uv lock --check` when changing dependencies or extras. +- Optional scenarios should be checked both without extras and with + `uv run --extra dev --extra typer --extra rich --extra telemetry pytest -q`. + +## Product boundary + +- Keep Northstar deterministic, offline, and independent of the Base workspace + runtime. It consumes released `base-cli` APIs only. +- Keep parser commands, service fixtures, and domain policy in this repository; + keep lifecycle behavior at the public `base_cli` boundary. +- Do not make ecosystem/catalog examples depend on unreleased platform + contracts. + +## Release workflow + +- Read `docs/release-process.md` before editing `VERSION`, release notes, tags, + or GitHub Releases. +- Verify package contents and `basectl release check/plan/notes` first. +- Use `basectl release publish --version X.Y.Z --dry-run` to review the plan. + Never create a tag or publish a GitHub Release without explicit user + authorization. ## Boundaries diff --git a/tests/test_minimal_starter.py b/tests/test_minimal_starter.py new file mode 100644 index 0000000..a68474e --- /dev/null +++ b/tests/test_minimal_starter.py @@ -0,0 +1,26 @@ +from __future__ import annotations + +import os +import subprocess +import sys +from pathlib import Path + + +def test_minimal_starter_runs_with_the_installed_framework(tmp_path: Path) -> None: + script = Path(__file__).parents[1] / "examples" / "minimal_cli.py" + environment = os.environ.copy() + environment["BASE_CLI_CACHE_DIR"] = str(tmp_path / "cache") + environment["HOME"] = str(tmp_path / "home") + environment["USERPROFILE"] = str(tmp_path / "home") + + result = subprocess.run( + [sys.executable, str(script), "--name", "Ada"], + capture_output=True, + cwd=tmp_path, + env=environment, + text=True, + check=False, + ) + + assert result.returncode == 0, result.stderr + assert result.stdout.strip() == "hello Ada"