minitest-cli 0.4.4__tar.gz → 0.5.0__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 (61) hide show
  1. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/.github/workflows/ci.yml +33 -0
  2. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/PKG-INFO +49 -33
  3. minitest_cli-0.5.0/README.md +141 -0
  4. minitest_cli-0.5.0/install.ps1 +116 -0
  5. minitest_cli-0.5.0/install.sh +144 -0
  6. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/pyproject.toml +1 -1
  7. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/auth.py +52 -1
  8. minitest_cli-0.5.0/src/minitest_cli/commands/maintenance_check.py +52 -0
  9. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/oauth.py +27 -28
  10. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/token_exchange.py +38 -0
  11. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/main.py +2 -1
  12. minitest_cli-0.5.0/src/minitest_cli/models/maintenance_check.py +12 -0
  13. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/uv.lock +1 -1
  14. minitest_cli-0.4.4/README.md +0 -125
  15. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/.env.example +0 -0
  16. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/.github/workflows/release.yml +0 -0
  17. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/.gitignore +0 -0
  18. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/.opencode/skill/release/SKILL.md +0 -0
  19. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/AGENTS.md +0 -0
  20. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/RELEASE.md +0 -0
  21. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/pyrightconfig.json +0 -0
  22. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/__init__.py +0 -0
  23. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/api/__init__.py +0 -0
  24. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/api/client.py +0 -0
  25. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/assets/__init__.py +0 -0
  26. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/assets/callback.html +0 -0
  27. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/__init__.py +0 -0
  28. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/apps.py +0 -0
  29. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/build.py +0 -0
  30. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/build_helpers.py +0 -0
  31. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/flow.py +0 -0
  32. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/flow_helpers.py +0 -0
  33. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/flow_modify.py +0 -0
  34. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/run.py +0 -0
  35. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/run_display.py +0 -0
  36. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/run_helpers.py +0 -0
  37. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/commands/skill.py +0 -0
  38. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/__init__.py +0 -0
  39. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/app_context.py +0 -0
  40. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/auth.py +0 -0
  41. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/config.py +0 -0
  42. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/core/credentials.py +0 -0
  43. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/models/__init__.py +0 -0
  44. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/models/app.py +0 -0
  45. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/models/base.py +0 -0
  46. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/models/build.py +0 -0
  47. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/models/flow_run.py +0 -0
  48. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/models/flow_template.py +0 -0
  49. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/utils/__init__.py +0 -0
  50. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/utils/output.py +0 -0
  51. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/src/minitest_cli/utils/update_check.py +0 -0
  52. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/__init__.py +0 -0
  53. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_apps_commands.py +0 -0
  54. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_auth.py +0 -0
  55. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_auth_commands.py +0 -0
  56. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_build_commands.py +0 -0
  57. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_code_quality.py +0 -0
  58. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_flow_commands.py +0 -0
  59. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_run_commands.py +0 -0
  60. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_skill_command.py +0 -0
  61. {minitest_cli-0.4.4 → minitest_cli-0.5.0}/tests/test_version.py +0 -0
@@ -63,3 +63,36 @@ jobs:
63
63
 
64
64
  - name: Run tests
65
65
  run: uv run pytest
66
+
67
+ install-script:
68
+ strategy:
69
+ matrix:
70
+ os: [ubuntu-latest, macos-latest]
71
+ runs-on: ${{ matrix.os }}
72
+ steps:
73
+ - name: Clone the repository
74
+ uses: actions/checkout@v5
75
+
76
+ - name: Run install.sh
77
+ run: |
78
+ bash install.sh
79
+ echo "$HOME/.local/bin" >> "$GITHUB_PATH"
80
+
81
+ - name: Verify minitest is usable
82
+ run: minitest --help
83
+
84
+ install-script-windows:
85
+ runs-on: windows-latest
86
+ steps:
87
+ - name: Clone the repository
88
+ uses: actions/checkout@v5
89
+
90
+ - name: Run install.ps1
91
+ shell: pwsh
92
+ run: |
93
+ .\install.ps1
94
+ "$env:USERPROFILE\.local\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
95
+
96
+ - name: Verify minitest is usable
97
+ shell: pwsh
98
+ run: minitest --help
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: minitest-cli
3
- Version: 0.4.4
3
+ Version: 0.5.0
4
4
  Summary: Minitest CLI – command-line interface for the Minitest testing platform
5
5
  Project-URL: Homepage, https://minitap.ai/
6
6
  Project-URL: Source, https://github.com/minitap-ai/minitest-cli
@@ -37,27 +37,43 @@ Command-line interface for the Minitest testing platform.
37
37
 
38
38
  ## Installation
39
39
 
40
- ### pip (recommended)
40
+ ### One-liner (recommended)
41
+
42
+ **macOS / Linux:**
41
43
 
42
44
  ```bash
43
- pip install minitest-cli
45
+ curl -fsSL https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.sh | bash
46
+ ```
47
+
48
+ **Windows (PowerShell):**
49
+
50
+ ```powershell
51
+ powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.ps1 | iex"
44
52
  ```
45
53
 
46
- ### uvx (zero-install)
54
+ Both scripts use `uv` if available, or install it automatically.
55
+
56
+ ### Other methods
47
57
 
48
- Run without installing:
58
+ **uv** (all platforms):
49
59
 
50
60
  ```bash
51
- uvx --from minitest-cli minitest --help
61
+ uv tool install minitest-cli
52
62
  ```
53
63
 
