Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- AGENTS.md -->
# AGENTS.md

## Scope
Expand Down Expand Up @@ -46,10 +47,15 @@ Record the exact commands and their results in the pull request description.

## Branch and Merge Policy

- Branch from the current `main` and target pull requests to `main`.
- Use `dev` only when an explicitly approved release plan reactivates it.
- Follow `CONTRIBUTING.md` for branch naming, pull request content, and the
contributor workflow.
- Branch from the current `dev` using `feature/<name>` or `fix/<name>`, and
target pull requests to `dev`.
- Keep pull requests in draft until local validation is complete and recorded.
- Merge with a merge commit only after CI passes and review feedback is resolved.
- Squash-merge feature and fix pull requests into `dev` only after CI passes and
review feedback is resolved.
- Promote a verified `dev` branch to `main` with a merge commit. Do not squash
the `dev` to `main` release promotion.
- Never merge or enable auto-merge without explicit maintainer approval.

## Compatibility and Architecture
Expand Down
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,26 @@
<!-- CHANGELOG.md -->
# Changelog

All notable changes to this project are documented here. Pythonlings follows
Semantic Versioning.

## [0.4.2] - 2026-08-17

### Changed

- Refreshed the project branding, screenshots, terminal demo, and contributor
guidance.

### Fixed

- `pythonlings update` now prefers the current workspace while preserving the
precedence of an explicit `--path` and global `--root`. Missing and invalid
targets fail without being created or modified.
- Invalid, unreadable, and unsafe manifests now produce contextual command-line
errors without Python tracebacks.
- Workspace initialization and updates preserve custom `.gitignore` entries,
ordering, line endings, and repeated-run idempotency.

## [0.4.1] - 2026-06-21

### Added
Expand Down
23 changes: 18 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
<!-- CONTRIBUTING.md -->
# Contributing

