reviewloop 0.3.4__tar.gz → 0.3.6__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 (46) hide show
  1. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/skills/reviewloop/SKILL.md +5 -5
  2. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/skills/reviewloop/scripts/review-wait.sh +58 -12
  3. {reviewloop-0.3.4 → reviewloop-0.3.6}/PKG-INFO +1 -1
  4. {reviewloop-0.3.4 → reviewloop-0.3.6}/pyproject.toml +1 -1
  5. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/templates/SKILL.md +5 -5
  6. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/templates/scripts/review-wait.sh +58 -12
  7. {reviewloop-0.3.4 → reviewloop-0.3.6}/uv.lock +33 -24
  8. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.analyze.md +0 -0
  9. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.checklist.md +0 -0
  10. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.clarify.md +0 -0
  11. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.constitution.md +0 -0
  12. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.implement.md +0 -0
  13. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.plan.md +0 -0
  14. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.specify.md +0 -0
  15. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.tasks.md +0 -0
  16. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/commands/speckit.taskstoissues.md +0 -0
  17. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/settings.json +0 -0
  18. {reviewloop-0.3.4 → reviewloop-0.3.6}/.claude/skills/reviewloop/scripts/review-comments.sh +0 -0
  19. {reviewloop-0.3.4 → reviewloop-0.3.6}/.coderabbit.yaml +0 -0
  20. {reviewloop-0.3.4 → reviewloop-0.3.6}/.github/workflows/ci.yml +0 -0
  21. {reviewloop-0.3.4 → reviewloop-0.3.6}/.github/workflows/release.yml +0 -0
  22. {reviewloop-0.3.4 → reviewloop-0.3.6}/.gitignore +0 -0
  23. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/memory/constitution.md +0 -0
  24. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/scripts/bash/check-prerequisites.sh +0 -0
  25. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/scripts/bash/common.sh +0 -0
  26. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/scripts/bash/create-new-feature.sh +0 -0
  27. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/scripts/bash/setup-plan.sh +0 -0
  28. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/scripts/bash/update-agent-context.sh +0 -0
  29. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/templates/agent-file-template.md +0 -0
  30. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/templates/checklist-template.md +0 -0
  31. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/templates/plan-template.md +0 -0
  32. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/templates/spec-template.md +0 -0
  33. {reviewloop-0.3.4 → reviewloop-0.3.6}/.specify/templates/tasks-template.md +0 -0
  34. {reviewloop-0.3.4 → reviewloop-0.3.6}/.vscode/extensions.json +0 -0
  35. {reviewloop-0.3.4 → reviewloop-0.3.6}/CLAUDE.md +0 -0
  36. {reviewloop-0.3.4 → reviewloop-0.3.6}/LICENSE +0 -0
  37. {reviewloop-0.3.4 → reviewloop-0.3.6}/README.md +0 -0
  38. {reviewloop-0.3.4 → reviewloop-0.3.6}/README_PYPI.md +0 -0
  39. {reviewloop-0.3.4 → reviewloop-0.3.6}/assets/review_loop.webp +0 -0
  40. {reviewloop-0.3.4 → reviewloop-0.3.6}/renovate.json +0 -0
  41. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/__init__.py +0 -0
  42. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/cli.py +0 -0
  43. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/init.py +0 -0
  44. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/py.typed +0 -0
  45. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/templates/__init__.py +0 -0
  46. {reviewloop-0.3.4 → reviewloop-0.3.6}/src/reviewloop_cli/templates/scripts/review-comments.sh +0 -0
@@ -30,15 +30,15 @@ Run `.claude/skills/reviewloop/scripts/review-wait.sh` to poll for CI completion
30
30
  - Waits 10s for CI to start (in case called right after push)
31
31
  - Polls every 15s until CI completes
32
32
  - Times out after 10 minutes (configurable with `--timeout=SECONDS`)
33
- - Exits immediately if no CI is in progress
33
+ - Exits immediately if no CI is in progress, early exits if CI fails.
34
34
 
35
35
  ### 3. Fetch and address comments
36
36
 
37
- Once CI completes:
38
-
39
37
  1. Run `.claude/skills/reviewloop/scripts/review-comments.sh` to fetch comments into `.reviews/prComments.md`