54
- ### Homebrew
64
+ **Homebrew** (macOS):
55
65
 
56
66
  ```bash
57
67
  brew install minitap-ai/tap/minitest-cli
58
68
  ```
59
69
 
60
- ### From source
70
+ **uvx** (zero-install, all platforms):
71
+
72
+ ```bash
73
+ uvx --from minitest-cli minitest --help
74
+ ```
75
+
76
+ **From source:**
61
77
 
62
78
  ```bash
63
79
  git clone https://github.com/minitap-ai/minitest-cli.git
@@ -81,40 +97,40 @@ minitest run --app <app-id>
81
97
 
82
98
  ## Configuration
83
99
 
84
- | Environment Variable | Description | Required |
85
- |---------------------|-------------|----------|
86
- | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
87
- | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
88
- | `MINITEST_API_URL` | API base URL | No (defaults to production) |
100
+ | Environment Variable | Description | Required |
101
+ | -------------------- | ------------------------ | ---------------------------------- |
102
+ | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
103
+ | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
104
+ | `MINITEST_API_URL` | API base URL | No (defaults to production) |
89
105
 
90
106
  ## Global Flags
91
107
 
92
- | Flag | Description |
93
- |------|-------------|
94
- | `--json` | Output JSON to stdout (diagnostics go to stderr) |
95
- | `--app <id-or-name>` | Target app for commands that require one |
96
- | `--version` | Show CLI version |
97
- | `--help` | Show help |
108
+ | Flag | Description |
109
+ | -------------------- | ------------------------------------------------ |
110
+ | `--json` | Output JSON to stdout (diagnostics go to stderr) |
111
+ | `--app <id-or-name>` | Target app for commands that require one |
112
+ | `--version` | Show CLI version |
113
+ | `--help` | Show help |
98
114
 
99
115
  ## Commands
100
116
 
101
- | Command | Description |
102
- |---------|-------------|
103
- | `minitest auth` | Authentication management |
104
- | `minitest apps` | App management |
105
- | `minitest flow` | Testing flow operations |
106
- | `minitest build` | Build management |
107
- | `minitest run` | Test execution |
117
+ | Command | Description |
118
+ | ---------------- | ------------------------- |
119
+ | `minitest auth` | Authentication management |
120
+ | `minitest apps` | App management |
121
+ | `minitest flow` | Testing flow operations |
122
+ | `minitest build` | Build management |
123
+ | `minitest run` | Test execution |
108
124
 
109
125
  ## Exit Codes
110
126
 
111
- | Code | Meaning |
112
- |------|---------|
113
- | 0 | Success |
114
- | 1 | General error |
115
- | 2 | Authentication error |
116
- | 3 | Network / API error |
117
- | 4 | Resource not found |
127
+ | Code | Meaning |
128
+ | ---- | -------------------- |
129
+ | 0 | Success |
130
+ | 1 | General error |
131
+ | 2 | Authentication error |
132
+ | 3 | Network / API error |
133
+ | 4 | Resource not found |
118
134
 
119
135
  ## Using the Dev Environment
120
136
 
@@ -0,0 +1,141 @@
1
+ # minitest-cli
2
+
3
+ Command-line interface for the Minitest testing platform.
4
+
5
+ ## Installation
6
+
7
+ ### One-liner (recommended)
8
+
9
+ **macOS / Linux:**
10
+
11
+ ```bash
12
+ curl -fsSL https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.sh | bash
13
+ ```
14
+
15
+ **Windows (PowerShell):**
16
+
17
+ ```powershell
18
+ powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.ps1 | iex"
19
+ ```
20
+
21
+ Both scripts use `uv` if available, or install it automatically.
22
+
23
+ ### Other methods
24
+
25
+ **uv** (all platforms):
26
+
27
+ ```bash
28
+ uv tool install minitest-cli
29
+ ```
30
+
31
+ **Homebrew** (macOS):
32
+
33
+ ```bash
34
+ brew install minitap-ai/tap/minitest-cli
35
+ ```
36
+
37
+ **uvx** (zero-install, all platforms):
38
+
39
+ ```bash
40
+ uvx --from minitest-cli minitest --help
41
+ ```
42
+
43
+ **From source:**
44
+
45
+ ```bash
46
+ git clone https://github.com/minitap-ai/minitest-cli.git
47
+ cd minitest-cli
48
+ uv sync
49
+ uv run minitest --help
50
+ ```
51
+
52
+ ## Quick Start
53
+
54
+ ```bash
55
+ # Authenticate
56
+ minitest auth login
57
+
58
+ # List your apps
59
+ minitest apps list
60
+
61
+ # Run tests
62
+ minitest run --app <app-id>
63
+ ```
64
+
65
+ ## Configuration
66
+
67
+ | Environment Variable | Description | Required |
68
+ | -------------------- | ------------------------ | ---------------------------------- |
69
+ | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
70
+ | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
71
+ | `MINITEST_API_URL` | API base URL | No (defaults to production) |
72
+
73
+ ## Global Flags
74
+
75
+ | Flag | Description |
76
+ | -------------------- | ------------------------------------------------ |
77
+ | `--json` | Output JSON to stdout (diagnostics go to stderr) |
78
+ | `--app <id-or-name>` | Target app for commands that require one |
79
+ | `--version` | Show CLI version |
80
+ | `--help` | Show help |
81
+
82
+ ## Commands
83
+
84
+ | Command | Description |
85
+ | ---------------- | ------------------------- |
86
+ | `minitest auth` | Authentication management |
87
+ | `minitest apps` | App management |
88
+ | `minitest flow` | Testing flow operations |
89
+ | `minitest build` | Build management |
90
+ | `minitest run` | Test execution |
91
+
92
+ ## Exit Codes
93
+
94
+ | Code | Meaning |
95
+ | ---- | -------------------- |
96
+ | 0 | Success |
97
+ | 1 | General error |
98
+ | 2 | Authentication error |
99
+ | 3 | Network / API error |
100
+ | 4 | Resource not found |
101
+
102
+ ## Using the Dev Environment
103
+
104
+ To point the CLI at the **dev** environment instead of production, set these environment variables when running `minitest`:
105
+
106
+ ```bash
107
+ MINITEST_SUPABASE_URL=https://qrezuucghnmfvaxghqsv.supabase.co \
108
+ MINITEST_SUPABASE_PUBLISHABLE_KEY=sb_publishable_4JRhoCm8pa5PbII0dhS09A_jhpkQhmy \
109
+ MINITEST_API_URL=https://testing-service.dev.minitap.ai \
110
+ minitest auth login
111
+ ```
112
+
113
+ This authenticates against the dev environment and stores a dev-specific auth token. After logging in, keep the same variables set for all subsequent commands:
114
+
115
+ ```bash
116
+ MINITEST_SUPABASE_URL=https://qrezuucghnmfvaxghqsv.supabase.co \
117
+ MINITEST_SUPABASE_PUBLISHABLE_KEY=sb_publishable_4JRhoCm8pa5PbII0dhS09A_jhpkQhmy \
118
+ MINITEST_API_URL=https://testing-service.dev.minitap.ai \
119
+ minitest apps list
120
+ ```
121
+
122
+ > **Tip:** You can `export` these variables in your shell session (or add them to a `.envrc` / `.env` file) to avoid repeating them on every invocation.
123
+
124
+ ## Development
125
+
126
+ ```bash
127
+ # Install dependencies
128
+ uv sync --dev
129
+
130
+ # Run linter
131
+ uv run ruff check .
132
+
133
+ # Run formatter
134
+ uv run ruff format .
135
+
136
+ # Run type checker
137
+ uv run pyright
138
+
139
+ # Run tests
140
+ uv run pytest
141
+ ```
@@ -0,0 +1,116 @@
1
+ # install.ps1 - Install minitest-cli via uv (installs uv if not installed)
2
+ #
3
+ # Usage:
4
+ # powershell -ExecutionPolicy ByPass -c "irm https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.ps1 | iex"
5
+ #
6
+
7
+ $ErrorActionPreference = "Stop"
8
+ $Package = "minitest-cli"
9
+ $InstalledVia = ""
10
+
11
+ function Write-Info { param([string]$Message) Write-Host "==> $Message" -ForegroundColor Blue }
12
+ function Write-Ok { param([string]$Message) Write-Host "==> $Message" -ForegroundColor Green }
13
+ function Write-Warn { param([string]$Message) Write-Host "Warning: $Message" -ForegroundColor Yellow }
14
+ function Write-Err { param([string]$Message) Write-Host "Error: $Message" -ForegroundColor Red }
15
+
16
+ function Get-UvToolBinDir {
17
+ $dir = & uv tool dir --bin 2>$null
18
+ if ($dir) { return $dir }
19
+ return "$env:USERPROFILE\.local\bin"
20
+ }
21
+
22
+ function Add-PathEntryIfMissing {
23
+ param([string]$Entry)
24
+ $normalizedEntry = $Entry.TrimEnd('\')
25
+ $pathEntries = ($env:PATH -split ';' | Where-Object { $_ }) | ForEach-Object { $_.TrimEnd('\') }
26
+ if (-not ($pathEntries | Where-Object { $_ -ieq $normalizedEntry })) {
27
+ $env:PATH = "$Entry;$env:PATH"
28
+ }
29
+ }
30
+
31
+ function Install-WithUv {
32
+ Write-Info "Installing $Package with uv..."
33
+ try {
34
+ & uv tool install $Package --force 2>&1 | Write-Host
35
+ if ($LASTEXITCODE -eq 0) {
36
+ $script:InstalledVia = "uv"
37
+ return $true
38
+ }
39
+ Write-Warn "uv tool install failed with exit code $LASTEXITCODE."
40
+ return $false
41
+ } catch {
42
+ Write-Warn "uv tool install failed."
43
+ return $false
44
+ }
45
+ }
46
+
47
+ function Install-Uv {
48
+ Write-Info "Installing uv package manager..."
49
+ try {
50
+ & ([scriptblock]::Create((Invoke-RestMethod "https://astral.sh/uv/install.ps1")))
51
+ # Refresh PATH so uv is available in this session
52
+ $uvPath = "$env:USERPROFILE\.local\bin"
53
+ Add-PathEntryIfMissing -Entry $uvPath
54
+ # Also check cargo bin (uv may install there on some setups)
55
+ $cargoPath = "$env:USERPROFILE\.cargo\bin"
56
+ Add-PathEntryIfMissing -Entry $cargoPath
57
+ return $true
58
+ } catch {
59
+ Write-Warn "Failed to install uv."
60
+ return $false
61
+ }
62
+ }
63
+
64
+ # -------------------------------------------------------------------
65
+ # Main: use uv if available, otherwise bootstrap it
66
+ # -------------------------------------------------------------------
67
+
68
+ # 1. uv already installed? Use it
69
+ if (Get-Command uv -ErrorAction SilentlyContinue) {
70
+ Install-WithUv | Out-Null
71
+ }
72
+
73
+ # 2. No uv - bootstrap it
74
+ if (-not $InstalledVia) {
75
+ if (Install-Uv) {
76
+ Install-WithUv | Out-Null
77
+ }
78
+ }
79
+
80
+ if (-not $InstalledVia) {
81
+ Write-Err "Installation failed."
82
+ Write-Err "Please install manually:"
83
+ Write-Err " powershell -ExecutionPolicy ByPass -c `"irm https://astral.sh/uv/install.ps1 | iex`""
84
+ Write-Err " uv tool install $Package"
85
+ exit 1
86
+ }
87
+
88
+ # -------------------------------------------------------------------
89
+ # Verify installation
90
+ # -------------------------------------------------------------------
91
+ if (Get-Command minitest -ErrorAction SilentlyContinue) {
92
+ Write-Ok "minitest-cli installed successfully!"
93
+ minitest --version
94
+ Write-Host ""
95
+ Write-Info "Next steps:"
96
+ Write-Host " minitest auth login # authenticate"
97
+ Write-Host " minitest apps list # list your apps"
98
+ Write-Host " minitest --help # see all commands"
99
+ } else {
100
+ # Check uv tool bin directory
101
+ $toolBinDir = Get-UvToolBinDir
102
+ $uvBin = Join-Path $toolBinDir "minitest.exe"
103
+ if (Test-Path $uvBin) {
104
+ Write-Ok "minitest-cli installed successfully!"
105
+ & $uvBin --version
106
+ Write-Host ""
107
+ Write-Warn "minitest is not on your PATH in this session."
108
+ Write-Warn "Close and reopen your terminal, then run:"
109
+ Write-Host " minitest --help"
110
+ } else {
111
+ Write-Err "Installation reported success, but 'minitest' binary was not found."
112
+ Write-Err "Please try reinstalling:"
113
+ Write-Err " uv tool install $Package"
114
+ exit 1
115
+ }
116
+ }
@@ -0,0 +1,144 @@
1
+ #!/usr/bin/env bash
2
+ # install.sh — Install minitest-cli via uv (installs uv if not installed)
3
+ #
4
+ # Usage:
5
+ # curl -fsSL https://raw.githubusercontent.com/minitap-ai/minitest-cli/main/install.sh | bash
6
+ #
7
+ set -euo pipefail
8
+
9
+ PACKAGE="minitest-cli"
10
+ INSTALLED_VIA=""
11
+
12
+ info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
13
+ ok() { printf '\033[1;32m==>\033[0m %s\n' "$*"; }
14
+ warn() { printf '\033[1;33mWarning:\033[0m %s\n' "$*"; }
15
+ error() { printf '\033[1;31mError:\033[0m %s\n' "$*" >&2; }
16
+
17
+ # Resolve the directory where uv installs tool executables.
18
+ # Respects UV_TOOL_BIN_DIR / XDG_BIN_HOME; falls back to ~/.local/bin.
19
+ get_uv_bin_dir() {
20
+ uv tool dir --bin 2>/dev/null || printf '%s\n' "${UV_TOOL_BIN_DIR:-${XDG_BIN_HOME:-${HOME}/.local/bin}}"
21
+ }
22
+
23
+ # Resolve the target shell rc file based on $SHELL.
24
+ get_shell_rc() {
25
+ case "${SHELL:-}" in
26
+ */zsh) printf '%s\n' "${HOME}/.zshrc" ;;
27
+ */bash) printf '%s\n' "${HOME}/.bashrc" ;;
28
+ *) printf '%s\n' "${HOME}/.profile" ;;
29
+ esac
30
+ }
31
+
32
+ # -------------------------------------------------------------------
33
+ # ensure_on_path — make sure a directory is on PATH now + in shell rc
34
+ # -------------------------------------------------------------------
35
+ ensure_on_path() {
36
+ local dir="$1"
37
+ case ":${PATH}:" in
38
+ *":${dir}:"*) return 0 ;; # already on PATH
39
+ esac
40
+ export PATH="${dir}:${PATH}"
41
+
42
+ # Patch the user's shell rc file so new terminals pick it up
43
+ local rc
44
+ rc="$(get_shell_rc)"
45
+
46
+ local line="export PATH=\"${dir}:\$PATH\""
47
+ local dir_relative="${dir/#${HOME}/\$HOME}"
48
+ if grep -qF "${dir}" "${rc}" 2>/dev/null \
49
+ || grep -qF "${dir_relative}" "${rc}" 2>/dev/null \
50
+ || grep -q "local/bin/env" "${rc}" 2>/dev/null; then
51
+ : # already has a PATH entry for this dir
52
+ else
53
+ info "Adding ${dir} to PATH in ${rc}"
54
+ printf '\n# Added by minitest-cli installer\n%s\n' "${line}" >> "${rc}"
55
+ fi
56
+ }
57
+
58
+ # -------------------------------------------------------------------
59
+ # install_with_uv — use uv tool install
60
+ # -------------------------------------------------------------------
61
+ install_with_uv() {
62
+ info "Installing ${PACKAGE} with uv…"
63
+ if uv tool install "${PACKAGE}" --force 2>&1; then
64
+ INSTALLED_VIA="uv"
65
+ local bin_dir
66
+ bin_dir="$(get_uv_bin_dir)"
67
+ ensure_on_path "${bin_dir}"
68
+ return 0
69
+ fi
70
+ warn "uv tool install failed."
71
+ return 1
72
+ }
73
+
74
+ # -------------------------------------------------------------------
75
+ # bootstrap_uv — install uv itself, then use it to install minitest
76
+ # -------------------------------------------------------------------
77
+ bootstrap_uv() {
78
+ info "Installing uv package manager…"
79
+ if curl -LsSf https://astral.sh/uv/install.sh | sh 2>&1; then
80
+ # uv installer defaults to ~/.local/bin; add it so uv is available
81
+ ensure_on_path "${HOME}/.local/bin"
82
+ install_with_uv
83
+ return $?
84
+ fi
85
+ warn "Failed to install uv."
86
+ return 1
87
+ }
88
+
89
+ # -------------------------------------------------------------------
90
+ # Main: use uv if available, otherwise bootstrap it
91
+ # -------------------------------------------------------------------
92
+
93
+ # 1. uv already installed? Use it
94
+ if command -v uv &>/dev/null; then
95
+ install_with_uv || true
96
+ fi
97
+
98
+ # 2. No uv — bootstrap it
99
+ if [[ -z "${INSTALLED_VIA}" ]]; then
100
+ bootstrap_uv || true
101
+ fi
102
+
103
+ if [[ -z "${INSTALLED_VIA}" ]]; then
104
+ error "Installation failed."
105
+ error "Please install manually:"
106
+ error " curl -LsSf https://astral.sh/uv/install.sh | sh"
107
+ error " uv tool install ${PACKAGE}"
108
+ exit 1
109
+ fi
110
+
111
+ # -------------------------------------------------------------------
112
+ # Verify installation
113
+ # -------------------------------------------------------------------
114
+ if command -v minitest &>/dev/null; then
115
+ ok "minitest-cli installed successfully!"
116
+ minitest --version
117
+ echo ""
118
+ info "Next steps:"
119
+ echo " minitest auth login # authenticate"
120
+ echo " minitest apps list # list your apps"
121
+ echo " minitest --help # see all commands"
122
+ else
123
+ # minitest binary exists but shell doesn't see it yet (current session)
124
+ uv_bin_dir="$(get_uv_bin_dir)"
125
+ if [[ -x "${uv_bin_dir}/minitest" ]]; then
126
+ ok "minitest-cli installed successfully!"
127
+ "${uv_bin_dir}/minitest" --version
128
+ echo ""
129
+ warn "Run this to use minitest in your current terminal:"
130
+ echo " export PATH=\"${uv_bin_dir}:\$PATH\""
131
+ echo ""
132
+ info "It will work automatically in new terminals."
133
+ echo ""
134
+ info "Next steps:"
135
+ echo " minitest auth login # authenticate"
136
+ echo " minitest apps list # list your apps"
137
+ echo " minitest --help # see all commands"
138
+ else
139
+ error "Installation reported success, but 'minitest' binary was not found."
140
+ error "Please try reinstalling:"
141
+ error " uv tool install ${PACKAGE}"
142
+ exit 1
143
+ fi
144
+ fi
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "minitest-cli"
3
- version = "0.4.4"
3
+ version = "0.5.0"
4
4
  description = "Minitest CLI – command-line interface for the Minitest testing platform"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -1,5 +1,7 @@
