refactorai-cli 0.5.6__tar.gz → 0.5.8__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 (51) hide show
  1. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/PKG-INFO +2 -2
  2. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/pyproject.toml +2 -2
  3. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/pre_push_cmds.py +3 -2
  5. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/run_cmds.py +69 -6
  6. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/local_constitution.py +4 -0
  7. refactorai_cli-0.5.8/refactorai_cli/local_paths.py +76 -0
  8. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/refactor_branch_store.py +2 -2
  9. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/watch_supervisor.py +2 -2
  10. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli.egg-info/PKG-INFO +2 -2
  11. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli.egg-info/requires.txt +1 -1
  12. refactorai_cli-0.5.6/refactorai_cli/local_paths.py +0 -40
  13. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/README.md +0 -0
  14. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/auth.py +0 -0
  15. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/client.py +0 -0
  16. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/cloud_rr.py +0 -0
  17. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/__init__.py +0 -0
  18. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/account_cmds.py +0 -0
  19. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/auth_cmds.py +0 -0
  20. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/branch_cmds.py +0 -0
  21. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/cloud_cmds.py +0 -0
  22. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/engine_cmds.py +0 -0
  23. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/hook_cmds.py +0 -0
  24. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/model_cmds.py +0 -0
  25. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/request_cmds.py +0 -0
  26. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/rules_cmds.py +0 -0
  27. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/runtime_cmds.py +0 -0
  28. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/runtime_proxy_cmds.py +0 -0
  29. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/setup_cmds.py +0 -0
  30. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/toolchains_cmds.py +0 -0
  31. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commands/watch_cmds.py +0 -0
  32. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commit_queue.py +0 -0
  33. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/commit_telemetry.py +0 -0
  34. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/control_plane.py +0 -0
  35. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/credentials.py +0 -0
  36. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/dotenv_loader.py +0 -0
  37. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/git_scope.py +0 -0
  38. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/local_engine_runtime.py +0 -0
  39. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/main.py +0 -0
  40. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/model_policy.py +0 -0
  41. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/pre_push_gate.py +0 -0
  42. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/review_runner.py +0 -0
  43. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/runtime_manager.py +0 -0
  44. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/settings.py +0 -0
  45. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/setup_flow.py +0 -0
  46. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli/watch_state.py +0 -0
  47. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  48. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  49. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli.egg-info/entry_points.txt +0 -0
  50. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/refactorai_cli.egg-info/top_level.txt +0 -0
  51. {refactorai_cli-0.5.6 → refactorai_cli-0.5.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.5.6
3
+ Version: 0.5.8
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -8,7 +8,7 @@ Requires-Dist: typer>=0.12.0
8
8
  Requires-Dist: httpx>=0.27.0
9
9
  Requires-Dist: rich>=13.7.0
10
10
  Requires-Dist: PyYAML>=6.0.1
11
- Requires-Dist: refactorai-core>=3.2.11
11
+ Requires-Dist: refactorai-core>=3.2.13
12
12
 
13
13
  # refactorai-cli
14
14
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "refactorai-cli"
3
- version = "0.5.6"
3
+ version = "0.5.8"
4
4
  description = "Local-first CLI for the refactor platform"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -12,7 +12,7 @@ dependencies = [
12
12
  "httpx>=0.27.0",
13
13
  "rich>=13.7.0",
14
14
  "PyYAML>=6.0.1",
15
- "refactorai-core>=3.2.11",
15
+ "refactorai-core>=3.2.13",
16
16
  ]
17
17
 
18
18
  [project.scripts]
@@ -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.6"
8
+ __version__ = "0.5.8"
@@ -72,9 +72,10 @@ def _count_requests(project_root: Path) -> tuple[int, int]:
72
72
 
73
73
  def _record_skip(project_root: Path, *, reasons: list[str], reason: str) -> None:
74
74
  try:
75
- from refactor_core.paths import ensure_dir, project_store_dir
75
+ from refactor_core.paths import ensure_dir
76
+ from refactor_core.store import resolve_store_dir
76
77
 
77
- audit_path = ensure_dir(project_store_dir(project_root)) / "pre_push_audit.jsonl"
78
+ audit_path = ensure_dir(resolve_store_dir(project_root)) / "pre_push_audit.jsonl"
78
79
  entry = {
79
80
  "at": datetime.now(timezone.utc).isoformat(),
80
81
  "event": "pre_push_bypass",
@@ -90,8 +90,9 @@ from refactor_core.store import (
90
90
  create_run,
91
91
  head_run_dir,
92
92
  list_runs,
93
- project_store_dir,
94
93
  prune_runs,
94
+ resolve_project_id as resolve_store_project_id,
95
+ resolve_store_dir,
95
96
  read_findings,
96
97
  read_report,
97
98
  read_sandbox_install_history,
@@ -913,6 +914,50 @@ def _load_project() -> tuple[Path, object, object]:
913
914
  return consti_path.parent, constitution, config
914
915
 
915
916
 
917
+ def _print_store_context(project_root) -> None:
918
+ """Surface the resolved project id + central store dir for this run.
919
+
920
+ Makes the id-keyed store observable so a wrong/duplicate binding (or an
921
+ unexpectedly unbound folder) is obvious at a glance instead of surfacing as a
922
+ confusing "resumed old requests" symptom later.
923
+ """
924
+ try:
925
+ pid = resolve_store_project_id(project_root)
926
+ store_dir = resolve_store_dir(project_root)
927
+ except Exception:
928
+ return
929
+ label = pid or "(unbound / local)"
930
+ console.print(f"[dim]project id:[/dim] {label} [dim]· store:[/dim] {store_dir}")
931
+
932
+
933
+ def _report_store_binding(project_root) -> None:
934
+ """Doctor diagnostic: resolved project id, store dir, and binding mismatch.
935
+
936
+ A mismatch between the id resolved from ``refactor.config`` and the id stamped
937
+ on the on-disk store record is the fingerprint of a re-linked/duplicated
938
+ project; surfacing it here turns a silent state bug into an obvious check.
939
+ """
940
+ try:
941
+ from refactor_core.store import project_record
942
+
943
+ pid = resolve_store_project_id(project_root)
944
+ store_dir = resolve_store_dir(project_root)
945
+ except Exception:
946
+ return
947
+ console.print(f"[bold]project id[/bold]: {pid or '(unbound / local)'}")
948
+ console.print(f"[bold]store[/bold]: {store_dir}")
949
+ try:
950
+ record = project_record(project_root) or {}
951
+ record_id = str(record.get("project_id", "") or "")
952
+ except Exception:
953
+ record_id = ""
954
+ if pid and record_id and record_id != pid:
955
+ console.print(
956
+ f"[yellow]store binding[/yellow]: on-disk record id '{record_id}' differs "
957
+ f"from resolved '{pid}'. The store will re-key to the resolved id."
958
+ )
959
+
960
+
916
961
  def _warn_raw_key(constitution, config) -> None:
917
962
  if has_raw_secret_literal(constitution) or bool(raw_secret_matches_config(config)):
918
963
  console.print(
@@ -1105,7 +1150,9 @@ def init(
1105
1150
  config_target.write_text(config_text, encoding="utf-8")
1106
1151
 
1107
1152
  constitution = load_constitution(consti_target)
1108
- record = register_project(project_root, constitution_hash=constitution.content_hash)
1153
+ record = register_project(
1154
+ project_root, constitution_hash=constitution.content_hash, project_id=project_id
1155
+ )
1109
1156
  console.print(f"[green]Created[/green] {consti_target}")
1110
1157
  console.print(f"[green]Created[/green] {config_target}")
1111
1158
  account_note = f" (account {account_slug})" if account_slug else ""
@@ -1125,7 +1172,7 @@ def init(
1125
1172
  "(mode=cloud_byok, env-key-first via provider_key; credential_ref is optional fallback)."
1126
1173
  )
1127
1174
  console.print(
1128
- f"[green]Registered[/green] project in central store: {project_store_dir(project_root)}"
1175
+ f"[green]Registered[/green] project in central store: {resolve_store_dir(project_root)}"
1129
1176
  )
1130
1177
  console.print(
1131
1178
  f"Only {CONSTITUTION_FILENAME} and {CONFIG_FILENAME} are added to your repo; "
@@ -2787,7 +2834,14 @@ def review(
2787
2834
  )
2788
2835
  for warning in compliance_resolution.warnings:
2789
2836
  console.print(f"[yellow]review: compliance warning:[/yellow] {warning}")
2790
- register_project(project_root, constitution_hash=constitution.content_hash)
2837
+ from refactorai_cli.commit_telemetry import resolve_project_id
2838
+
2839
+ register_project(
2840
+ project_root,
2841
+ constitution_hash=constitution.content_hash,
2842
+ project_id=resolve_project_id(project_root) or None,
2843
+ )
2844
+ _print_store_context(project_root)
2791
2845
  _maybe_build_graph(project_root, project_config)
2792
2846
 
2793
2847
  # Commit-scoped review (docs/41 `R28`). Works in every execution mode: local
@@ -3121,7 +3175,14 @@ def code(
3121
3175
  )
3122
3176
  for warning in compliance_resolution.warnings:
3123
3177
  console.print(f"[yellow]code: compliance warning:[/yellow] {warning}")
3124
- register_project(project_root, constitution_hash=constitution.content_hash)
3178
+ from refactorai_cli.commit_telemetry import resolve_project_id
3179
+
3180
+ register_project(
3181
+ project_root,
3182
+ constitution_hash=constitution.content_hash,
3183
+ project_id=resolve_project_id(project_root) or None,
3184
+ )
3185
+ _print_store_context(project_root)
3125
3186
  _maybe_build_graph(project_root, project_config)
3126
3187
 
3127
3188
  # Commit-scoped resolve (docs/41 `R28`, Phase 4), symmetric to `review` and
@@ -4367,7 +4428,7 @@ def gc(
4367
4428
  removed = prune_runs(project_root, keep=keep)
4368
4429
  console.print(
4369
4430
  f"[green]Pruned[/green] {len(removed)} run(s); kept {before - len(removed)} "
4370
- f"under {project_store_dir(project_root)}."
4431
+ f"under {resolve_store_dir(project_root)}."
4371
4432
  )
4372
4433
 
4373
4434
 
@@ -4602,6 +4663,7 @@ def _cloud_doctor(
4602
4663
  """
4603
4664
  console.print(f"[bold]project[/bold]: {project_root}")
4604
4665
  console.print(f"[bold]mode[/bold]: {execution_mode}")
4666
+ _report_store_binding(project_root)
4605
4667
 
4606
4668
  try:
4607
4669
  auth_ctx = ensure_authenticated(project_root)
@@ -4835,6 +4897,7 @@ def doctor(
4835
4897
  console.print(f"[bold]mode[/bold]: {mode}")
4836
4898
  console.print(f"[bold]provider[/bold]: {provider_name}")
4837
4899
  console.print(f"[bold]supported[/bold]: {', '.join(supported_providers())}")
4900
+ _report_store_binding(project_root)
4838
4901
 
4839
4902
  try:
4840
4903
  auth_ctx = ensure_authenticated(project_root)
@@ -46,11 +46,15 @@ def _search_root(start: Path | None = None) -> Path:
46
46
 
47
47
 
48
48
  def find_config(start: Path | None = None) -> Path | None:
49
+ # Stop at the enclosing git repo root so we never adopt a parent directory's
50
+ # refactor.config (mirror of refactor_core.constitution discovery guard).
49
51
  current = _search_root(start)
50
52
  for candidate in [current, *current.parents]:
51
53
  config = candidate / CONFIG_FILENAME
52
54
  if config.is_file():
53
55
  return config
56
+ if (candidate / ".git").exists():
57
+ return None
54
58
  return None
55
59
 
56
60
 
@@ -0,0 +1,76 @@
1
+ """Local path helpers for the standalone CLI package.
2
+
3
+ These mirror the path helpers that were previously imported from ``refactor_core``
4
+ so the public ``refactorai-cli`` package can install without requiring a separate
5
+ ``refactor-core`` wheel on PyPI.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import os
11
+ import re
12
+ from pathlib import Path
13
+
14
+
15
+ def refactor_home() -> Path:
16
+ override = os.environ.get("REFACTOR_HOME")
17
+ if override:
18
+ return Path(override).expanduser()
19
+ return Path.home() / ".refactor"
20
+
21
+
22
+ def encode_project_path(project_root: Path | str) -> str:
23
+ resolved = str(Path(project_root).resolve())
24
+ return resolved.lstrip("/").replace("/", "-") or "root"
25
+
26
+
27
+ def sanitize_project_id(project_id: str | None) -> str:
28
+ """Filesystem-safe project id, or ``""`` (mirror of core helper)."""
29
+ value = str(project_id or "").strip()
30
+ if not value or value in {".", ".."}:
31
+ return ""
32
+ safe = re.sub(r"[^A-Za-z0-9._-]+", "-", value).strip("-")
33
+ if safe in {"", ".", ".."}:
34
+ return ""
35
+ return safe[:128]
36
+
37
+
38
+ def project_store_dir_by_id(project_id: str) -> Path:
39
+ safe = sanitize_project_id(project_id)
40
+ if not safe:
41
+ raise ValueError("project_store_dir_by_id requires a non-empty project_id")
42
+ return refactor_home() / "projects" / safe
43
+
44
+
45
+ def ensure_dir(path: Path, *, mode: int = 0o700) -> Path:
46
+ path.mkdir(parents=True, exist_ok=True, mode=mode)
47
+ return path
48
+
49
+
50
+ def credentials_path() -> Path:
51
+ return refactor_home() / "credentials.json"
52
+
53
+
54
+ def validation_cache_dir() -> Path:
55
+ return refactor_home() / "cache" / "validation"
56
+
57
+
58
+ def project_store_dir(project_root: Path | str) -> Path:
59
+ """Legacy path-encoded store dir (backward-compat reads + migration only)."""
60
+ return refactor_home() / "projects" / encode_project_path(project_root)
61
+
62
+
63
+ def resolve_store_dir(project_root: Path | str, *, project_id: str | None = None) -> Path:
64
+ """Return the project's central store dir, keyed by ``project_id``.
65
+
66
+ Delegates to ``refactor_core.store.resolve_store_dir`` (the single source of
67
+ truth, which also migrates any legacy path-encoded dir) when the core package
68
+ is importable. Falls back to the legacy path-encoded dir for pip-only installs
69
+ without ``refactor_core`` on disk, preserving this module's standalone role.
70
+ """
71
+ try:
72
+ from refactor_core.store import resolve_store_dir as _core_resolve
73
+
74
+ return _core_resolve(project_root, project_id=project_id)
75
+ except Exception:
76
+ return project_store_dir(project_root)
@@ -16,7 +16,7 @@ from dataclasses import dataclass
16
16
  from datetime import datetime, timezone
17
17
  from pathlib import Path
18
18
 
19
- from refactorai_cli.local_paths import ensure_dir, project_store_dir
19
+ from refactorai_cli.local_paths import ensure_dir, resolve_store_dir
20
20
 
21
21
  _DIR = "refactor_branches"
22
22
 
@@ -67,7 +67,7 @@ def refactor_branch_name(
67
67
 
68
68
 
69
69
  def _dir(project_root: Path | str) -> Path:
70
- return project_store_dir(project_root) / _DIR
70
+ return resolve_store_dir(project_root) / _DIR
71
71
 
72
72
 
73
73
  def _path(project_root: Path | str, branch: str) -> Path:
@@ -50,9 +50,9 @@ def lock_path(project_root: Path | str) -> Path:
50
50
  directory = commit_queue.runs_dir(project_root)
51
51
  if directory is None:
52
52
  # Outside a git dir (or tests): fall back to the central project store.
53
- from refactorai_cli.local_paths import project_store_dir
53
+ from refactorai_cli.local_paths import resolve_store_dir
54
54
 
55
- directory = project_store_dir(project_root)
55
+ directory = resolve_store_dir(project_root)
56
56
  return Path(directory) / LOCK_FILENAME
57
57
 
58
58
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.5.6
3
+ Version: 0.5.8
4
4
  Summary: Local-first CLI for the refactor platform
5
5
  Requires-Python: >=3.11
6
6
  Description-Content-Type: text/markdown
@@ -8,7 +8,7 @@ Requires-Dist: typer>=0.12.0
8
8
  Requires-Dist: httpx>=0.27.0
9
9
  Requires-Dist: rich>=13.7.0
10
10
  Requires-Dist: PyYAML>=6.0.1
11
- Requires-Dist: refactorai-core>=3.2.11
11
+ Requires-Dist: refactorai-core>=3.2.13
12
12
 
13
13
  # refactorai-cli
14
14
 
@@ -2,4 +2,4 @@ typer>=0.12.0
2
2
  httpx>=0.27.0
3
3
  rich>=13.7.0
4
4
  PyYAML>=6.0.1
5
- refactorai-core>=3.2.11
5
+ refactorai-core>=3.2.13
@@ -1,40 +0,0 @@
1
- """Local path helpers for the standalone CLI package.
2
-
3
- These mirror the path helpers that were previously imported from ``refactor_core``
4
- so the public ``refactorai-cli`` package can install without requiring a separate
5
- ``refactor-core`` wheel on PyPI.
6
- """
7
-
8
- from __future__ import annotations
9
-
10
- import os
11
- from pathlib import Path
12
-
13
-
14
- def refactor_home() -> Path:
15
- override = os.environ.get("REFACTOR_HOME")
16
- if override:
17
- return Path(override).expanduser()
18
- return Path.home() / ".refactor"
19
-
20
-
21
- def encode_project_path(project_root: Path | str) -> str:
22
- resolved = str(Path(project_root).resolve())
23
- return resolved.lstrip("/").replace("/", "-") or "root"
24
-
25
-
26
- def ensure_dir(path: Path, *, mode: int = 0o700) -> Path:
27
- path.mkdir(parents=True, exist_ok=True, mode=mode)
28
- return path
29
-
30
-
31
- def credentials_path() -> Path:
32
- return refactor_home() / "credentials.json"
33
-
34
-
35
- def validation_cache_dir() -> Path:
36
- return refactor_home() / "cache" / "validation"
37
-
38
-
39
- def project_store_dir(project_root: Path | str) -> Path:
40
- return refactor_home() / "projects" / encode_project_path(project_root)
File without changes
File without changes