qbraid-cli 0.15.0__tar.gz → 0.15.1__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 (68) hide show
  1. {qbraid_cli-0.15.0/qbraid_cli.egg-info → qbraid_cli-0.15.1}/PKG-INFO +1 -1
  2. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/pyproject.toml +1 -1
  3. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/skills/app.py +113 -16
  4. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1/qbraid_cli.egg-info}/PKG-INFO +1 -1
  5. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/LICENSE +0 -0
  6. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/README.md +0 -0
  7. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/__init__.py +0 -0
  8. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/_build_info.py +0 -0
  9. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/_version.py +0 -0
  10. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/account/__init__.py +0 -0
  11. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/account/app.py +0 -0
  12. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/admin/__init__.py +0 -0
  13. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/admin/app.py +0 -0
  14. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/admin/headers.py +0 -0
  15. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/admin/validation.py +0 -0
  16. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/agents/__init__.py +0 -0
  17. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/agents/app.py +0 -0
  18. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/ai/__init__.py +0 -0
  19. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/ai/app.py +0 -0
  20. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/ai/token.py +0 -0
  21. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/compute/__init__.py +0 -0
  22. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/compute/app.py +0 -0
  23. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/compute/instances.py +0 -0
  24. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/compute/profiles.py +0 -0
  25. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/compute/server.py +0 -0
  26. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/compute/ssh.py +0 -0
  27. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/configure/__init__.py +0 -0
  28. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/configure/actions.py +0 -0
  29. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/configure/app.py +0 -0
  30. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/devices/__init__.py +0 -0
  31. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/devices/app.py +0 -0
  32. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/devices/validation.py +0 -0
  33. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/__init__.py +0 -0
  34. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/activate.py +0 -0
  35. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/app.py +0 -0
  36. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/create.py +0 -0
  37. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/data_handling.py +0 -0
  38. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/groups.py +0 -0
  39. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/publish.py +0 -0
  40. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/registry.py +0 -0
  41. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/envs/share.py +0 -0
  42. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/exceptions.py +0 -0
  43. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/handlers.py +0 -0
  44. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/jobs/__init__.py +0 -0
  45. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/jobs/app.py +0 -0
  46. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/jobs/groups.py +0 -0
  47. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/kernels/__init__.py +0 -0
  48. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/kernels/app.py +0 -0
  49. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/lab/__init__.py +0 -0
  50. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/lab/app.py +0 -0
  51. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/main.py +0 -0
  52. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/pip/__init__.py +0 -0
  53. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/pip/app.py +0 -0
  54. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/pip/hooks.py +0 -0
  55. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/projects/__init__.py +0 -0
  56. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/projects/app.py +0 -0
  57. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/py.typed +0 -0
  58. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/skills/__init__.py +0 -0
  59. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/troubleshoot/__init__.py +0 -0
  60. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli/troubleshoot/app.py +0 -0
  61. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli.egg-info/SOURCES.txt +0 -0
  62. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli.egg-info/dependency_links.txt +0 -0
  63. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli.egg-info/entry_points.txt +0 -0
  64. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli.egg-info/requires.txt +0 -0
  65. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/qbraid_cli.egg-info/top_level.txt +0 -0
  66. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/setup.cfg +0 -0
  67. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/tests/test_cli_e2e.py +0 -0
  68. {qbraid_cli-0.15.0 → qbraid_cli-0.15.1}/tests/test_version.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qbraid-cli
3
- Version: 0.15.0
3
+ Version: 0.15.1
4
4
  Summary: Command Line Interface for interacting with all parts of the qBraid platform.
5
5
  Author-email: qBraid Development Team <contact@qbraid.com>
6
6
  License-Expression: LicenseRef-Proprietary
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "qbraid-cli"
7
- version = "0.15.0"
7
+ version = "0.15.1"
8
8
  description = "Command Line Interface for interacting with all parts of the qBraid platform."
9
9
  readme = "README.md"
10
10
  authors = [{ name = "qBraid Development Team", email = "contact@qbraid.com" }]
@@ -40,11 +40,21 @@ class SkillTargetOption(str, Enum):
40
40
 
41
41
  CLAUDE_CODE = "claude-code"
42
42
  CODEX = "codex"