Pythonlings is actively developed and **open to contributors** — beginners welcome.
The fastest way in is a [`good first issue`](https://github.com/abhiksark/pythonlings/issues?q=is%3Aopen+label%3A%22good+first+issue%22).

## Where the work is

- **[0.3.0 roadmap](docs/roadmap/0.3.0.md)** — the current focus (wider adoption
for beginners). Each roadmap issue is written to be picked up cold: it has
context, scope, the exact files to touch, and how to verify.
- Browse open issues by label: [`good first issue`](https://github.com/abhiksark/pythonlings/issues?q=is%3Aopen+label%3A%22good+first+issue%22),
- Track current work in the
[open issue tracker](https://github.com/abhiksark/pythonlings/issues?q=is%3Aissue+is%3Aopen).
- Find contributor-ready work by label: [`good first issue`](https://github.com/abhiksark/pythonlings/issues?q=is%3Aopen+label%3A%22good+first+issue%22),
[`help wanted`](https://github.com/abhiksark/pythonlings/issues?q=is%3Aopen+label%3A%22help+wanted%22).

## Claiming an issue
Expand Down Expand Up @@ -39,8 +39,21 @@ reference solution** (`tests/integration/test_solution_verify.py` enforces this)

## Pull Requests

- Use focused branches named `feature/<name>` or `fix/<name>`.
- Create focused branches from the current `dev` branch, named
`feature/<name>` or `fix/<name>`.
- Open pull requests against `dev`. Feature and fix pull requests are
squash-merged after CI passes and review feedback is resolved.
- Reference the issue you're closing (`Closes #NN`).
- Include a short description, test output (`python -m pytest -q`), and
screenshots/GIFs for TUI changes.
- Keep PRs scoped to one issue where possible.

## Release Flow

```text
feature/<name> or fix/<name> -> dev -> main -> vMAJOR.MINOR.PATCH
```

Maintainers promote a verified `dev` branch to `main` with a merge commit, not
a squash merge. The release tag is created from the exact promoted commit on
`main`; see [RELEASE.md](RELEASE.md) for the release checklist.
Comment thread
coderabbitai[bot] marked this conversation as resolved.
5 changes: 3 additions & 2 deletions RELEASE.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- RELEASE.md -->
# Release Checklist

Pythonlings follows Semantic Versioning. Use full `MAJOR.MINOR.PATCH` versions in
Expand Down Expand Up @@ -27,10 +28,10 @@ pythonlings --root "$tmp" solution variables1
pythonlings --root "$tmp" reset variables1 --yes
```

Expected release version for `v0.3.0`:
Confirm that the command reports the version represented by the release tag:

```text
pythonlings 0.3.0
pythonlings MAJOR.MINOR.PATCH
```

## Tag And Publish
Expand Down
9 changes: 5 additions & 4 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ uvx pythonlings
How it works: **edit** the broken exercise in the built-in editor → checks
rerun as you type and advance you to the next one. That's the whole loop.

Status: `v0.4.0`, alpha — published on PyPI as `pythonlings`.
Status: alpha. Published on PyPI as `pythonlings`.

![Coding screen](docs/assets/screenshots/coding-screen.png)

Expand Down Expand Up @@ -193,9 +193,10 @@ together. Keep exercise and check filenames mirrored, for example

## Contributing

Pythonlings is actively developed and welcomes contributors — beginners included.
The current focus is the [0.3.0 roadmap](docs/roadmap/0.3.0.md) (wider adoption),
and every roadmap issue is written to be picked up cold. Start with a
Pythonlings is actively developed and welcomes contributors, including
beginners. Current work is tracked in the
[open issue tracker](https://github.com/abhiksark/pythonlings/issues?q=is%3Aissue+is%3Aopen).
Start with a
[`good first issue`](https://github.com/abhiksark/pythonlings/issues?q=is%3Aopen+label%3A%22good+first+issue%22),
comment to claim it, and see [CONTRIBUTING.md](CONTRIBUTING.md).

Expand Down
3 changes: 2 additions & 1 deletion docs-site/faq.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- docs-site/faq.md -->
# FAQ

## How is this different from Rustlings?
Expand Down Expand Up @@ -26,7 +27,7 @@ Yes. All exercises and the bundled local Python reference (press `F5` in the TUI

## Is Pythonlings on PyPI?

Yes — install it as [`pythonlings`](https://pypi.org/project/pythonlings/) (current release: `v0.4.0`).
Yes. Install it as [`pythonlings`](https://pypi.org/project/pythonlings/).

## How do I see the reference answer?

Expand Down
7 changes: 5 additions & 2 deletions docs-site/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
hide:
- toc
---
<!-- docs-site/index.md -->

<div class="pl-hero" markdown>
<div class="pl-eyebrow">Rustlings for Python</div>
Expand Down Expand Up @@ -38,5 +39,7 @@ Prefer a permanent install? See [Quick Start](quick-start.md) for `pipx`, `uv to

## Project status

Pythonlings is `v0.4.0`, published on PyPI as `pythonlings`. The learner loop, CLI, and
curriculum are stable; see the [Roadmap](roadmap.md) for what's next.
Pythonlings is published on PyPI as `pythonlings`. The learner loop, CLI, and
curriculum are stable; see the [Roadmap](roadmap.md) and
[open issue tracker](https://github.com/abhiksark/pythonlings/issues?q=is%3Aissue+is%3Aopen)
for current work.
3 changes: 2 additions & 1 deletion docs-site/quick-start.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<!-- docs-site/quick-start.md -->
# Quick Start

> Current release: **v0.4.0** · [PyPI](https://pypi.org/project/pythonlings/)
> Latest release: [pythonlings on PyPI](https://pypi.org/project/pythonlings/)

## Zero-Install (uvx)

Expand Down
16 changes: 8 additions & 8 deletions docs-site/roadmap.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<!-- docs-site/roadmap.md -->
# Roadmap

Pythonlings is `v0.4.0`, published on PyPI as `pythonlings`. Install with
`uvx pythonlings` or `pip install pythonlings`.
Pythonlings is published on PyPI as `pythonlings`. Install with `uvx
pythonlings` or `pip install pythonlings`.

## Shipped

Expand All @@ -11,13 +12,12 @@ Pythonlings is `v0.4.0`, published on PyPI as `pythonlings`. Install with
- Bundled Python docs snippets with official docs links.
- Published on PyPI as `pythonlings`; canonical install is `uvx pythonlings`.

## Next Work
## Active Work

- Improve first-run onboarding and empty-state copy.
- Harden keyboard flow around `Enter`, `Esc`, `F4`, and `F5`.
- Add more TUI tests for the coding screen, docs window, and topic picker.
- Add a release smoke test that installs the built wheel and exercises the CLI.
- Continue auditing exercises for clearer hints and stronger hidden checks.
Current priorities and ready-to-pick-up tasks are maintained in the
[open issue tracker](https://github.com/abhiksark/pythonlings/issues?q=is%3Aissue+is%3Aopen).
New contributors can start with the
[`good first issue` label](https://github.com/abhiksark/pythonlings/issues?q=is%3Aopen+label%3A%22good+first+issue%22).

## Release Policy

Expand Down
9 changes: 5 additions & 4 deletions docs/RELEASE_PROCESS.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
<!-- docs/RELEASE_PROCESS.md -->
# Release Process

Pythonlings uses a feature-branch workflow and Semantic Versioning.
Expand All @@ -10,7 +11,7 @@ Pythonlings uses a feature-branch workflow and Semantic Versioning.

## Versioning

Use `MAJOR.MINOR` release tags.
Use `MAJOR.MINOR.PATCH` release tags.

- Increment `MAJOR` for incompatible CLI, manifest, or curriculum changes.
- Increment `MINOR` for new exercises, topics, TUI features, or docs workflows.
Expand All @@ -21,7 +22,7 @@ Use `MAJOR.MINOR` release tags.
1. Merge feature branches into `dev` with reviewed, focused commits.
2. Run `python -m pytest -q`.
3. Run `pythonlings --root tests/fixtures/passing_curriculum verify`.
4. Update `CHANGELOG.md` and the version in `pythonlings/cli.py` and `pyproject.toml`.
5. Merge `dev` into `main`.
6. Create an annotated tag, for example `git tag -a v0.1 -m "Release v0.1"`.
4. Update `CHANGELOG.md` and the version in `pyproject.toml`.
5. Merge the verified `dev` branch into `main` with a merge commit.
6. Create an annotated tag, for example `git tag -a v0.4.2 -m "Release v0.4.2"`.
7. Push `main`, `dev`, and tags.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
# pyproject.toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "pythonlings"
version = "0.4.1"
version = "0.4.2"
description = "Python learnings, Rustlings-style, in a terminal TUI."
readme = "Readme.md"
requires-python = ">=3.9"
Expand Down
13 changes: 10 additions & 3 deletions pythonlings/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def _build_parser() -> argparse.ArgumentParser:
)

p_update = sub.add_parser("update", help="Update an existing pythonlings workspace.")
p_update.add_argument("--path", type=Path, default=default_workspace_root())
p_update.add_argument("--path", type=Path, default=None)

sub.add_parser("watch", help="Launch the TUI in watch mode (default).")
sub.add_parser("topics", help="Launch the TUI on the topic picker.")
Expand Down Expand Up @@ -278,8 +278,14 @@ def main(argv: list[str] | None = None) -> int:

try:
root: Path | None = None
if args.command in ("init", "update"):
update_root: Path | None = None
if args.command == "init":
migrate_legacy_state_dir(Path(args.path))
elif args.command == "update":
explicit_root = args.path if args.path is not None else args.root
update_root = resolve_workspace_root(
Path.cwd(), explicit_root, create_if_missing=False
).root
else:
launches_tui = args.command in (None, "watch", "start", "topics")
resolved = resolve_workspace_root(
Expand All @@ -305,7 +311,8 @@ def main(argv: list[str] | None = None) -> int:
if args.command == "init":
return _cmd_init(args.path, args.force)
if args.command == "update":
return _cmd_update(args.path)
assert update_root is not None
return _cmd_update(update_root)

assert root is not None
if args.command == "verify":
Expand Down
9 changes: 7 additions & 2 deletions pythonlings/core/curriculum.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# pythonlings/core/curriculum.py
from __future__ import annotations

import shutil
Expand Down Expand Up @@ -109,10 +110,14 @@ def init_workspace(path: Path, *, force: bool = False) -> Path:

def update_workspace(path: Path) -> Path:
path = path.expanduser().resolve()
if not (path / "info.toml").exists():
if not (path / "info.toml").is_file():
raise WorkspaceError(f"{path} is not a pythonlings workspace")

src_root = source_root()
src_root = source_root().resolve()
if path == src_root:
raise WorkspaceError(f"cannot update the curriculum source at {path}")

migrate_legacy_state_dir(path)
_copy_path(src_root / "info.toml", path / "info.toml", overwrite=True)
_copy_path(src_root / "checks", path / "checks", overwrite=True)
_copy_path(src_root / "solutions", path / "solutions", overwrite=True)
Expand Down
Loading
Loading