40
- 2. Review all comments critically - not all may be valid or apply to our codebase
41
- 3. **ASK BACK** the user for decisions if needed before implementing fixes
38
+ 2. Classify every comment as **Address**, **Dismiss**, or **Needs user input**:
39
+ - **Dismiss** without asking if: factually wrong, misunderstanding, doesn't apply, already handled, or trivial nitpick conflicting with project conventions.
40
+ - **Needs user input** for real decisions: scope questions, design trade-offs, unclear priority. When in doubt, ask.
41
+ 3. **ASK BACK** the user for decisions if needed before implementing fixes. Also present all "Needs user input" comments batched to the user at this point.
42
42
  4. Address valid issues
43
43
  5. For inline threads: resolve via the `gh api graphql` mutation shown in the file (whether addressed or rejected)
44
44
  6. For general review comments: react with thumbs-up via the mutation shown in the file to mark as processed (even if no action was needed)
@@ -6,6 +6,8 @@ INITIAL_DELAY=10
6
6
  POLL_INTERVAL=15
7
7
  DEFAULT_TIMEOUT=600
8
8
  RUNNING_STATES=(pending in_progress queued waiting requested)
9
+ FAILURE_STATES=(failure error cancelled timed_out startup_failure action_required stale)
10
+ LAST_CHECKS_JSON=""
9
11
 