43
+ PI = "pi"
43
44
  CURSOR = "cursor"
44
45
  COPILOT = "copilot"
45
46
  ALL = "all"
46
47
 
47
48
 
49
+ _ACTION_STYLE = {
50
+ "installed": "[green]installed[/green]",
51
+ "updated": "[green]updated[/green]",
52
+ "unchanged": "[dim]unchanged[/dim]",
53
+ "failed": "[red]failed[/red]",
54
+ "unknown": "[dim]done (older qbraid-core)[/dim]",
55
+ }
56
+
57
+
48
58
  class VisibilityOption(str, Enum):
49
59
  """Skill visibility level."""
50
60
 
@@ -213,39 +223,106 @@ def skills_install(
213
223
  "-t",
214
224
  help="Target AI coding tool(s) to install for",
215
225
  ),
226
+ path: Optional[str] = typer.Option(
227
+ None,
228
+ "--path",
229
+ "-p",
230
+ help="Install into this directory instead of a tool's skills directory",
231
+ ),
232
+ force: bool = typer.Option(
233
+ False,
234
+ "--force",
235
+ "-f",
236
+ help="Replace the installed copy even if it is unchanged or was not installed by qbraid",
237
+ ),
216
238
  ) -> None:
217
239
  """Install a skill for AI coding tools.
218
240
 
219
- By default, installs to Claude Code (~/.claude/skills/).
220
- Use --target all to install to all supported tools.
241
+ By default, installs to Claude Code (~/.claude/skills/). Use --target all
242
+ for every supported tool; --target codex or --target pi both land in
243
+ ~/.agents/skills/, which Codex and CodeQ (pi) read.
244
+
245
+ In a tool's skills directory the skill is placed under its own name (not
246
+ its slug); with --path it is placed under its slug at that path. The
247
+ download is checked against the registry's checksum, and installing an
248
+ unchanged skill again is a no-op.
221
249
  """
250
+ from pathlib import Path
251
+
252
+ install_path = Path(path).expanduser() if path else None
253
+ console = Console()
254
+ legacy_core = False
222
255
 
223
256
  def install_skill():
257
+ nonlocal legacy_core
224
258
  from qbraid_core.services.skills import SkillsClient
225
259
 
226
260
  client = SkillsClient()
227
- return client.install_skill(slug, target=target.value)
228
-
229
- console = Console()
261
+ if hasattr(client, "install_skill_ex"):
262
+ return client.install_skill_ex(slug, target=target.value, install_path=install_path, force=force)
263
+ # older qbraid-core: slug-named directories, no checksum or manifest, no --force
264
+ legacy_core = True
265
+ return {
266
+ name: _LegacyInstall(slug, installed_path)
267
+ for name, installed_path in client.install_skill(
268
+ slug, target=target.value, install_path=install_path
269
+ ).items()
270
+ }
230
271
 
231
272
  try:
232
273
  console.print(f"[dim]Installing skill '{slug}'...[/dim]")
233
- installed_paths = run_progress_task(install_skill)
274
+ results = run_progress_task(install_skill)
234
275
 
235
- console.print(f"\n[green]✓[/green] Skill '{slug}' installed successfully!\n")
276
+ if legacy_core and force:
277
+ console.print(
278
+ "[yellow]Note:[/yellow] the installed qbraid-core does not support --force; "
279
+ "the flag was ignored. Upgrade qbraid-core for --force, checksum "
280
+ "verification and name-based skill directories."
281
+ )
236
282
 
237
- for target_name, path in installed_paths.items():
238
- console.print(f" [cyan]{target_name}[/cyan]: {path}")
283
+ failed = [name for name, result in results.items() if result.action == "failed"]
284
+ mark = "[yellow]![/yellow]" if failed else "[green]✓[/green]"
285
+ console.print(f"\n{mark} Skill '{slug}':\n")
286
+ for target_name, result in results.items():
287
+ version = f" v{result.version}" if result.version else ""
288
+ line = (
289
+ f" [cyan]{target_name}[/cyan]: "
290
+ f"{_ACTION_STYLE.get(result.action, result.action)} "
291
+ f"[bold]{result.name}[/bold]{version} → {result.path}"
292
+ )
293
+ if result.action == "failed" and getattr(result, "error", None):
294
+ line += f"\n [red]{result.error}[/red]"
295
+ console.print(line)
239
296
 
297
+ if failed:
298
+ handle_error(message=f"Skill '{slug}' failed for: {', '.join(failed)}")
240
299
  console.print("\n[dim]The skill is now available in your AI coding tool(s).[/dim]")
241
300
 
301
+ except typer.Exit:
302
+ raise
242
303
  except Exception as err: # pylint: disable=broad-exception-caught
243
304
  handle_error(message=f"Failed to install skill: {err}")
244
305
 
245
306
 
307
+ class _LegacyInstall: # pylint: disable=too-few-public-methods
308
+ """Install result shape for a qbraid-core without install_skill_ex.
309
+
310
+ That core cannot tell whether anything changed, so the action is reported
311
+ as ``unknown`` rather than claiming ``installed``.
312
+ """
313
+
314
+ def __init__(self, slug: str, path) -> None:
315
+ self.slug = slug
316
+ self.name = slug
317
+ self.path = path
318
+ self.version = None
319
+ self.action = "unknown"
320
+ self.error = None
321
+
322
+
246
323
  @skills_app.command(name="uninstall")
247
324
  def skills_uninstall(
248
- slug: str = typer.Argument(..., help="The skill slug to uninstall"),
325
+ slug: str = typer.Argument(..., help="The skill slug (or installed name) to uninstall"),
249
326
  target: SkillTargetOption = typer.Option(
250
327
  SkillTargetOption.ALL,
251
328
  "--target",
@@ -255,7 +332,8 @@ def skills_uninstall(
255
332
  ) -> None:
256
333
  """Uninstall a skill from AI coding tools.
257
334
 
258
- By default, uninstalls from all supported tools.
335
+ By default, uninstalls from all supported tools. Accepts the registry slug
336
+ or the installed directory name.
259
337
  """
260
338
 
261
339
  def uninstall_skill():
@@ -294,15 +372,28 @@ def skills_installed(
294
372
  help="Show skills for specific tool only",
295
373
  ),
296
374
  ) -> None:
297
- """List locally installed skills."""
375
+ """List locally installed skills.
376
+
377
+ Shows each skill's name, registry slug and version per tool.
378
+ """
298
379
 
299
380
  def list_installed():
300
381
  from qbraid_core.services.skills import SkillsClient
301
382
 
302
383
  client = SkillsClient()
384
+ if hasattr(client, "list_all_installed_skills_detailed"):
385
+ if target:
386
+ return {target.value: client.list_installed_skills_detailed(target=target.value)}
387
+ return client.list_all_installed_skills_detailed()
388
+ # older qbraid-core: directory names only
303
389
  if target:
304
- return {target.value: client.list_installed_skills(target=target.value)}
305
- return client.list_all_installed_skills()
390
+ listed = {target.value: client.list_installed_skills(target=target.value)}
391
+ else:
392
+ listed = client.list_all_installed_skills()
393
+ return {
394
+ name: [{"name": skill, "slug": skill, "version": None} for skill in skills]
395
+ for name, skills in listed.items()
396
+ }
306
397
 
307
398
  console = Console()
308
399
 
@@ -320,8 +411,14 @@ def skills_installed(
320
411
  for target_name, skills in results.items():
321
412
  if skills:
322
413
  console.print(f"[cyan]{target_name}[/cyan]:")
323
- for skill_slug in skills:
324
- console.print(f" • {skill_slug}")
414
+ for entry in skills:
415
+ slug_note = (
416
+ f" [dim]({entry['slug']})[/dim]"
417
+ if entry.get("slug") and entry["slug"] != entry["name"]
418
+ else ""
419
+ )
420
+ version = f" v{entry['version']}" if entry.get("version") else ""
421
+ console.print(f" • {entry['name']}{slug_note}{version}")
325
422
  else:
326
423
  console.print(f"[dim]{target_name}: (none)[/dim]")
327
424
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: qbraid-cli
3
- Version: 0.15.0
3
+ Version: 0.15.1
4
4
  Summary: Command Line Interface for interacting with all parts of the qBraid platform.
5
5
  Author-email: qBraid Development Team <contact@qbraid.com>
6
6
  License-Expression: LicenseRef-Proprietary
File without changes
File without changes
File without changes