minitest-cli 0.3.0__tar.gz → 0.4.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. minitest_cli-0.4.1/.env.example +16 -0
  2. minitest_cli-0.4.1/.github/workflows/ci.yml +65 -0
  3. minitest_cli-0.4.1/.github/workflows/release.yml +130 -0
  4. minitest_cli-0.4.1/.gitignore +85 -0
  5. minitest_cli-0.4.1/.opencode/skill/release/SKILL.md +52 -0
  6. minitest_cli-0.4.1/AGENTS.md +61 -0
  7. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/PKG-INFO +12 -13
  8. minitest_cli-0.4.1/RELEASE.md +90 -0
  9. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/pyproject.toml +5 -6
  10. minitest_cli-0.4.1/pyrightconfig.json +3 -0
  11. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/flow.py +8 -7
  12. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/flow_helpers.py +29 -0
  13. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/flow_modify.py +6 -2
  14. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/skill.py +3 -1
  15. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/models/__init__.py +0 -2
  16. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/models/flow_template.py +3 -17
  17. minitest_cli-0.4.1/tests/__init__.py +0 -0
  18. minitest_cli-0.4.1/tests/test_apps_commands.py +144 -0
  19. minitest_cli-0.4.1/tests/test_auth.py +283 -0
  20. minitest_cli-0.4.1/tests/test_auth_commands.py +208 -0
  21. minitest_cli-0.4.1/tests/test_build_commands.py +403 -0
  22. minitest_cli-0.4.1/tests/test_code_quality.py +22 -0
  23. minitest_cli-0.4.1/tests/test_flow_commands.py +287 -0
  24. minitest_cli-0.4.1/tests/test_run_commands.py +827 -0
  25. minitest_cli-0.4.1/tests/test_skill_command.py +79 -0
  26. minitest_cli-0.4.1/tests/test_version.py +12 -0
  27. minitest_cli-0.4.1/uv.lock +443 -0
  28. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/README.md +0 -0
  29. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/__init__.py +0 -0
  30. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/api/__init__.py +0 -0
  31. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/api/client.py +0 -0
  32. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/assets/__init__.py +0 -0
  33. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/assets/callback.html +0 -0
  34. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/__init__.py +0 -0
  35. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/apps.py +0 -0
  36. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/auth.py +0 -0
  37. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/build.py +0 -0
  38. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/build_helpers.py +0 -0
  39. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/run.py +0 -0
  40. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/run_display.py +0 -0
  41. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/commands/run_helpers.py +0 -0
  42. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/__init__.py +0 -0
  43. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/app_context.py +0 -0
  44. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/auth.py +0 -0
  45. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/config.py +0 -0
  46. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/credentials.py +0 -0
  47. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/oauth.py +0 -0
  48. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/core/token_exchange.py +0 -0
  49. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/main.py +0 -0
  50. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/models/app.py +0 -0
  51. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/models/base.py +0 -0
  52. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/models/build.py +0 -0
  53. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/models/flow_run.py +0 -0
  54. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/utils/__init__.py +0 -0
  55. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/utils/output.py +0 -0
  56. {minitest_cli-0.3.0 → minitest_cli-0.4.1}/src/minitest_cli/utils/update_check.py +0 -0
@@ -0,0 +1,16 @@
1
+ # Minitest CLI configuration
2
+ # Copy to .env and customize as needed. All values below are optional —
3
+ # sensible defaults are built into the CLI.
4
+
5
+ # Testing service base URL
6
+ # MINITEST_API_URL=
7
+
8
+ # Pre-authenticated token (bypasses OAuth login)
9
+ # MINITEST_TOKEN=
10
+
11
+ # Default app ID for commands that require --app
12
+ # MINITEST_APP_ID=
13
+
14
+ # Supabase OAuth settings
15
+ # MINITEST_SUPABASE_URL=
16
+ # MINITEST_SUPABASE_PUBLISHABLE_KEY=
@@ -0,0 +1,65 @@
1
+ name: CI 🧪
2
+
3
+ on:
4
+ pull_request:
5
+ push:
6
+ branches:
7
+ - main
8
+
9
+ jobs:
10
+ build:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - name: Clone the repository
14
+ uses: actions/checkout@v5
15
+
16
+ - name: Install uv
17
+ uses: astral-sh/setup-uv@v6
18
+ with:
19
+ enable-cache: true
20
+
21
+ - name: Build package
22
+ run: uv build
23
+
24
+ pyright:
25
+ runs-on: ubuntu-latest
26
+ steps:
27
+ - uses: actions/checkout@v5
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v6
30
+ with:
31
+ enable-cache: true
32
+ - name: Install dependencies
33
+ run: uv sync --dev
34
+ - name: Run pyright
35
+ run: uv run pyright
36
+
37
+ ruff:
38
+ runs-on: ubuntu-latest
39
+ steps:
40
+ - uses: actions/checkout@v5
41
+ - name: Run ruff format check
42
+ uses: astral-sh/ruff-action@v3
43
+ with:
44
+ args: "format --check"
45
+ - name: Run ruff linter
46
+ uses: astral-sh/ruff-action@v3
47
+ with:
48
+ args: "check"
49
+
50
+ test:
51
+ runs-on: ubuntu-latest
52
+ steps:
53
+ - name: Clone the repository
54
+ uses: actions/checkout@v5
55
+
56
+ - name: Install uv
57
+ uses: astral-sh/setup-uv@v6
58
+ with:
59
+ enable-cache: true
60
+
61
+ - name: Install dependencies
62
+ run: uv sync --dev
63
+
64
+ - name: Run tests
65
+ run: uv run pytest
@@ -0,0 +1,130 @@
1
+ name: Release 🚀
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*.*.*"
7
+
8
+ permissions: {}
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - name: Clone the repository
15
+ uses: actions/checkout@v5
16
+
17
+ - name: Install uv
18
+ uses: astral-sh/setup-uv@v6
19
+ with:
20
+ enable-cache: true
21
+
22
+ - name: Verify tag matches package version
23
+ run: |
24
+ python3 - <<'PY'
25
+ import os, pathlib, sys, tomllib
26
+ version = tomllib.loads(pathlib.Path("pyproject.toml").read_text())["project"]["version"]
27
+ tag_version = os.environ["GITHUB_REF_NAME"].removeprefix("v")
28
+ if version != tag_version:
29
+ print(f"::error::Tag v{tag_version} does not match pyproject.toml version {version}")
30
+ sys.exit(1)
31
+ print(f"Version {version} matches tag")
32
+ PY
33
+
34
+ - name: Build package
35
+ run: uv build
36
+
37
+ - name: Upload build artifacts
38
+ uses: actions/upload-artifact@v4
39
+ with:
40
+ name: dist
41
+ path: dist/
42
+
43
+ publish-pypi:
44
+ needs: build
45
+ runs-on: ubuntu-latest
46
+ environment:
47
+ name: pypi
48
+ url: https://pypi.org/project/minitest-cli/
49
+ permissions:
50
+ id-token: write
51
+ steps:
52
+ - name: Download build artifacts
53
+ uses: actions/download-artifact@v4
54
+ with:
55
+ name: dist
56
+ path: dist/
57
+
58
+ - name: Publish to PyPI
59
+ uses: pypa/gh-action-pypi-publish@release/v1
60
+
61
+ github-release:
62
+ needs: publish-pypi
63
+ runs-on: ubuntu-latest
64
+ permissions:
65
+ contents: write
66
+ steps:
67
+ - name: Clone the repository
68
+ uses: actions/checkout@v5
69
+
70
+ - name: Download build artifacts
71
+ uses: actions/download-artifact@v4
72
+ with:
73
+ name: dist
74
+ path: dist/
75
+
76
+ - name: Create GitHub Release
77
+ uses: softprops/action-gh-release@v2
78
+ with:
79
+ generate_release_notes: true
80
+ files: dist/*
81
+
82
+ update-homebrew:
83
+ needs: publish-pypi
84
+ runs-on: ubuntu-latest
85
+ steps:
86
+ - name: Fetch PyPI metadata
87
+ id: pypi
88
+ run: |
89
+ VERSION="${GITHUB_REF_NAME#v}"
90
+ echo "version=$VERSION" >> "$GITHUB_OUTPUT"
91
+
92
+ echo "Waiting for minitest-cli $VERSION to be available on PyPI..."
93
+ for i in $(seq 1 30); do
94
+ if curl -sSf --connect-timeout 5 --max-time 15 \
95
+ "https://pypi.org/pypi/minitest-cli/$VERSION/json" -o /tmp/pypi.json.tmp; then
96
+ mv /tmp/pypi.json.tmp /tmp/pypi.json
97
+ echo "Package is available on PyPI"
98
+ break
99
+ fi
100
+ echo "Attempt $i/30 - not yet available, waiting 10s..."
101
+ sleep 10
102
+ done
103
+
104
+ if [ ! -f /tmp/pypi.json ]; then
105
+ echo "::error::Package not found on PyPI after 5 minutes"
106
+ exit 1
107
+ fi
108
+
109
+ SDIST_URL=$(jq -r '.urls[] | select(.packagetype == "sdist") | .url' /tmp/pypi.json)
110
+ SDIST_SHA256=$(jq -r '.urls[] | select(.packagetype == "sdist") | .digests.sha256' /tmp/pypi.json)
111
+ if [ -z "$SDIST_URL" ] || [ "$SDIST_URL" = "null" ] || [ -z "$SDIST_SHA256" ] || [ "$SDIST_SHA256" = "null" ]; then
112
+ echo "::error::sdist metadata is missing from PyPI response"
113
+ exit 1
114
+ fi
115
+ echo "sdist_url=$SDIST_URL" >> "$GITHUB_OUTPUT"
116
+ echo "sdist_sha256=$SDIST_SHA256" >> "$GITHUB_OUTPUT"
117
+
118
+ - name: Trigger Homebrew tap update
119
+ uses: peter-evans/repository-dispatch@v3
120
+ with:
121
+ token: ${{ secrets.HOMEBREW_TAP_TOKEN }}
122
+ repository: minitap-ai/homebrew-tap
123
+ event-type: update-formula
124
+ client-payload: |
125
+ {
126
+ "formula": "minitest-cli",
127
+ "version": "${{ steps.pypi.outputs.version }}",
128
+ "sdist_url": "${{ steps.pypi.outputs.sdist_url }}",
129
+ "sdist_sha256": "${{ steps.pypi.outputs.sdist_sha256 }}"
130
+ }
@@ -0,0 +1,85 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ *.egg-info/
24
+ .installed.cfg
25
+ *.egg
26
+
27
+ # PyInstaller
28
+ *.manifest
29
+ *.spec
30
+
31
+ # Installer logs
32
+ pip-log.txt
33
+ pip-delete-this-directory.txt
34
+
35
+ # Unit test / coverage reports
36
+ htmlcov/
37
+ .tox/
38
+ .nox/
39
+ .coverage
40
+ .coverage.*
41
+ .cache
42
+ nosetests.xml
43
+ coverage.xml
44
+ *.cover
45
+ *.py,cover
46
+ .hypothesis/
47
+ .pytest_cache/
48
+
49
+ # Translations
50
+ *.mo
51
+ *.pot
52
+
53
+ # Environments
54
+ .env
55
+ .venv
56
+ env/
57
+ venv/
58
+ ENV/
59
+ env.bak/
60
+ venv.bak/
61
+
62
+ # IDE
63
+ .idea/
64
+ .vscode/
65
+ *.swp
66
+ *.swo
67
+ *~
68
+
69
+ # mypy
70
+ .mypy_cache/
71
+ .dmypy.json
72
+ dmypy.json
73
+
74
+ # Pyright
75
+ .pyright/
76
+
77
+ # Ruff
78
+ .ruff_cache/
79
+
80
+ # macOS
81
+ .DS_Store
82
+
83
+ # Opencode (local agent config — keep skills tracked)
84
+ .opencode/*
85
+ !.opencode/skill/
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: release
3
+ description: >
4
+ Release a new version of minitest-cli. Use when the user says "release",
5
+ "tag a new version", "bump version", "create a release", or "publish".
6
+ Determines the next semver tag from git history, updates pyproject.toml,
7
+ syncs the lockfile, commits, tags, and pushes.
8
+ ---
9
+
10
+ # Release minitest-cli
11
+
12
+ ## Process
13
+
14
+ ### 1. Determine the next version
15
+
16
+ Fetch all existing tags and the commits since the latest tag:
17
+
18
+ ```bash
19
+ git fetch --tags
20
+ git tag -l 'v*' --sort=-v:refname # find latest tag
21
+ git log <latest-tag>..HEAD --oneline # commits since last release
22
+ ```
23
+
24
+ Apply semver rules to the commit list:
25
+
26
+ | Condition | Bump |
27
+ |-----------|------|
28
+ | Any commit message **breaks** backward compatibility (`BREAKING CHANGE`, `!:`) | **major** (X.0.0) |
29
+ | At least one `feat:` commit | **minor** (0.X.0) |
30
+ | Only `fix:`, `docs:`, `test:`, `chore:`, `refactor:`, `style:`, `perf:`, `ci:` | **patch** (0.0.X) |
31
+
32
+ Present the suggested version and commit summary to the user. Wait for confirmation before proceeding.
33
+
34
+ ### 2. Bump version
35
+
36
+ ```bash
37
+ # Edit pyproject.toml version field
38
+ # Then sync lockfile
39
+ uv sync
40
+ ```
41
+
42
+ ### 3. Commit, tag, push
43
+
44
+ ```bash
45
+ git add pyproject.toml uv.lock
46
+ git commit -m "release: v<VERSION>"
47
+ git tag v<VERSION>
48
+ git push origin HEAD
49
+ git push origin v<VERSION>
50
+ ```
51
+
52
+ The tag push triggers the release workflow (PyPI publish, GitHub Release, Homebrew tap update).
@@ -0,0 +1,61 @@
1
+ # AGENTS.md - minitest-cli
2
+
3
+ ## Commands
4
+
5
+ ```bash
6
+ uv run minitest --help # Show CLI help
7
+ uv run pytest # Run tests
8
+ uv run ruff check . # Lint
9
+ uv run ruff format . # Format
10
+ uv run pyright # Type check
11
+ uv add <package> # Add new dependency (always use uv add, never edit pyproject.toml manually)
12
+ ```
13
+
14
+ ## Project Structure
15
+
16
+ - `src/minitest_cli/` - Main package
17
+ - `commands/` - One Typer sub-app per command group (auth, apps, flow, build, run)
18
+ - `core/`
19
+ - `config.py` - pydantic-settings: MINITEST_API_URL, MINITEST_TOKEN, config dir
20
+ - `app_context.py` - --app flag / MINITEST_APP_ID resolution
21
+ - `auth.py` - Token storage (read/write ~/.minitest/credentials.json)
22
+ - `api/client.py` - httpx async client with auto auth + X-Minitest-Channel header
23
+ - `utils/`
24
+ - `output.py` - --json helpers: stdout=data, stderr=diagnostics
25
+ - `update_check.py` - PyPI version check (cached 24h, non-blocking)
26
+ - `main.py` - Typer app entry point, global flags, command group registration
27
+ - `tests/` - Unit tests
28
+
29
+ ## Coding Guidelines
30
+
31
+ ### Imports
32
+ - ALWAYS use absolute imports (relative imports banned by ruff)
33
+ - ALWAYS place imports at the top of the file
34
+ - Order: standard library → third-party → local imports
35
+
36
+ ### Naming
37
+ - Files/modules: `snake_case.py`
38
+ - Classes: `PascalCase`
39
+ - Functions/variables: `snake_case`
40
+ - Constants: `UPPER_SNAKE_CASE`
41
+ - Test files: `test_*.py`
42
+
43
+ ### Code Style
44
+ - Keep files under 150 lines when possible
45
+ - Use `X | None` syntax (not `Optional[X]`)
46
+ - Use `Annotated[Type, ...]` for Typer parameters
47
+ - Enums: inherit from `str, Enum`
48
+
49
+ ### Output Convention
50
+ - `--json` flag: JSON to stdout, diagnostics to stderr
51
+ - Without `--json`: human-friendly rich tables to stdout, diagnostics to stderr
52
+ - Exit codes: 0=Success, 1=General error, 2=Auth error, 3=Network/API error, 4=Not found
53
+
54
+ ### No Interactive Prompts
55
+ - All input via flags, env vars, or stdin
56
+ - Never prompt for input interactively
57
+
58
+ ### Testing
59
+ - Group tests in classes by feature
60
+ - Name tests: `test_<action>_<scenario>`
61
+ - Always check exit codes and output
@@ -1,11 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minitest-cli
3
- Version: 0.3.0
3
+ Version: 0.4.1
4
4
  Summary: Minitest CLI – command-line interface for the Minitest testing platform
5
- Keywords: testing,cli,minitest,minitap,automation,qa
6
- Author: Minitap Team
5
+ Project-URL: Homepage, https://minitap.ai/
6
+ Project-URL: Source, https://github.com/minitap-ai/minitest-cli
7
+ Project-URL: Bug Tracker, https://github.com/minitap-ai/minitest-cli/issues
8
+ Project-URL: Changelog, https://github.com/minitap-ai/minitest-cli/releases
7
9
  Author-email: Minitap Team <support@minitap.ai>
8
10
  License-Expression: MIT
11
+ Keywords: automation,cli,minitap,minitest,qa,testing
9
12
  Classifier: Development Status :: 3 - Alpha
10
13
  Classifier: Environment :: Console
11
14
  Classifier: Intended Audience :: Developers
@@ -16,20 +19,16 @@ Classifier: Programming Language :: Python :: 3.12
16
19
  Classifier: Programming Language :: Python :: 3.13
17
20
  Classifier: Topic :: Software Development :: Testing
18
21
  Classifier: Typing :: Typed
19
- Requires-Dist: typer>=0.24.1
22
+ Requires-Python: >=3.12
20
23
  Requires-Dist: httpx>=0.28.1
21
- Requires-Dist: pydantic>=2.12.5
22
24
  Requires-Dist: pydantic-settings>=2.13.1
25
+ Requires-Dist: pydantic>=2.12.5
23
26
  Requires-Dist: rich>=14.3.3
24
- Requires-Dist: ruff>=0.15.6 ; extra == 'dev'
25
- Requires-Dist: pyright>=1.1.408 ; extra == 'dev'
26
- Requires-Dist: pytest>=9.0.2 ; extra == 'dev'
27
- Requires-Python: >=3.12
28
- Project-URL: Homepage, https://minitap.ai/
29
- Project-URL: Source, https://github.com/minitap-ai/minitest-cli
30
- Project-URL: Bug Tracker, https://github.com/minitap-ai/minitest-cli/issues
31
- Project-URL: Changelog, https://github.com/minitap-ai/minitest-cli/releases
27
+ Requires-Dist: typer>=0.24.1
32
28
  Provides-Extra: dev
29
+ Requires-Dist: pyright>=1.1.408; extra == 'dev'
30
+ Requires-Dist: pytest>=9.0.2; extra == 'dev'
31
+ Requires-Dist: ruff>=0.15.6; extra == 'dev'
33
32
  Description-Content-Type: text/markdown
34
33
 
35
34
  # minitest-cli
@@ -0,0 +1,90 @@
1
+ # Release Process
2
+
3
+ ## Overview
4
+
5
+ Releases are automated via GitHub Actions. Pushing a version tag triggers the
6
+ full pipeline: build, publish to PyPI, create a GitHub Release, and update the
7
+ Homebrew formula.
8
+
9
+ ## Steps
10
+
11
+ ### 1. Update the version
12
+
13
+ Bump the version in `pyproject.toml`:
14
+
15
+ - `pyproject.toml` — `version = "X.Y.Z"`
16
+
17
+ Then sync the lockfile:
18
+
19
+ ```bash
20
+ uv sync
21
+ ```
22
+
23
+ ### 2. Commit and tag
24
+
25
+ ```bash
26
+ git add pyproject.toml uv.lock
27
+ git commit -m "release: vX.Y.Z"
28
+ git tag vX.Y.Z
29
+ git push origin main
30
+ git push origin vX.Y.Z
31
+ ```
32
+
33
+ ### 3. Automated pipeline
34
+
35
+ The tag push triggers `.github/workflows/release.yml`, which:
36
+
37
+ 1. **Build** — runs `uv build` to produce sdist and wheel
38
+ 2. **Publish to PyPI** — uses trusted publishing (OIDC), no API token needed
39
+ 3. **GitHub Release** — creates a release with auto-generated notes and attaches
40
+ the built artifacts
41
+ 4. **Homebrew update** — dispatches an event to `minitap-ai/homebrew-tap` with
42
+ the new version, sdist URL, and SHA256 hash
43
+
44
+ ### 4. Verify
45
+
46
+ After the pipeline completes:
47
+
48
+ ```bash
49
+ # PyPI
50
+ pip install minitest-cli==X.Y.Z
51
+ minitest --version
52
+
53
+ # uvx
54
+ uvx --from minitest-cli@X.Y.Z minitest --version
55
+
56
+ # Homebrew (after tap update completes)
57
+ brew update
58
+ brew install minitap-ai/tap/minitest-cli
59
+ minitest --version
60
+ ```
61
+
62
+ ## Prerequisites
63
+
64
+ ### PyPI trusted publishing
65
+
66
+ Configure on PyPI at https://pypi.org/manage/project/minitest-cli/settings/publishing/:
67
+
68
+ | Field | Value |
69
+ |-------|-------|
70
+ | Owner | `minitap-ai` |
71
+ | Repository | `minitest-cli` |
72
+ | Workflow | `release.yml` |
73
+ | Environment | `pypi` |
74
+
75
+ ### GitHub repository settings
76
+
77
+ Create a GitHub Actions environment named `pypi` in the repository settings.
78
+
79
+ ### Homebrew tap token
80
+
81
+ A `HOMEBREW_TAP_TOKEN` secret must be set in this repository with a GitHub
82
+ personal access token that has write access to `minitap-ai/homebrew-tap`.
83
+
84
+ ## Version scheme
85
+
86
+ We follow [Semantic Versioning](https://semver.org/):
87
+
88
+ - **Patch** (`0.1.1`) — bug fixes, no API changes
89
+ - **Minor** (`0.2.0`) — new features, backward-compatible
90
+ - **Major** (`1.0.0`) — breaking changes
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "minitest-cli"
3
- version = "0.3.0"
3
+ version = "0.4.1"
4
4
  description = "Minitest CLI – command-line interface for the Minitest testing platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -51,12 +51,11 @@ minitest = "minitest_cli.main:app"
51
51
  "Changelog" = "https://github.com/minitap-ai/minitest-cli/releases"
52
52
 
53
53
  [build-system]
54
- requires = ["uv_build>=0.10.11,<0.11.0"]
55
- build-backend = "uv_build"
54
+ requires = ["hatchling"]
55
+ build-backend = "hatchling.build"
56
56
 
57
- [tool.uv.build-backend]
58
- module-root = "src"
59
- module-name = "minitest_cli"
57
+ [tool.hatch.build.targets.wheel]
58
+ packages = ["src/minitest_cli"]
60
59
 
61
60
  [tool.ruff]
62
61
  exclude = [
@@ -0,0 +1,3 @@
1
+ {
2
+ "exclude": [".venv/**"]
3
+ }
@@ -16,10 +16,10 @@ from minitest_cli.commands.flow_helpers import (
16
16
  handle_response_error,
17
17
  is_json_mode,
18
18
  run_api_call,
19
+ validate_flow_type,
19
20
  )
20
21
  from minitest_cli.core.app_context import resolve_app_id
21
22
  from minitest_cli.core.auth import require_auth
22
- from minitest_cli.models.flow_template import FlowType
23
23
  from minitest_cli.utils.output import output, print_info, print_success, print_table
24
24
 
25
25
  app = typer.Typer(name="flow", help="Testing flow operations.")
@@ -31,7 +31,7 @@ app.command(name="delete")(flow_modify.delete_flow)
31
31
  @app.command(name="create")
32
32
  def create_flow(
33
33
  name: Annotated[str, typer.Option("--name", help="Flow name.")],
34
- flow_type: Annotated[FlowType, typer.Option("--type", help="Flow type.")],
34
+ flow_type: Annotated[str, typer.Option("--type", help="Flow type.")],
35
35
  description: Annotated[
36
36
  str | None, typer.Option("--description", help="Flow description.")
37
37
  ] = None,
@@ -44,7 +44,8 @@ def create_flow(
44
44
  json_mode = is_json_mode()
45
45
  require_auth(settings)
46
46
  app_id = resolve_app_id(settings, get_app_flag())
47
- payload: dict[str, Any] = {"name": name, "type": flow_type.value}
47
+ validate_flow_type(flow_type, settings)
48
+ payload: dict[str, Any] = {"name": name, "type": flow_type}
48
49
  if description is not None:
49
50
  payload["description"] = description
50
51
  if criteria:
@@ -64,9 +65,7 @@ def create_flow(
64
65
 
65
66
  @app.command(name="list")
66
67
  def list_flows(
67
- flow_type: Annotated[
68
- FlowType | None, typer.Option("--type", help="Filter by flow type.")
69
- ] = None,
68
+ flow_type: Annotated[str | None, typer.Option("--type", help="Filter by flow type.")] = None,
70
69
  page: Annotated[int, typer.Option("--page", min=1, help="Page number.")] = 1,
71
70
  page_size: Annotated[
72
71
  int, typer.Option("--page-size", min=1, max=100, help="Items per page.")
@@ -80,12 +79,14 @@ def list_flows(
80
79
  json_mode = is_json_mode()
81
80
  require_auth(settings)
82
81
  app_id = resolve_app_id(settings, get_app_flag())
82
+ if flow_type is not None:
83
+ validate_flow_type(flow_type, settings)
83
84
  if all_flows:
84
85
  page, page_size = 1, 100
85
86
 
86
87
  params: dict[str, Any] = {"page": page, "page_size": page_size}
87
88
  if flow_type is not None:
88
- params["type"] = flow_type.value
89
+ params["type"] = flow_type
89
90
 
90
91
  async def _run() -> Any:
91
92
  async with ApiClient(settings) as client:
@@ -20,6 +20,35 @@ EXIT_NOT_FOUND = 4
20
20
  FLOW_TABLE_HEADERS = ["ID", "Name", "Type", "Description", "Acceptance Criteria"]
21
21
 
22
22
 
23
+ def fetch_flow_types(settings: Settings) -> list[str]:
24
+ """Fetch valid flow types from the API."""
25
+ try:
26
+ resp = httpx.get(
27
+ f"{settings.api_url}/api/v1/flow-types",
28
+ timeout=10,
29
+ )
30
+ except httpx.HTTPError as exc:
31
+ print_error(f"Failed to fetch flow types: {exc}")
32
+ raise typer.Exit(code=EXIT_NETWORK_ERROR) from exc
33
+ if resp.status_code != 200:
34
+ print_error(f"Failed to fetch flow types: HTTP {resp.status_code}")
35
+ raise typer.Exit(code=EXIT_NETWORK_ERROR)
36
+ data = resp.json()
37
+ if not isinstance(data, list) or not data:
38
+ print_error("Invalid response from flow types endpoint.")
39
+ raise typer.Exit(code=EXIT_NETWORK_ERROR)
40
+ return data
41
+
42
+
43
+ def validate_flow_type(value: str, settings: Settings) -> str:
44
+ """Validate a flow type value against types from the API."""
45
+ valid = fetch_flow_types(settings)
46
+ if value not in valid:
47
+ print_error(f"Invalid flow type '{value}'. Valid types: {', '.join(valid)}")
48
+ raise typer.Exit(code=1)
49
+ return value
50
+
51
+
23
52
  def get_settings() -> Settings:
24
53
  return typer.Context.settings # type: ignore[attr-defined]
25
54