refactorai-cli 0.2.4__tar.gz → 0.2.5__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 (31) hide show
  1. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/PKG-INFO +1 -1
  2. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/pyproject.toml +1 -1
  3. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/__init__.py +1 -1
  4. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/setup_cmds.py +1 -0
  5. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/local_engine_runtime.py +11 -1
  6. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/setup_flow.py +46 -8
  7. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/PKG-INFO +1 -1
  8. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/README.md +0 -0
  9. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/auth.py +0 -0
  10. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/client.py +0 -0
  11. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/__init__.py +0 -0
  12. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/auth_cmds.py +0 -0
  13. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/engine_cmds.py +0 -0
  14. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/model_cmds.py +0 -0
  15. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/rules_cmds.py +0 -0
  16. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/run_cmds.py +0 -0
  17. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/commands/runtime_cmds.py +0 -0
  18. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/control_plane.py +0 -0
  19. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/credentials.py +0 -0
  20. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/local_constitution.py +0 -0
  21. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/local_paths.py +0 -0
  22. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/main.py +0 -0
  23. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/model_policy.py +0 -0
  24. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/runtime_manager.py +0 -0
  25. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli/settings.py +0 -0
  26. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/SOURCES.txt +0 -0
  27. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/dependency_links.txt +0 -0
  28. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/entry_points.txt +0 -0
  29. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/requires.txt +0 -0
  30. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/refactorai_cli.egg-info/top_level.txt +0 -0
  31. {refactorai_cli-0.2.4 → refactorai_cli-0.2.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.4
3
+ Version: 0.2.5
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.2.4"
3
+ version = "0.2.5"
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.2.4"
8
+ __version__ = "0.2.5"
@@ -53,6 +53,7 @@ def setup(
53
53
  resume=resume,
54
54
  from_stage=from_stage,
55
55
  ask_approval=lambda prompt: _ask_approval(yes, prompt),
56
+ on_progress=lambda message: console.print(f"[cyan]{message}[/cyan]"),
56
57
  )
57
58
  except SetupError as exc:
58
59
  console.print(f"[red]Setup failed:[/red] {exc}")
@@ -7,6 +7,7 @@ remain usable without a separate ``refactor-core`` package dependency.
7
7
  from __future__ import annotations
8
8
 
9
9
  import json
10
+ import re
10
11
  import shutil
11
12
  import subprocess
12
13
  from datetime import datetime, timezone
@@ -22,6 +23,8 @@ ENGINE_DIR = "engine"
22
23
  ENGINE_STATE_FILE = "engine.json"
23
24
  ENGINE_MODELS_DIR = "models"
24
25
  ENGINE_CACHE_DIR = "cache/ollama"
26
+ _ANSI_RE = re.compile(r"\x1b\[[0-9;?]*[A-Za-z]")
27
+ _CONTROL_RE = re.compile(r"[\x00-\x08\x0b-\x1f\x7f]")
25
28
 
26
29
 
27
30
  def _now_iso() -> str:
@@ -85,6 +88,13 @@ def _run(cmd: list[str]) -> subprocess.CompletedProcess:
85
88
  return subprocess.run(cmd, capture_output=True, text=True)
86
89
 
87
90
 
91
+ def _clean_output(text: str) -> str:
92
+ cleaned = _ANSI_RE.sub("", text or "")
93
+ cleaned = cleaned.replace("\r", "\n")
94
+ cleaned = _CONTROL_RE.sub("", cleaned)
95
+ return "\n".join(line.strip() for line in cleaned.splitlines() if line.strip())
96
+
97
+
88
98
  def _exists(runtime: str, name: str) -> bool:
89
99
  proc = _run([runtime, "container", "exists", name])
90
100
  return proc.returncode == 0
@@ -235,7 +245,7 @@ def pull_model(*, runtime: str, container_name: str, model_id: str) -> tuple[boo
235
245
  if probe.returncode != 0:
236
246
  return False, "Engine image does not include `ollama`; use a model-capable engine image."
237
247
  proc = _run([runtime, "exec", container_name, "sh", "-lc", f"ollama pull {model_id}"])
238
- text = (proc.stdout or proc.stderr or "").strip()
248
+ text = _clean_output(proc.stdout or proc.stderr or "")
239
249
  if proc.returncode != 0:
240
250
  return False, text or "Model pull failed."
241
251
  return True, text or "Model pull complete."
@@ -7,6 +7,7 @@ import shutil
7
7
  from dataclasses import dataclass
8
8
  from datetime import datetime, timezone
9
9
  from pathlib import Path
10
+ from inspect import signature
10
11
  from typing import Callable
11
12
 
12
13
  from refactorai_cli.local_engine_runtime import (
@@ -142,7 +143,7 @@ def get_setup_diagnostics() -> dict:
142
143
  return {"state": state, "stages": stages}
143
144
 
144
145
 
145
- def _stage_s1_precheck(_ask_approval: Callable[[str], bool]) -> dict:
146
+ def _stage_s1_precheck(_ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None) -> dict:
146
147
  profile = detect_machine_profile()
147
148
  runtime, reason = resolve_engine_runtime("podman")
148
149
  disk = shutil.disk_usage(str(refactor_home()))
@@ -159,7 +160,7 @@ def _stage_s1_precheck(_ask_approval: Callable[[str], bool]) -> dict:
159
160
  }
160
161
 
161
162
 
162
- def _stage_s2_auth(_ask_approval: Callable[[str], bool]) -> dict:
163
+ def _stage_s2_auth(_ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None) -> dict:
163
164
  auth = ensure_authenticated(force_remote=True)
164
165
  lease = ensure_lease(force_refresh=True)
165
166
  return {
@@ -171,11 +172,17 @@ def _stage_s2_auth(_ask_approval: Callable[[str], bool]) -> dict:
171
172
  }
172
173
 
173
174
 
174
- def _stage_s3_runtime(ask_approval: Callable[[str], bool]) -> dict:
175
+ def _stage_s3_runtime(ask_approval: Callable[[str], bool], emit: Callable[[str], None] | None = None) -> dict:
176
+ if emit:
177
+ emit("Resolving runtime manifest from control plane...")
175
178
  manifest = resolve_runtime_manifest(channel="stable")
176
179
  if not ask_approval(f"Install runtime artifact version {manifest.runtime_version}?"):
177
180
  raise SetupError("Runtime installation was not approved.")
181
+ if emit:
182
+ emit(f"Downloading runtime artifact {manifest.runtime_version}...")
178
183
  artifact = download_artifact(manifest.artifact_url)
184
+ if emit:
185
+ emit("Verifying and activating runtime artifact...")
179
186
  path = activate_runtime(manifest, artifact, channel="stable")
180
187
  status = runtime_status()
181
188
  return {
@@ -200,7 +207,9 @@ def _selected_backend() -> str:
200
207
  return backend
201
208
 
202
209
 
203
- def _stage_s4_backend_choice(ask_approval: Callable[[str], bool]) -> dict:
210
+ def _stage_s4_backend_choice(
211
+ ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None
212
+ ) -> dict:
204
213
  if ask_approval("Install local refactor-server and local model now?"):
205
214
  backend = BACKEND_LOCAL_SERVER
206
215
  else:
@@ -211,7 +220,7 @@ def _stage_s4_backend_choice(ask_approval: Callable[[str], bool]) -> dict:
211
220
  }
212
221
 
213
222
 
214
- def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
223
+ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool], emit: Callable[[str], None] | None = None) -> dict:
215
224
  backend = _selected_backend()
216
225
  if backend == BACKEND_BYOK:
217
226
  return {
@@ -222,9 +231,13 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
222
231
  }
223
232
  if not ask_approval("Install/start local refactor-server and bootstrap model now?"):
224
233
  raise SetupError("Local server bootstrap was not approved.")
234
+ if emit:
235
+ emit("Checking local engine runtime availability...")
225
236
  runtime, reason = resolve_engine_runtime("podman")
226
237
  if not runtime:
227
238
  raise SetupError(reason or "Engine runtime unavailable.")
239
+ if emit:
240
+ emit("Starting local refactor-engine container...")
228
241
  ok, message, state = ensure_engine_up(runtime=runtime)
229
242
  if not ok:
230
243
  raise SetupError(f"Local server bootstrap failed: {message}")
@@ -232,6 +245,8 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
232
245
  if str(estatus.get("status") or "").lower() != "ready":
233
246
  # If a stale container from older installs exists (for example an image
234
247
  # without ollama), rebuild once with the default engine image.
248
+ if emit:
249
+ emit("Detected non-ready engine container. Rebuilding once with default model runtime image...")
235
250
  ok, message, _state = ensure_engine_up(
236
251
  runtime=runtime,
237
252
  image=DEFAULT_ENGINE_IMAGE,
@@ -247,6 +262,8 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
247
262
  "Run `refactor engine status` and retry from S5."
248
263
  )
249
264
 
265
+ if emit:
266
+ emit("Resolving signed model policy...")
250
267
  policy = resolve_policy(force_refresh=False)
251
268
  profile = detect_machine_profile()
252
269
  model_id = recommended_model_id(policy, profile=str(profile["profile"]))
@@ -261,6 +278,11 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
261
278
  runtime, reason = resolve_engine_runtime("podman")
262
279
  if not runtime:
263
280
  raise SetupError(reason or "Engine runtime unavailable.")
281
+ if emit:
282
+ emit(
283
+ f"Pulling recommended model '{model_id}' in local engine "
284
+ "(this can take several minutes on first download)..."
285
+ )
264
286
  ok, message = pull_model(runtime=runtime, container_name=DEFAULT_ENGINE_CONTAINER, model_id=model_id)
265
287
  if not ok:
266
288
  raise SetupError(f"Model pull failed: {message}")
@@ -273,7 +295,7 @@ def _stage_s5_backend_bootstrap(ask_approval: Callable[[str], bool]) -> dict:
273
295
  }
274
296
 
275
297
 
276
- def _stage_s6_validate(_ask_approval: Callable[[str], bool]) -> dict:
298
+ def _stage_s6_validate(_ask_approval: Callable[[str], bool], _emit: Callable[[str], None] | None = None) -> dict:
277
299
  backend = _selected_backend()
278
300
  runtime_status_data = runtime_status()
279
301
  if not runtime_status_data.get("active_version") or not runtime_status_data.get("active_artifact_exists"):
@@ -309,7 +331,7 @@ def _stage_s6_validate(_ask_approval: Callable[[str], bool]) -> dict:
309
331
  }
310
332
 
311
333
 
312
- STAGE_HANDLERS: dict[str, Callable[[Callable[[str], bool]], dict]] = {
334
+ STAGE_HANDLERS: dict[str, Callable[..., dict]] = {
313
335
  "S1": _stage_s1_precheck,
314
336
  "S2": _stage_s2_auth,
315
337
  "S3": _stage_s3_runtime,
@@ -319,12 +341,24 @@ STAGE_HANDLERS: dict[str, Callable[[Callable[[str], bool]], dict]] = {
319
341
  }
320
342
 
321
343
 
344
+ def _invoke_stage_handler(
345
+ handler: Callable,
346
+ ask_approval: Callable[[str], bool],
347
+ on_progress: Callable[[str], None] | None,
348
+ ) -> dict:
349
+ param_names = list(signature(handler).parameters.keys())
350
+ if any(name in {"emit", "_emit", "on_progress"} for name in param_names):
351
+ return handler(ask_approval, on_progress)
352
+ return handler(ask_approval)
353
+
354
+
322
355
  def run_setup(
323
356
  *,
324
357
  auto_approve: bool = False,
325
358
  resume: bool = False,
326
359
  from_stage: str | None = None,
327
360
  ask_approval: Callable[[str], bool] | None = None,
361
+ on_progress: Callable[[str], None] | None = None,
328
362
  ) -> SetupResult:
329
363
  if ask_approval is None:
330
364
  ask_approval = lambda _prompt: bool(auto_approve)
@@ -360,6 +394,8 @@ def run_setup(
360
394
  }
361
395
  )
362
396
  for stage_id in STAGE_IDS[start_index:]:
397
+ if on_progress:
398
+ on_progress(f"Running stage {stage_id} ({STAGE_NAMES.get(stage_id, stage_id)})...")
363
399
  write_setup_state(
364
400
  {
365
401
  "status": "in_progress",
@@ -371,7 +407,7 @@ def run_setup(
371
407
  )
372
408
  handler = STAGE_HANDLERS[stage_id]
373
409
  try:
374
- output = handler(ask_approval)
410
+ output = _invoke_stage_handler(handler, ask_approval, on_progress)
375
411
  except SetupError as exc:
376
412
  write_setup_state(
377
413
  {
@@ -410,6 +446,8 @@ def run_setup(
410
446
  write_stage_output(stage_id, output)
411
447
  if stage_id not in completed:
412
448
  completed.append(stage_id)
449
+ if on_progress:
450
+ on_progress(f"Completed stage {stage_id}.")
413
451
  final_backend = _normalize_backend(read_setup_state().get("execution_backend"))
414
452
  write_setup_state(
415
453
  {
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: refactorai-cli
3
- Version: 0.2.4
3
+ Version: 0.2.5
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