1
1
  """Authentication commands: login, logout, status."""
2
2
 
3
+ import json
4
+ import subprocess
3
5
  from datetime import UTC, datetime
4
6
 
5
7
  import typer
@@ -13,10 +15,37 @@ from minitest_cli.core.auth import (
13
15
  oauth_pkce_login,
14
16
  )
15
17
  from minitest_cli.core.config import Settings
16
- from minitest_cli.utils.output import output, print_error, print_success
18
+ from minitest_cli.utils.output import output, print_error, print_info, print_success
17
19
 
18
20
  app = typer.Typer(name="auth", help="Authentication management.")
19
21
 
22
+ SKILL_NAME = "minitest-cli"
23
+ SKILL_INSTALL_CMD = "npx skills add minitap-ai/agent-skills --skill minitest-cli"
24
+
25
+
26
+ def _is_skill_installed() -> bool:
27
+ """Check if the minitest-cli skill is installed via ``npx skills ls``.
28
+
29
+ Queries both project-level and global scopes so the detection stays in
30
+ sync with whatever directories the ``skills`` CLI manages.
31
+ """
32
+ for flags in (["--json"], ["--json", "-g"]):
33
+ try:
34
+ result = subprocess.run(
35
+ ["npx", "skills", "ls", *flags],
36
+ capture_output=True,
37
+ text=True,
38
+ timeout=30,
39
+ )
40
+ if result.returncode == 0:
41
+ skills = json.loads(result.stdout)
42
+ if any(s.get("name") == SKILL_NAME for s in skills):
43
+ return True
44
+ except (subprocess.TimeoutExpired, json.JSONDecodeError, FileNotFoundError, OSError):
45
+ # npx not available or unexpected output – fall through
46
+ pass
47
+ return False
48
+
20
49
 
21
50
  def _get_settings() -> Settings:
22
51
  """Retrieve settings stored by the main callback."""
@@ -40,6 +69,28 @@ def login() -> None:
40
69
  creds = oauth_pkce_login(settings)
41
70
  print_success(f"Authenticated as {creds.email}")
42
71
 
72
+ # Check if the minitest-cli agent skill is installed
73
+ if not _is_skill_installed():
74
+ print_info("")
75
+ print_info("💡 The minitest-cli agent skill is not installed in this project.")
76
+ print_info(" Your AI agent needs it to know how to use minitest.")
77
+ print_info("")
78
+ try:
79
+ answer = input(" Install it now? [Y/n] ").strip().lower()
80
+ except (EOFError, KeyboardInterrupt):
81
+ answer = "n"
82
+ print() # newline after ^C / ^D
83
+ if answer in ("", "y", "yes"):
84
+ print_info("")
85
+ print_info(f" Running: {SKILL_INSTALL_CMD}")
86
+ print_info("")
87
+ subprocess.run(SKILL_INSTALL_CMD.split(), check=False)
88
+ else:
89
+ print_info("")
90
+ print_info(" You can install it later with:")
91
+ print_info(f" {SKILL_INSTALL_CMD}")
92
+ print_info("")
93
+
43
94
 
44
95
  @app.command()
45
96
  def logout() -> None:
@@ -0,0 +1,52 @@
1
+ """Maintenance check command - acknowledge test freshness for a commit."""
2
+
3
+ from typing import Annotated
4
+
5
+ import typer
6
+
7
+ from minitest_cli.api.client import ApiClient
8
+ from minitest_cli.commands.run_helpers import (
9
+ handle_response_error,
10
+ resolve_app,
11
+ run_api_call,
12
+ )
13
+ from minitest_cli.models.maintenance_check import MaintenanceCheckResponse
14
+ from minitest_cli.utils.output import print_json, print_success
15
+
16
+ app = typer.Typer(name="maintenance-check", help="Test maintenance acknowledgment.")
17
+
18
+
19
+ @app.callback(invoke_without_command=True)
20
+ def maintenance_check(
21
+ ctx: typer.Context,
22
+ commit_sha: Annotated[str, typer.Argument(help="Git commit SHA to acknowledge.")],
23
+ ) -> None:
24
+ """Acknowledge that tests have been reviewed for a commit."""
25
+ if ctx.invoked_subcommand is not None:
26
+ return
27
+
28
+ settings, app_id, json_mode = resolve_app()
29
+
30
+ async def _acknowledge() -> MaintenanceCheckResponse:
31
+ async with ApiClient(settings) as client:
32
+ body = {"commitSha": commit_sha}
33
+ resp = await client.post(
34
+ f"/api/v1/apps/{app_id}/maintenance-check",
35
+ json=body,
36
+ )
37
+ handle_response_error(resp, resource="Maintenance check")
38
+ return MaintenanceCheckResponse.model_validate(resp.json())
39
+
40
+ result = run_api_call(_acknowledge())
41
+
42
+ if json_mode:
43
+ print_json(
44
+ {
45
+ "id": result.id,
46
+ "app_id": result.app_id,
47
+ "commit_sha": result.commit_sha,
48
+ "created_at": result.created_at,
49
+ }
50
+ )
51
+ else:
52
+ print_success(f"Tests acknowledged for commit {commit_sha[:8]}")
@@ -1,4 +1,4 @@
1
- """OAuth PKCE login flow and token refresh."""
1
+ """OAuth PKCE login flow via Supabase OAuth2 server, and token refresh."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -20,9 +20,8 @@ from minitest_cli.core.config import Settings
20
20
  from minitest_cli.core.credentials import Credentials
21
21
  from minitest_cli.core.token_exchange import (
22
22
  auth_error,
23
- get_apikey_header,
24
23
  parse_and_save_token_response,
25
- require_supabase_url,
24
+ register_oauth_client,
26
25
  )
27
26
 
28
27
  _ASSETS = importlib.resources.files("minitest_cli.assets")
@@ -59,31 +58,24 @@ def refresh_token(settings: Settings, creds: Credentials) -> Credentials | None:
59
58
 
60
59
 
61
60
  def oauth_pkce_login(settings: Settings) -> Credentials:
62
- """Run the full OAuth PKCE login flow.
61
+ """Run the full OAuth PKCE login flow via Supabase's OAuth2 server.
63
62
 
64
63
  Steps:
65
- 1. Generate code verifier + challenge
66
- 2. Start local callback server
67
- 3. Open browser to Supabase authorize endpoint
68
- 4. Wait for callback with auth code
69
- 5. Exchange code for tokens
70
- 6. Save and return credentials
64
+ 1. Start local callback server
65
+ 2. Dynamically register an OAuth2 client with Supabase
66
+ 3. Generate PKCE code verifier + challenge
67
+ 4. Open browser to Supabase authorize endpoint (shows hosted sign-in page)
68
+ 5. Wait for callback with authorization code
69
+ 6. Exchange code + verifier for tokens at Supabase token endpoint
70
+ 7. Save and return credentials
71
71
  """
72
- supabase_url = require_supabase_url(settings)
72
+ supabase_url = settings.supabase_url.rstrip("/")
73
73
 
74
74
  # PKCE challenge: base64url(sha256(verifier)) without padding
75
75
  code_verifier = secrets.token_urlsafe(64)
76
76
  digest = hashlib.sha256(code_verifier.encode()).digest()
77
77
  code_challenge = base64.urlsafe_b64encode(digest).rstrip(b"=").decode()
78
78
 
79
- # CSRF protection: We do NOT generate our own state parameter.
80
- # Supabase internally uses 'state' as a FlowState UUID (database key) to track
81
- # the OAuth flow context (redirect_to, PKCE params, etc.). If we override it,
82
- # Supabase can't find the FlowState record and falls back to Site URL.
83
- # We still have CSRF protection via:
84
- # 1. Supabase's FlowState UUID (validated on callback)
85
- # 2. PKCE (code_challenge + code_verifier) per OAuth 2.1
86
-
87
79
  # Start callback server
88
80
  auth_code_holder: dict[str, str | None] = {"code": None, "error": None}
89
81
  ready_event = Event()
@@ -122,17 +114,21 @@ def oauth_pkce_login(settings: Settings) -> Credentials:
122
114
  port = server.server_address[1]
123
115
  redirect_uri = f"http://127.0.0.1:{port}/callback"
124
116
 
125
- # Build authorize URL (no custom state - see comment above)
117
+ # Register OAuth client with Supabase (dynamic client registration)
118
+ client_id = register_oauth_client(supabase_url, redirect_uri)
119
+
120
+ # Build authorize URL — Supabase's OAuth2 server shows its hosted sign-in page
126
121
  authorize_params = urllib.parse.urlencode(
127
122
  {
128
- "provider": "google",
123
+ "client_id": client_id,
124
+ "redirect_uri": redirect_uri,
129
125
  "response_type": "code",
130
126
  "code_challenge": code_challenge,
131
127
  "code_challenge_method": "S256",
132
- "redirect_to": redirect_uri,
128
+ "scope": "openid email profile",
133
129
  }
134
130
  )
135
- authorize_url = f"{supabase_url}/auth/v1/authorize?{authorize_params}"
131
+ authorize_url = f"{supabase_url}/auth/v1/oauth/authorize?{authorize_params}"
136
132
 
137
133
  print("Opening browser for authentication...", file=sys.stderr) # noqa: T201
138
134
  print(f"If the browser doesn't open, visit:\n{authorize_url}", file=sys.stderr) # noqa: T201
@@ -156,16 +152,19 @@ def oauth_pkce_login(settings: Settings) -> Credentials:
156
152
  if not auth_code:
157
153
  auth_error("No authorization code received.")
158
154
 
159
- # Exchange code for tokens (Supabase uses grant_type=pkce for PKCE flow)
155
+ # Exchange code for tokens at Supabase's OAuth2 token endpoint
160
156
  assert auth_code is not None # for type narrowing
161
157
  try:
162
158
  token_response = httpx.post(
163
- f"{supabase_url}/auth/v1/token?grant_type=pkce",
164
- json={
165
- "auth_code": auth_code,
159
+ f"{supabase_url}/auth/v1/oauth/token",
160
+ data={
161
+ "grant_type": "authorization_code",
162
+ "code": auth_code,
163
+ "redirect_uri": redirect_uri,
164
+ "client_id": client_id,
166
165
  "code_verifier": code_verifier,
167
166
  },
168
- headers={"Content-Type": "application/json", "apikey": get_apikey_header(settings)},
167
+ headers={"Content-Type": "application/x-www-form-urlencoded"},
169
168
  timeout=15.0,
170
169
  )
171
170
  except httpx.HTTPError as exc:
@@ -6,6 +6,8 @@ import sys
6
6
  import time
7
7
  from typing import Any, NoReturn
8
8
 
9
+ import httpx
10
+
9
11
  from minitest_cli.core.config import Settings
10
12
  from minitest_cli.core.credentials import Credentials, save_credentials
11
13
 
@@ -51,6 +53,42 @@ def parse_and_save_token_response(settings: Settings, data: dict[str, Any]) -> C
51
53
  return None
52
54
 
53
55
 
56
+ def register_oauth_client(supabase_url: str, redirect_uri: str) -> str:
57
+ """Dynamically register an OAuth2 client with Supabase and return the client_id."""
58
+ register_url = f"{supabase_url}/auth/v1/oauth/clients/register"
59
+ try:
60
+ resp = httpx.post(
61
+ register_url,
62
+ json={
63
+ "client_name": "minitest-cli",
64
+ "redirect_uris": [redirect_uri],
65
+ "grant_types": ["authorization_code", "refresh_token"],
66
+ "response_types": ["code"],
67
+ "token_endpoint_auth_method": "none",
68
+ },
69
+ headers={"Content-Type": "application/json"},
70
+ timeout=15.0,
71
+ )
72
+ except httpx.HTTPError as exc:
73
+ auth_error(f"Failed to register OAuth client: {exc}")
74
+
75
+ if resp.status_code not in (200, 201):
76
+ auth_error(f"OAuth client registration failed: {resp.text}")
77
+
78
+ try:
79
+ data = resp.json()
80
+ except ValueError:
81
+ auth_error(
82
+ f"OAuth client registration returned invalid response "
83
+ f"(HTTP {resp.status_code}): {resp.text}"
84
+ )
85
+
86
+ client_id: str | None = data.get("client_id")
87
+ if not client_id:
88
+ auth_error("OAuth client registration returned no client_id.")
89
+ return client_id # type: ignore[return-value]
90
+
91
+
54
92
  def auth_error(message: str) -> NoReturn:
55
93
  """Print auth error to stderr and exit with code 2."""
56
94
  print(f"Error: {message}", file=sys.stderr) # noqa: T201
@@ -5,7 +5,7 @@ from typing import Annotated
5
5
  import typer
6
6
 
7
7
  from minitest_cli import __version__
8
- from minitest_cli.commands import apps, auth, build, flow, run, skill
8
+ from minitest_cli.commands import apps, auth, build, flow, maintenance_check, run, skill
9
9
  from minitest_cli.core.config import get_settings
10
10
  from minitest_cli.utils.update_check import check_for_updates
11
11
 
@@ -21,6 +21,7 @@ app.add_typer(auth.app)
21
21
  app.add_typer(apps.app)
22
22
  app.add_typer(flow.app)
23
23
  app.add_typer(build.app)
24
+ app.add_typer(maintenance_check.app)
24
25
  app.add_typer(run.app)
25
26
  app.add_typer(skill.app)
26
27
 
@@ -0,0 +1,12 @@
1
+ """Models for maintenance check responses."""
2
+
3
+ from minitest_cli.models.base import CamelModel
4
+
5
+
6
+ class MaintenanceCheckResponse(CamelModel):
7
+ """Response from creating a maintenance check acknowledgment."""
8
+
9
+ id: str
10
+ app_id: str
11
+ commit_sha: str
12
+ created_at: str
@@ -141,7 +141,7 @@ wheels = [
141
141
 
142
142
  [[package]]
143
143
  name = "minitest-cli"
144
- version = "0.4.3"
144
+ version = "0.5.0"
145
145
  source = { editable = "." }
146
146
  dependencies = [
147
147
  { name = "httpx" },
@@ -1,125 +0,0 @@
1
- # minitest-cli
2
-
3
- Command-line interface for the Minitest testing platform.
4
-
5
- ## Installation
6
-
7
- ### pip (recommended)
8
-
9
- ```bash
10
- pip install minitest-cli
11
- ```
12
-
13
- ### uvx (zero-install)
14
-
15
- Run without installing:
16
-
17
- ```bash
18
- uvx --from minitest-cli minitest --help
19
- ```
20
-
21
- ### Homebrew
22
-
23
- ```bash
24
- brew install minitap-ai/tap/minitest-cli
25
- ```
26
-
27
- ### From source
28
-
29
- ```bash
30
- git clone https://github.com/minitap-ai/minitest-cli.git
31
- cd minitest-cli
32
- uv sync
33
- uv run minitest --help
34
- ```
35
-
36
- ## Quick Start
37
-
38
- ```bash
39
- # Authenticate
40
- minitest auth login
41
-
42
- # List your apps
43
- minitest apps list
44
-
45
- # Run tests
46
- minitest run --app <app-id>
47
- ```
48
-
49
- ## Configuration
50
-
51
- | Environment Variable | Description | Required |
52
- |---------------------|-------------|----------|
53
- | `MINITEST_TOKEN` | API authentication token | Yes (or use `minitest auth login`) |
54
- | `MINITEST_APP_ID` | Default app ID | No (can use `--app` flag) |
55
- | `MINITEST_API_URL` | API base URL | No (defaults to production) |
56
-
57
- ## Global Flags
58
-
59
- | Flag | Description |
60
- |------|-------------|
61
- | `--json` | Output JSON to stdout (diagnostics go to stderr) |
62
- | `--app <id-or-name>` | Target app for commands that require one |
63
- | `--version` | Show CLI version |
64
- | `--help` | Show help |
65
-
66
- ## Commands
67
-
68
- | Command | Description |
69
- |---------|-------------|
70
- | `minitest auth` | Authentication management |
71
- | `minitest apps` | App management |
72
- | `minitest flow` | Testing flow operations |
73
- | `minitest build` | Build management |
74
- | `minitest run` | Test execution |
75
-
76
- ## Exit Codes
77
-
78
- | Code | Meaning |
79
- |------|---------|
80
- | 0 | Success |
81
- | 1 | General error |
82
- | 2 | Authentication error |
83
- | 3 | Network / API error |
84
- | 4 | Resource not found |
85
-
86
- ## Using the Dev Environment
87
-
88
- To point the CLI at the **dev** environment instead of production, set these environment variables when running `minitest`:
89
-
90
- ```bash
91
- MINITEST_SUPABASE_URL=https://qrezuucghnmfvaxghqsv.supabase.co \
92
- MINITEST_SUPABASE_PUBLISHABLE_KEY=sb_publishable_4JRhoCm8pa5PbII0dhS09A_jhpkQhmy \
93
- MINITEST_API_URL=https://testing-service.dev.minitap.ai \
94
- minitest auth login
95
- ```
96
-
97
- This authenticates against the dev environment and stores a dev-specific auth token. After logging in, keep the same variables set for all subsequent commands:
98
-
99
- ```bash
100
- MINITEST_SUPABASE_URL=https://qrezuucghnmfvaxghqsv.supabase.co \
101
- MINITEST_SUPABASE_PUBLISHABLE_KEY=sb_publishable_4JRhoCm8pa5PbII0dhS09A_jhpkQhmy \
102
- MINITEST_API_URL=https://testing-service.dev.minitap.ai \
103
- minitest apps list
104
- ```
105
-
106
- > **Tip:** You can `export` these variables in your shell session (or add them to a `.envrc` / `.env` file) to avoid repeating them on every invocation.
107
-
108
- ## Development
109
-
110
- ```bash
111
- # Install dependencies
112
- uv sync --dev
113
-
114
- # Run linter
115
- uv run ruff check .
116
-
117
- # Run formatter
118
- uv run ruff format .
119
-
120
- # Run type checker
121
- uv run pyright
122
-
123
- # Run tests
124
- uv run pytest
125
- ```
File without changes
File without changes
File without changes
File without changes