refactorai-cli 0.5.0__tar.gz → 0.5.2__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 (50) hide show
  1. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/PKG-INFO +1 -1
  2. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/pyproject.toml +1 -1
  3. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/client.py +5 -4
  5. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/run_cmds.py +34 -12
  6. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/watch_cmds.py +9 -0
  7. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/watch_state.py +46 -7
  8. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli.egg-info/PKG-INFO +1 -1
  9. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/README.md +0 -0
  10. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/auth.py +0 -0
  11. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/cloud_rr.py +0 -0
  12. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/__init__.py +0 -0
  13. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/account_cmds.py +0 -0
  14. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/auth_cmds.py +0 -0
  15. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/branch_cmds.py +0 -0
  16. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/cloud_cmds.py +0 -0
  17. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/engine_cmds.py +0 -0
  18. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/hook_cmds.py +0 -0
  19. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/model_cmds.py +0 -0
  20. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/pre_push_cmds.py +0 -0
  21. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/request_cmds.py +0 -0
  22. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/rules_cmds.py +0 -0
  23. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/runtime_cmds.py +0 -0
  24. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  25. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/setup_cmds.py +0 -0
  26. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commands/toolchains_cmds.py +0 -0
  27. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commit_queue.py +0 -0
  28. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/commit_telemetry.py +0 -0
  29. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/control_plane.py +0 -0
  30. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/credentials.py +0 -0
  31. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/dotenv_loader.py +0 -0
  32. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/git_scope.py +0 -0
  33. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/local_constitution.py +0 -0
  34. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/local_engine_runtime.py +0 -0
  35. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/local_paths.py +0 -0
  36. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/main.py +0 -0
  37. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/model_policy.py +0 -0
  38. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/pre_push_gate.py +0 -0
  39. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/refactor_branch_store.py +0 -0
  40. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/review_runner.py +0 -0
  41. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/runtime_manager.py +0 -0
  42. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/settings.py +0 -0
  43. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/setup_flow.py +0 -0
  44. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli/watch_supervisor.py +0 -0
  45. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  46. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  47. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli.egg-info/entry_points.txt +0 -0
  48. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli.egg-info/requires.txt +0 -0
  49. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/refactorai_cli.egg-info/top_level.txt +0 -0
  50. {refactorai_cli-0.5.0 → refactorai_cli-0.5.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "refactorai-cli"
3
- version = "0.5.0"
3
+ version = "0.5.2"
4
4
  description = "Local-first CLI for the refactor platform"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -5,4 +5,4 @@ the shared `refactor_core` pipeline from a project folder while staying
5
5
  authenticated to the hosted platform via a developer key.
6
6
  """
7
7
 
8
- __version__ = "0.5.0"
8
+ __version__ = "0.5.2"
@@ -48,7 +48,6 @@ class PlatformClient:
48
48
  def create_project(
49
49
  self,
50
50
  developer_key: str,
51
- account_slug: str,
52
51
  name: str,
53
52
  *,
54
53
  idempotency_key: str,
@@ -57,10 +56,12 @@ class PlatformClient:
57
56
  visibility: str = "private",
58
57
  repo_url: str | None = None,
59
58
  ) -> dict:
60
- """Create a project under ``account_slug`` and return the created record.
59
+ """Provision a project in the developer key's account.
61
60
 
62
61
  Used by ``refactor init`` to auto-provision a cloud project for the local
63
- commit loop. ``repo_url`` is optional; it can be attached later from the
62
+ commit loop. This targets the developer-key authenticated ``/v1/projects/
63
+ provision`` endpoint (the JWT-only account endpoints do not accept a
64
+ developer key). ``repo_url`` is optional and can be attached later from the
64
65
  web UI. Raises ``PlatformError`` on any rejection or transport failure.
65
66
  """
66
67
  body: dict = {
@@ -73,7 +74,7 @@ class PlatformClient:
73
74
  body["repo_url"] = repo_url
74
75
  try:
75
76
  response = httpx.post(
76
- f"{self.base_url}/v1/accounts/{account_slug}/projects",
77
+ f"{self.base_url}/v1/projects/provision",
77
78
  headers={
78
79
  "Authorization": f"Bearer {developer_key}",
79
80
  "Content-Type": "application/json",
@@ -1065,25 +1065,35 @@ def init(
1065
1065
  # Authenticate and provision the cloud project BEFORE writing any files, so a
1066
1066
  # failure leaves the working tree untouched (fail-fast, no partial state).
1067
1067
  auth_ctx = _ensure_init_auth(project_root)
1068
- account_slug = auth_ctx.account_slug or load_credentials().get("active_account_slug") or ""
1069
- if not account_slug:
1070
- console.print(
1071
- "[red]Could not resolve your account.[/red] Run `refactor login` and retry."
1072
- )
1073
- raise typer.Exit(code=1)
1074
-
1075
1068
  project_name = project_root.name or "refactor-project"
1076
1069
  client = PlatformClient()
1077
1070
  try:
1078
1071
  created = client.create_project(
1079
1072
  auth_ctx.key.key,
1080
- account_slug,
1081
1073
  project_name,
1082
1074
  idempotency_key=str(uuid.uuid4()),
1083
1075
  )
1084
1076
  except PlatformError as exc:
1085
- console.print(f"[red]Could not create cloud project:[/red] {exc}")
1086
- raise typer.Exit(code=1) from exc
1077
+ # A rejected key mid-flow (revoked/rotated) is recoverable in a terminal:
1078
+ # ask for an updated key and retry once rather than dead-ending.
1079
+ if getattr(exc, "status_code", None) == 401 and sys.stdin.isatty():
1080
+ console.print(
1081
+ f"[yellow]{exc}.[/yellow] Enter an updated developer key to continue."
1082
+ )
1083
+ auth_ctx = _prompt_developer_key(project_root)
1084
+ try:
1085
+ created = client.create_project(
1086
+ auth_ctx.key.key,
1087
+ project_name,
1088
+ idempotency_key=str(uuid.uuid4()),
1089
+ )
1090
+ except PlatformError as retry_exc:
1091
+ console.print(f"[red]Could not create cloud project:[/red] {retry_exc}")
1092
+ raise typer.Exit(code=1) from retry_exc
1093
+ else:
1094
+ console.print(f"[red]Could not create cloud project:[/red] {exc}")
1095
+ raise typer.Exit(code=1) from exc
1096
+ account_slug = auth_ctx.account_slug or load_credentials().get("active_account_slug") or ""
1087
1097
  project_id = str(created.get("id") or "").strip()
1088
1098
  if not project_id:
1089
1099
  console.print("[red]Cloud project creation returned no id.[/red] Please retry.")
@@ -1098,9 +1108,10 @@ def init(
1098
1108
  record = register_project(project_root, constitution_hash=constitution.content_hash)
1099
1109
  console.print(f"[green]Created[/green] {consti_target}")
1100
1110
  console.print(f"[green]Created[/green] {config_target}")
1111
+ account_note = f" (account {account_slug})" if account_slug else ""
1101
1112
  console.print(
1102
- f"[green]Linked[/green] to cloud project [bold]{project_name}[/bold] "
1103
- f"(account {account_slug}). The project id is saved in {CONFIG_FILENAME}; "
1113
+ f"[green]Linked[/green] to cloud project [bold]{project_name}[/bold]"
1114
+ f"{account_note}. The project id is saved in {CONFIG_FILENAME}; "
1104
1115
  "rename the project or attach a repository anytime from the web UI."
1105
1116
  )
1106
1117
  if config_profile == "local_server":
@@ -1145,6 +1156,17 @@ def _ensure_init_auth(project_root: Path) -> AuthContext:
1145
1156
  raise typer.Exit(code=1)
1146
1157
 
1147
1158
  console.print("[bold]Sign in to Refactor[/bold] to create your cloud project.")
1159
+ return _prompt_developer_key(project_root)
1160
+
1161
+
1162
+ def _prompt_developer_key(project_root: Path) -> AuthContext:
1163
+ """Interactively prompt for a developer key, validate it, and persist it.
1164
+
1165
+ Shared by the initial `init` login and the retry path when the platform
1166
+ rejects a key mid-flow (e.g. it was revoked). Returns a fresh, validated
1167
+ ``AuthContext``. Non-interactive callers must guard on ``sys.stdin.isatty()``
1168
+ before calling this.
1169
+ """
1148
1170
  console.print(
1149
1171
  f"[dim]No account yet? Create one and generate a developer key at "
1150
1172
  f"{platform_url()}, then paste it below.[/dim]"
@@ -688,6 +688,15 @@ def _dispatch_key(live, project_root: Path, key: str, state: WatchState) -> None
688
688
  f"Press [{key}] again.[/dim]"
689
689
  )
690
690
  return
691
+ if not state.has_pending_work():
692
+ # Nothing anywhere needs review/merge: this is the idle steady
693
+ # state, so guide toward the next commit rather than implying the
694
+ # user picked the wrong node.
695
+ console.print(
696
+ "[dim]watch: all commits are reviewed and resolved — "
697
+ "watching for the next commit.[/dim]"
698
+ )
699
+ return
691
700
  console.print(
692
701
  f"[dim]watch: selected {node.short_sha} ({node.branch or 'detached'} / "
693
702
  f"phase={node.phase}, requests={node.rr_count}) has nothing to "
@@ -199,6 +199,27 @@ class WatchState:
199
199
  and node.sha not in self.forced_review
200
200
  )
201
201
 
202
+ def has_pending_work(self) -> bool:
203
+ """True when any node still needs attention (review, resolve, or merge).
204
+
205
+ Drives the terminal's idle vs. active affordances: when this is ``False``
206
+ every commit is reviewed with no open requests and nothing to finalize,
207
+ so the footer should say "watching for the next commit" rather than
208
+ advertising review/merge hotkeys the user can't act on.
209
+ """
210
+ for node in self._nodes.values():
211
+ if node.state in (QUEUED, REVIEWING):
212
+ return True
213
+ if (
214
+ node.state == REVIEWED
215
+ and node.rr_count > 0
216
+ and (node.phase == PHASE_REVIEW or node.sha in self.forced_review)
217
+ ):
218
+ return True
219
+ if node.phase == PHASE_FINALIZE and node.sha not in self.forced_review:
220
+ return True
221
+ return False
222
+
202
223
  def has(self, sha: str) -> bool:
203
224
  return sha in self._nodes
204
225
 
@@ -522,6 +543,7 @@ def _render_review_view(state: WatchState, *, now: datetime | None = None, width
522
543
  lines.append(_with_pad(row))
523
544
  lines.append(_with_pad(Text.from_markup("[cyan]+[/cyan]")))
524
545
  lines.append(_with_pad(Text("")))
546
+ pending = state.has_pending_work()
525
547
  selected = state.selected()
526
548
  if selected is not None:
527
549
  context = Text.from_markup("[bold bright_cyan]selected[/bold bright_cyan]: ")
@@ -531,15 +553,32 @@ def _render_review_view(state: WatchState, *, now: datetime | None = None, width
531
553
  context.append(f" requests={selected.rr_count}", style="dim")
532
554
  lines.append(_with_pad(context))
533
555
  lines.append(_with_pad(Text("")))
534
- lines.append(
535
- _with_pad(
536
- Text.from_markup(
537
- "[dim]\\[↑/↓ or j/k][/dim] select [bold]\\[r][/bold] refactor "
538
- "[bold]\\[f][/bold] fresh review [bold]\\[d][/bold] diff "
539
- "[bold]\\[q][/bold] quit"
556
+ if pending:
557
+ lines.append(
558
+ _with_pad(
559
+ Text.from_markup(
560
+ "[dim]\\[↑/↓ or j/k][/dim] select [bold]\\[r][/bold] refactor "
561
+ "[bold]\\[f][/bold] fresh review [bold]\\[d][/bold] diff "
562
+ "[bold]\\[q][/bold] quit"
563
+ )
564
+ )
565
+ )
566
+ else:
567
+ # Everything is reviewed and resolved: switch to an idle affordance so the
568
+ # user isn't offered review/merge keys that would no-op (docs/41 Part F).
569
+ lines.append(
570
+ _with_pad(
571
+ Text.from_markup("[green]✓ all commits reviewed[/green] — [dim]watching for the next commit[/dim]")
572
+ )
573
+ )
574
+ lines.append(
575
+ _with_pad(
576
+ Text.from_markup(
577
+ "[dim]\\[↑/↓ or j/k][/dim] select [bold]\\[d][/bold] diff "
578
+ "[bold]\\[q][/bold] quit"
579
+ )
540
580
  )
541
581
  )
542
- )
543
582
  return Group(*lines)
544
583
 
545
584
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.5.0
3
+ Version: 0.5.2
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
File without changes
File without changes