10
12
  die() {
11
13
  echo "Error: $1" >&2
@@ -25,27 +27,33 @@ get_pr_number() {
25
27
  echo "$pr_number"
26
28
  }
27
29
 
28
- # Returns aggregated state: if any check is running, returns that state; otherwise returns the last state
30
+ # Returns aggregated state with priority: failure > running > success
31
+ # Stores raw JSON in LAST_CHECKS_JSON for reuse by get_failed_checks
29
32
  get_ci_state() {
30
33
  local pr_number="$1"
31
- local checks_json states
32
- checks_json=$(gh pr checks "$pr_number" --json name,state 2>&1) || die "Failed to fetch PR checks: $checks_json"
34
+ LAST_CHECKS_JSON=$(gh pr checks "$pr_number" --json name,state 2>&1) || die "Failed to fetch PR checks: $LAST_CHECKS_JSON"
33
35
 
34
- # Get all check states
35
- states=$(echo "$checks_json" | jq -r '[.[] | .state] | if length == 0 then "" else .[] end' 2>/dev/null | tr -d '\r')
36
+ local states
37
+ states=$(echo "$LAST_CHECKS_JSON" | jq -r '[.[] | .state] | if length == 0 then "" else .[] end' 2>/dev/null | tr -d '\r')
36
38
 
37
39
  [[ -z "$states" ]] && return
38
40
 
39
- # If any state is a running state, return it (priority: running > completed)
40
- local state
41
+ local state has_running=false
41
42
  while IFS= read -r state; do
42
- if is_running_state "$state"; then
43
- echo "$state"
43
+ if is_failure_state "$state"; then
44
+ echo "FAILED"
44
45
  return
45
46
  fi
47
+ if is_running_state "$state"; then
48
+ has_running=true
49
+ fi
46
50
  done <<< "$states"
47
51
 
48
- # No running states found, return the last state
52
+ if [[ "$has_running" == "true" ]]; then
53
+ echo "running"
54
+ return
55
+ fi
56
+
49
57
  echo "$states" | tail -n1
50
58
  }
51
59
 
@@ -58,6 +66,33 @@ is_running_state() {
58
66
  return 1
59
67
  }
60
68
 
69
+ is_failure_state() {
70
+ local state="${1,,}"
71
+ local s
72
+ for s in "${FAILURE_STATES[@]}"; do
73
+ [[ "$state" == "$s" ]] && return 0
74
+ done
75
+ return 1
76
+ }
77
+
78
+ failure_states_jq_filter() {
79
+ local filter="["
80
+ local first=true
81
+ for s in "${FAILURE_STATES[@]}"; do
82
+ [[ "$first" == "true" ]] && first=false || filter+=","
83
+ filter+="\"$s\""
84
+ done
85
+ filter+="]"
86
+ echo "$filter"
87
+ }
88
+
89
+ print_failed_checks() {
90
+ local filter
91
+ filter=$(failure_states_jq_filter)
92
+ echo "CI failed. Failed checks:"
93
+ echo "$LAST_CHECKS_JSON" | jq -r --argjson states "$filter" '.[] | select((.state | ascii_downcase) as $s | $states | index($s)) | " - " + .name' 2>/dev/null | tr -d '\r'
94
+ }
95
+
61
96
  parse_args() {
62
97
  TIMEOUT=$DEFAULT_TIMEOUT
63
98
  local arg value
@@ -85,7 +120,12 @@ main() {
85
120
 
86
121
  state=$(get_ci_state "$pr_number")
87
122
 
88
- if [[ -z "$state" ]] || ! is_running_state "$state"; then
123
+ if [[ "$state" == "FAILED" ]]; then
124
+ print_failed_checks
125
+ exit 2
126
+ fi
127
+
128
+ if [[ -z "$state" ]] || [[ "$state" != "running" ]]; then
89
129
  echo "No CI checks in progress"
90
130
  exit 0
91
131
  fi
@@ -105,7 +145,13 @@ main() {
105
145
 
106
146
  state=$(get_ci_state "$pr_number")
107
147
 
108
- if [[ -z "$state" ]] || ! is_running_state "$state"; then
148
+ if [[ "$state" == "FAILED" ]]; then
149
+ echo ""
150
+ print_failed_checks
151
+ exit 2
152
+ fi
153
+
154
+ if [[ -z "$state" ]] || [[ "$state" != "running" ]]; then
109
155
  echo ""
110
156
  echo "CI completed${state:+ ($state)}"
111
157
  exit 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reviewloop
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: Autonomous review loop - automates the review-fix-push cycle
5
5
  Project-URL: Homepage, https://github.com/fabianboth/autonomous-review-loop
6
6
  Project-URL: Repository, https://github.com/fabianboth/autonomous-review-loop.git
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "reviewloop"
7
- version = "0.3.4"
7
+ version = "0.3.6"
8
8
  description = "Autonomous review loop - automates the review-fix-push cycle"
9
9
  readme = "README_PYPI.md"
10
10
  license = "MIT"
@@ -30,15 +30,15 @@ Run `.claude/skills/reviewloop/scripts/review-wait.sh` to poll for CI completion
30
30
  - Waits 10s for CI to start (in case called right after push)
31
31
  - Polls every 15s until CI completes
32
32
  - Times out after 10 minutes (configurable with `--timeout=SECONDS`)
33
- - Exits immediately if no CI is in progress
33
+ - Exits immediately if no CI is in progress, early exits if CI fails.
34
34
 
35
35
  ### 3. Fetch and address comments
36
36
 
37
- Once CI completes:
38
-
39
37
  1. Run `.claude/skills/reviewloop/scripts/review-comments.sh` to fetch comments into `.reviews/prComments.md`
40
- 2. Review all comments critically - not all may be valid or apply to our codebase
41
- 3. **ASK BACK** the user for decisions if needed before implementing fixes
38
+ 2. Classify every comment as **Address**, **Dismiss**, or **Needs user input**:
39
+ - **Dismiss** without asking if: factually wrong, misunderstanding, doesn't apply, already handled, or trivial nitpick conflicting with project conventions.
40
+ - **Needs user input** for real decisions: scope questions, design trade-offs, unclear priority. When in doubt, ask.
41
+ 3. **ASK BACK** the user for decisions if needed before implementing fixes. Also present all "Needs user input" comments batched to the user at this point.
42
42
  4. Address valid issues
43
43
  5. For inline threads: resolve via the `gh api graphql` mutation shown in the file (whether addressed or rejected)
44
44
  6. For general review comments: react with thumbs-up via the mutation shown in the file to mark as processed (even if no action was needed)
@@ -6,6 +6,8 @@ INITIAL_DELAY=10
6
6
  POLL_INTERVAL=15
7
7
  DEFAULT_TIMEOUT=600
8
8
  RUNNING_STATES=(pending in_progress queued waiting requested)
9
+ FAILURE_STATES=(failure error cancelled timed_out startup_failure action_required stale)
10
+ LAST_CHECKS_JSON=""
9
11
 
10
12
  die() {
11
13
  echo "Error: $1" >&2
@@ -25,27 +27,33 @@ get_pr_number() {
25
27
  echo "$pr_number"
26
28
  }
27
29
 
28
- # Returns aggregated state: if any check is running, returns that state; otherwise returns the last state
30
+ # Returns aggregated state with priority: failure > running > success
31
+ # Stores raw JSON in LAST_CHECKS_JSON for reuse by get_failed_checks
29
32
  get_ci_state() {
30
33
  local pr_number="$1"
31
- local checks_json states
32
- checks_json=$(gh pr checks "$pr_number" --json name,state 2>&1) || die "Failed to fetch PR checks: $checks_json"
34
+ LAST_CHECKS_JSON=$(gh pr checks "$pr_number" --json name,state 2>&1) || die "Failed to fetch PR checks: $LAST_CHECKS_JSON"
33
35
 
34
- # Get all check states
35
- states=$(echo "$checks_json" | jq -r '[.[] | .state] | if length == 0 then "" else .[] end' 2>/dev/null | tr -d '\r')
36
+ local states
37
+ states=$(echo "$LAST_CHECKS_JSON" | jq -r '[.[] | .state] | if length == 0 then "" else .[] end' 2>/dev/null | tr -d '\r')
36
38
 
37
39
  [[ -z "$states" ]] && return
38
40
 
39
- # If any state is a running state, return it (priority: running > completed)
40
- local state
41
+ local state has_running=false
41
42
  while IFS= read -r state; do
42
- if is_running_state "$state"; then
43
- echo "$state"
43
+ if is_failure_state "$state"; then
44
+ echo "FAILED"
44
45
  return
45
46
  fi
47
+ if is_running_state "$state"; then
48
+ has_running=true
49
+ fi
46
50
  done <<< "$states"
47
51
 
48
- # No running states found, return the last state
52
+ if [[ "$has_running" == "true" ]]; then
53
+ echo "running"
54
+ return
55
+ fi
56
+
49
57
  echo "$states" | tail -n1
50
58
  }
51
59
 
@@ -58,6 +66,33 @@ is_running_state() {
58
66
  return 1
59
67
  }
60
68
 
69
+ is_failure_state() {
70
+ local state="${1,,}"
71
+ local s
72
+ for s in "${FAILURE_STATES[@]}"; do
73
+ [[ "$state" == "$s" ]] && return 0
74
+ done
75
+ return 1
76
+ }
77
+
78
+ failure_states_jq_filter() {
79
+ local filter="["
80
+ local first=true
81
+ for s in "${FAILURE_STATES[@]}"; do
82
+ [[ "$first" == "true" ]] && first=false || filter+=","
83
+ filter+="\"$s\""
84
+ done
85
+ filter+="]"
86
+ echo "$filter"
87
+ }
88
+
89
+ print_failed_checks() {
90
+ local filter
91
+ filter=$(failure_states_jq_filter)
92
+ echo "CI failed. Failed checks:"
93
+ echo "$LAST_CHECKS_JSON" | jq -r --argjson states "$filter" '.[] | select((.state | ascii_downcase) as $s | $states | index($s)) | " - " + .name' 2>/dev/null | tr -d '\r'
94
+ }
95
+
61
96
  parse_args() {
62
97
  TIMEOUT=$DEFAULT_TIMEOUT
63
98
  local arg value
@@ -85,7 +120,12 @@ main() {
85
120
 
86
121
  state=$(get_ci_state "$pr_number")
87
122
 
88
- if [[ -z "$state" ]] || ! is_running_state "$state"; then
123
+ if [[ "$state" == "FAILED" ]]; then
124
+ print_failed_checks
125
+ exit 2
126
+ fi
127
+
128
+ if [[ -z "$state" ]] || [[ "$state" != "running" ]]; then
89
129
  echo "No CI checks in progress"
90
130
  exit 0
91
131
  fi
@@ -105,7 +145,13 @@ main() {
105
145
 
106
146
  state=$(get_ci_state "$pr_number")
107
147
 
108
- if [[ -z "$state" ]] || ! is_running_state "$state"; then
148
+ if [[ "$state" == "FAILED" ]]; then
149
+ echo ""
150
+ print_failed_checks
151
+ exit 2
152
+ fi
153
+
154
+ if [[ -z "$state" ]] || [[ "$state" != "running" ]]; then
109
155
  echo ""
110
156
  echo "CI completed${state:+ ($state)}"
111
157
  exit 0
@@ -2,6 +2,15 @@ version = 1
2
2
  revision = 3
3
3
  requires-python = ">=3.12"
4
4
 
5
+ [[package]]
6
+ name = "annotated-doc"
7
+ version = "0.0.4"
8
+ source = { registry = "https://pypi.org/simple" }
9
+ sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
10
+ wheels = [
11
+ { url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
12
+ ]
13
+
5
14
  [[package]]
6
15
  name = "click"
7
16
  version = "8.3.1"
@@ -86,7 +95,7 @@ wheels = [
86
95
 
87
96
  [[package]]
88
97
  name = "reviewloop"
89
- version = "0.3.4"
98
+ version = "0.3.6"
90
99
  source = { editable = "." }
91
100
  dependencies = [
92
101
  { name = "readchar" },
@@ -128,27 +137,27 @@ wheels = [
128
137
 
129
138
  [[package]]
130
139
  name = "ruff"
131
- version = "0.15.0"
140
+ version = "0.15.1"
132
141
  source = { registry = "https://pypi.org/simple" }
133
- sdist = { url = "https://files.pythonhosted.org/packages/c8/39/5cee96809fbca590abea6b46c6d1c586b49663d1d2830a751cc8fc42c666/ruff-0.15.0.tar.gz", hash = "sha256:6bdea47cdbea30d40f8f8d7d69c0854ba7c15420ec75a26f463290949d7f7e9a", size = 4524893, upload-time = "2026-02-03T17:53:35.357Z" }
142
+ sdist = { url = "https://files.pythonhosted.org/packages/04/dc/4e6ac71b511b141cf626357a3946679abeba4cf67bc7cc5a17920f31e10d/ruff-0.15.1.tar.gz", hash = "sha256:c590fe13fb57c97141ae975c03a1aedb3d3156030cabd740d6ff0b0d601e203f", size = 4540855, upload-time = "2026-02-12T23:09:09.998Z" }
134
143
  wheels = [
135
- { url = "https://files.pythonhosted.org/packages/bc/88/3fd1b0aa4b6330d6aaa63a285bc96c9f71970351579152d231ed90914586/ruff-0.15.0-py3-none-linux_armv6l.whl", hash = "sha256:aac4ebaa612a82b23d45964586f24ae9bc23ca101919f5590bdb368d74ad5455", size = 10354332, upload-time = "2026-02-03T17:52:54.892Z" },
136
- { url = "https://files.pythonhosted.org/packages/72/f6/62e173fbb7eb75cc29fe2576a1e20f0a46f671a2587b5f604bfb0eaf5f6f/ruff-0.15.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:dcd4be7cc75cfbbca24a98d04d0b9b36a270d0833241f776b788d59f4142b14d", size = 10767189, upload-time = "2026-02-03T17:53:19.778Z" },
137
- { url = "https://files.pythonhosted.org/packages/99/e4/968ae17b676d1d2ff101d56dc69cf333e3a4c985e1ec23803df84fc7bf9e/ruff-0.15.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:d747e3319b2bce179c7c1eaad3d884dc0a199b5f4d5187620530adf9105268ce", size = 10075384, upload-time = "2026-02-03T17:53:29.241Z" },
138
- { url = "https://files.pythonhosted.org/packages/a2/bf/9843c6044ab9e20af879c751487e61333ca79a2c8c3058b15722386b8cae/ruff-0.15.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:650bd9c56ae03102c51a5e4b554d74d825ff3abe4db22b90fd32d816c2e90621", size = 10481363, upload-time = "2026-02-03T17:52:43.332Z" },
139
- { url = "https://files.pythonhosted.org/packages/55/d9/4ada5ccf4cd1f532db1c8d44b6f664f2208d3d93acbeec18f82315e15193/ruff-0.15.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a6664b7eac559e3048223a2da77769c2f92b43a6dfd4720cef42654299a599c9", size = 10187736, upload-time = "2026-02-03T17:53:00.522Z" },
140
- { url = "https://files.pythonhosted.org/packages/86/e2/f25eaecd446af7bb132af0a1d5b135a62971a41f5366ff41d06d25e77a91/ruff-0.15.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6f811f97b0f092b35320d1556f3353bf238763420ade5d9e62ebd2b73f2ff179", size = 10968415, upload-time = "2026-02-03T17:53:15.705Z" },
141
- { url = "https://files.pythonhosted.org/packages/e7/dc/f06a8558d06333bf79b497d29a50c3a673d9251214e0d7ec78f90b30aa79/ruff-0.15.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:761ec0a66680fab6454236635a39abaf14198818c8cdf691e036f4bc0f406b2d", size = 11809643, upload-time = "2026-02-03T17:53:23.031Z" },
142
- { url = "https://files.pythonhosted.org/packages/dd/45/0ece8db2c474ad7df13af3a6d50f76e22a09d078af63078f005057ca59eb/ruff-0.15.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:940f11c2604d317e797b289f4f9f3fa5555ffe4fb574b55ed006c3d9b6f0eb78", size = 11234787, upload-time = "2026-02-03T17:52:46.432Z" },
143
- { url = "https://files.pythonhosted.org/packages/8a/d9/0e3a81467a120fd265658d127db648e4d3acfe3e4f6f5d4ea79fac47e587/ruff-0.15.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bcbca3d40558789126da91d7ef9a7c87772ee107033db7191edefa34e2c7f1b4", size = 11112797, upload-time = "2026-02-03T17:52:49.274Z" },
144
- { url = "https://files.pythonhosted.org/packages/b2/cb/8c0b3b0c692683f8ff31351dfb6241047fa873a4481a76df4335a8bff716/ruff-0.15.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9a121a96db1d75fa3eb39c4539e607f628920dd72ff1f7c5ee4f1b768ac62d6e", size = 11033133, upload-time = "2026-02-03T17:53:33.105Z" },
145
- { url = "https://files.pythonhosted.org/packages/f8/5e/23b87370cf0f9081a8c89a753e69a4e8778805b8802ccfe175cc410e50b9/ruff-0.15.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:5298d518e493061f2eabd4abd067c7e4fb89e2f63291c94332e35631c07c3662", size = 10442646, upload-time = "2026-02-03T17:53:06.278Z" },
146
- { url = "https://files.pythonhosted.org/packages/e1/9a/3c94de5ce642830167e6d00b5c75aacd73e6347b4c7fc6828699b150a5ee/ruff-0.15.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:afb6e603d6375ff0d6b0cee563fa21ab570fd15e65c852cb24922cef25050cf1", size = 10195750, upload-time = "2026-02-03T17:53:26.084Z" },
147
- { url = "https://files.pythonhosted.org/packages/30/15/e396325080d600b436acc970848d69df9c13977942fb62bb8722d729bee8/ruff-0.15.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:77e515f6b15f828b94dc17d2b4ace334c9ddb7d9468c54b2f9ed2b9c1593ef16", size = 10676120, upload-time = "2026-02-03T17:53:09.363Z" },
148
- { url = "https://files.pythonhosted.org/packages/8d/c9/229a23d52a2983de1ad0fb0ee37d36e0257e6f28bfd6b498ee2c76361874/ruff-0.15.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:6f6e80850a01eb13b3e42ee0ebdf6e4497151b48c35051aab51c101266d187a3", size = 11201636, upload-time = "2026-02-03T17:52:57.281Z" },
149
- { url = "https://files.pythonhosted.org/packages/6f/b0/69adf22f4e24f3677208adb715c578266842e6e6a3cc77483f48dd999ede/ruff-0.15.0-py3-none-win32.whl", hash = "sha256:238a717ef803e501b6d51e0bdd0d2c6e8513fe9eec14002445134d3907cd46c3", size = 10465945, upload-time = "2026-02-03T17:53:12.591Z" },
150
- { url = "https://files.pythonhosted.org/packages/51/ad/f813b6e2c97e9b4598be25e94a9147b9af7e60523b0cb5d94d307c15229d/ruff-0.15.0-py3-none-win_amd64.whl", hash = "sha256:dd5e4d3301dc01de614da3cdffc33d4b1b96fb89e45721f1598e5532ccf78b18", size = 11564657, upload-time = "2026-02-03T17:52:51.893Z" },
151
- { url = "https://files.pythonhosted.org/packages/f6/b0/2d823f6e77ebe560f4e397d078487e8d52c1516b331e3521bc75db4272ca/ruff-0.15.0-py3-none-win_arm64.whl", hash = "sha256:c480d632cc0ca3f0727acac8b7d053542d9e114a462a145d0b00e7cd658c515a", size = 10865753, upload-time = "2026-02-03T17:53:03.014Z" },
144
+ { url = "https://files.pythonhosted.org/packages/23/bf/e6e4324238c17f9d9120a9d60aa99a7daaa21204c07fcd84e2ef03bb5fd1/ruff-0.15.1-py3-none-linux_armv6l.whl", hash = "sha256:b101ed7cf4615bda6ffe65bdb59f964e9f4a0d3f85cbf0e54f0ab76d7b90228a", size = 10367819, upload-time = "2026-02-12T23:09:03.598Z" },
145
+ { url = "https://files.pythonhosted.org/packages/b3/ea/c8f89d32e7912269d38c58f3649e453ac32c528f93bb7f4219258be2e7ed/ruff-0.15.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:939c995e9277e63ea632cc8d3fae17aa758526f49a9a850d2e7e758bfef46602", size = 10798618, upload-time = "2026-02-12T23:09:22.928Z" },
146
+ { url = "https://files.pythonhosted.org/packages/5e/0f/1d0d88bc862624247d82c20c10d4c0f6bb2f346559d8af281674cf327f15/ruff-0.15.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:1d83466455fdefe60b8d9c8df81d3c1bbb2115cede53549d3b522ce2bc703899", size = 10148518, upload-time = "2026-02-12T23:08:58.339Z" },
147
+ { url = "https://files.pythonhosted.org/packages/f5/c8/291c49cefaa4a9248e986256df2ade7add79388fe179e0691be06fae6f37/ruff-0.15.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9457e3c3291024866222b96108ab2d8265b477e5b1534c7ddb1810904858d16", size = 10518811, upload-time = "2026-02-12T23:09:31.865Z" },
148
+ { url = "https://files.pythonhosted.org/packages/c3/1a/f5707440e5ae43ffa5365cac8bbb91e9665f4a883f560893829cf16a606b/ruff-0.15.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:92c92b003e9d4f7fbd33b1867bb15a1b785b1735069108dfc23821ba045b29bc", size = 10196169, upload-time = "2026-02-12T23:09:17.306Z" },
149
+ { url = "https://files.pythonhosted.org/packages/2a/ff/26ddc8c4da04c8fd3ee65a89c9fb99eaa5c30394269d424461467be2271f/ruff-0.15.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:1fe5c41ab43e3a06778844c586251eb5a510f67125427625f9eb2b9526535779", size = 10990491, upload-time = "2026-02-12T23:09:25.503Z" },
150
+ { url = "https://files.pythonhosted.org/packages/fc/00/50920cb385b89413f7cdb4bb9bc8fc59c1b0f30028d8bccc294189a54955/ruff-0.15.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:66a6dd6df4d80dc382c6484f8ce1bcceb55c32e9f27a8b94c32f6c7331bf14fb", size = 11843280, upload-time = "2026-02-12T23:09:19.88Z" },
151
+ { url = "https://files.pythonhosted.org/packages/5d/6d/2f5cad8380caf5632a15460c323ae326f1e1a2b5b90a6ee7519017a017ca/ruff-0.15.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6a4a42cbb8af0bda9bcd7606b064d7c0bc311a88d141d02f78920be6acb5aa83", size = 11274336, upload-time = "2026-02-12T23:09:14.907Z" },
152
+ { url = "https://files.pythonhosted.org/packages/a3/1d/5f56cae1d6c40b8a318513599b35ea4b075d7dc1cd1d04449578c29d1d75/ruff-0.15.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ab064052c31dddada35079901592dfba2e05f5b1e43af3954aafcbc1096a5b2", size = 11137288, upload-time = "2026-02-12T23:09:07.475Z" },
153
+ { url = "https://files.pythonhosted.org/packages/cd/20/6f8d7d8f768c93b0382b33b9306b3b999918816da46537d5a61635514635/ruff-0.15.1-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:5631c940fe9fe91f817a4c2ea4e81f47bee3ca4aa646134a24374f3c19ad9454", size = 11070681, upload-time = "2026-02-12T23:08:55.43Z" },
154
+ { url = "https://files.pythonhosted.org/packages/9a/67/d640ac76069f64cdea59dba02af2e00b1fa30e2103c7f8d049c0cff4cafd/ruff-0.15.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68138a4ba184b4691ccdc39f7795c66b3c68160c586519e7e8444cf5a53e1b4c", size = 10486401, upload-time = "2026-02-12T23:09:27.927Z" },
155
+ { url = "https://files.pythonhosted.org/packages/65/3d/e1429f64a3ff89297497916b88c32a5cc88eeca7e9c787072d0e7f1d3e1e/ruff-0.15.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:518f9af03bfc33c03bdb4cb63fabc935341bb7f54af500f92ac309ecfbba6330", size = 10197452, upload-time = "2026-02-12T23:09:12.147Z" },
156
+ { url = "https://files.pythonhosted.org/packages/78/83/e2c3bade17dad63bf1e1c2ffaf11490603b760be149e1419b07049b36ef2/ruff-0.15.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:da79f4d6a826caaea95de0237a67e33b81e6ec2e25fc7e1993a4015dffca7c61", size = 10693900, upload-time = "2026-02-12T23:09:34.418Z" },
157
+ { url = "https://files.pythonhosted.org/packages/a1/27/fdc0e11a813e6338e0706e8b39bb7a1d61ea5b36873b351acee7e524a72a/ruff-0.15.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:3dd86dccb83cd7d4dcfac303ffc277e6048600dfc22e38158afa208e8bf94a1f", size = 11227302, upload-time = "2026-02-12T23:09:36.536Z" },
158
+ { url = "https://files.pythonhosted.org/packages/f6/58/ac864a75067dcbd3b95be5ab4eb2b601d7fbc3d3d736a27e391a4f92a5c1/ruff-0.15.1-py3-none-win32.whl", hash = "sha256:660975d9cb49b5d5278b12b03bb9951d554543a90b74ed5d366b20e2c57c2098", size = 10462555, upload-time = "2026-02-12T23:09:29.899Z" },
159
+ { url = "https://files.pythonhosted.org/packages/e0/5e/d4ccc8a27ecdb78116feac4935dfc39d1304536f4296168f91ed3ec00cd2/ruff-0.15.1-py3-none-win_amd64.whl", hash = "sha256:c820fef9dd5d4172a6570e5721704a96c6679b80cf7be41659ed439653f62336", size = 11599956, upload-time = "2026-02-12T23:09:01.157Z" },
160
+ { url = "https://files.pythonhosted.org/packages/2a/07/5bda6a85b220c64c65686bc85bd0bbb23b29c62b3a9f9433fa55f17cda93/ruff-0.15.1-py3-none-win_arm64.whl", hash = "sha256:5ff7d5f0f88567850f45081fac8f4ec212be8d0b963e385c3f7d0d2eb4899416", size = 10874604, upload-time = "2026-02-12T23:09:05.515Z" },
152
161
  ]
153
162
 
154
163
  [[package]]
@@ -162,17 +171,17 @@ wheels = [
162
171
 
163
172
  [[package]]
164
173
  name = "typer"
165
- version = "0.21.1"
174
+ version = "0.23.1"
166
175
  source = { registry = "https://pypi.org/simple" }
167
176
  dependencies = [
177
+ { name = "annotated-doc" },
168
178
  { name = "click" },
169
179
  { name = "rich" },
170
180
  { name = "shellingham" },
171
- { name = "typing-extensions" },
172
181
  ]
173
- sdist = { url = "https://files.pythonhosted.org/packages/36/bf/8825b5929afd84d0dabd606c67cd57b8388cb3ec385f7ef19c5cc2202069/typer-0.21.1.tar.gz", hash = "sha256:ea835607cd752343b6b2b7ce676893e5a0324082268b48f27aa058bdb7d2145d", size = 110371, upload-time = "2026-01-06T11:21:10.989Z" }
182
+ sdist = { url = "https://files.pythonhosted.org/packages/fd/07/b822e1b307d40e263e8253d2384cf98c51aa2368cc7ba9a07e523a1d964b/typer-0.23.1.tar.gz", hash = "sha256:2070374e4d31c83e7b61362fd859aa683576432fd5b026b060ad6b4cd3b86134", size = 120047, upload-time = "2026-02-13T10:04:30.984Z" }
174
183
  wheels = [
175
- { url = "https://files.pythonhosted.org/packages/a0/1d/d9257dd49ff2ca23ea5f132edf1281a0c4f9de8a762b9ae399b670a59235/typer-0.21.1-py3-none-any.whl", hash = "sha256:7985e89081c636b88d172c2ee0cfe33c253160994d47bdfdc302defd7d1f1d01", size = 47381, upload-time = "2026-01-06T11:21:09.824Z" },
184
+ { url = "https://files.pythonhosted.org/packages/d5/91/9b286ab899c008c2cb05e8be99814807e7fbbd33f0c0c960470826e5ac82/typer-0.23.1-py3-none-any.whl", hash = "sha256:3291ad0d3c701cbf522012faccfbb29352ff16ad262db2139e6b01f15781f14e", size = 56813, upload-time = "2026-02-13T10:04:32.008Z" },
176
185
  ]
177
186
 
178
187
  [[package]]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes