oasr 0.4.1__py3-none-any.whl → 0.4.2__py3-none-any.whl

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.
cli.py CHANGED
@@ -13,7 +13,7 @@ from pathlib import Path
13
13
  from commands import adapter, clean, clone, config, diff, exec, find, registry, sync, update, use, validate
14
14
  from commands import help as help_cmd
15
15
 
16
- __version__ = "0.4.1"
16
+ __version__ = "0.4.2"
17
17
 
18
18
 
19
19
  def main(argv: list[str] | None = None) -> int:
commands/clean.py CHANGED
@@ -1,16 +1,16 @@
1
- """`asr clean` command."""
1
+ """`asr clean` command - DEPRECATED, use `oasr registry prune` instead."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
5
  import argparse
6
- import json
7
-
8
- from manifest import check_manifest, delete_manifest, list_manifests, load_manifest
9
- from registry import load_registry, remove_skill
6
+ import sys
10
7
 
11
8
 
12
9
  def register(subparsers) -> None:
13
- p = subparsers.add_parser("clean", help="Clean up corrupted/missing skills and orphaned artifacts")
10
+ p = subparsers.add_parser(
11
+ "clean",
12
+ help="(Deprecated) Clean up corrupted/missing skills - use 'oasr registry prune'",
13
+ )
14
14
  p.add_argument("-y", "--yes", action="store_true", help="Skip confirmation prompt")
15
15
  p.add_argument("--json", action="store_true", help="Output in JSON format")
16
16
  p.add_argument("--dry-run", action="store_true", help="Show what would be cleaned without doing it")
@@ -18,138 +18,13 @@ def register(subparsers) -> None:
18
18
 
19
19
 
20
20
  def run(args: argparse.Namespace) -> int:
21
- entries = load_registry()
22
- registered_names = {e.name for e in entries}
23
- manifest_names = set(list_manifests())
24
-
25
- to_remove_skills = []
26
- to_remove_manifests = []
27
-
28
- # Check for remote skills and show progress header
29
- import sys
30
-
31
- from skillcopy.remote import is_remote_source
32
-
33
- remote_count = 0
34
- for entry in entries:
35
- manifest = load_manifest(entry.name)
36
- if manifest and is_remote_source(manifest.source_path):
37
- remote_count += 1
38
-
39
- if remote_count > 0 and not args.json:
40
- print(f"Checking {remote_count} remote skill(s)...", file=sys.stderr)
41
-
42
- for entry in entries:
43
- manifest = load_manifest(entry.name)
44
- if manifest:
45
- # Show progress for remote skills
46
- is_remote = is_remote_source(manifest.source_path)
47
- if is_remote and not args.json:
48
- platform = (
49
- "GitHub"
50
- if "github.com" in manifest.source_path
51
- else "GitLab"
52
- if "gitlab.com" in manifest.source_path
53
- else "remote"
54
- )
55
- print(f" ↓ {entry.name} (checking {platform}...)", file=sys.stderr, flush=True)
56
-
57
- status = check_manifest(manifest)
58
-
59
- if is_remote and not args.json:
60
- print(f" ✓ {entry.name} (checked)", file=sys.stderr)
61
-
62
- if status.status == "missing":
63
- to_remove_skills.append(
64
- {
65
- "name": entry.name,
66
- "reason": "source missing",
67
- "path": entry.path,
68
- }
69
- )
70
-
71
- orphaned = manifest_names - registered_names
72
- for name in orphaned:
73
- to_remove_manifests.append(
74
- {
75
- "name": name,
76
- "reason": "orphaned manifest (not in registry)",
77
- }
78
- )
79
-
80
- if not to_remove_skills and not to_remove_manifests:
81
- if args.json:
82
- print(json.dumps({"cleaned": 0, "message": "nothing to clean"}))
83
- else:
84
- print("Nothing to clean.")
85
- return 0
86
-
87
- if args.json:
88
- result = {
89
- "skills_to_remove": to_remove_skills,
90
- "manifests_to_remove": to_remove_manifests,
91
- "dry_run": args.dry_run,
92
- }
93
- if not args.dry_run and not args.yes:
94
- result["requires_confirmation"] = True
95
- print(json.dumps(result, indent=2))
96
- if args.dry_run:
97
- return 0
98
- else:
99
- print("The following will be cleaned:\n")
100
-
101
- if to_remove_skills:
102
- print("Skills with missing sources:")
103
- for s in to_remove_skills:
104
- print(f" ✗ {s['name']} ({s['path']})")
105
-
106
- if to_remove_manifests:
107
- print("\nOrphaned manifests:")
108
- for m in to_remove_manifests:
109
- print(f" ✗ {m['name']}")
110
-
111
- print()
112
-
113
- if args.dry_run:
114
- print("(dry run - no changes made)")
115
- return 0
116
-
117
- if not args.yes and not args.json:
118
- try:
119
- response = input("Proceed with cleanup? [y/N] ").strip().lower()
120
- if response not in ("y", "yes"):
121
- print("Aborted.")
122
- return 1
123
- except (EOFError, KeyboardInterrupt):
124
- print("\nAborted.")
125
- return 1
126
-
127
- removed_skills = []
128
- removed_manifests = []
129
-
130
- for s in to_remove_skills:
131
- remove_skill(s["name"])
132
- removed_skills.append(s["name"])
133
-
134
- for m in to_remove_manifests:
135
- delete_manifest(m["name"])
136
- removed_manifests.append(m["name"])
21
+ """Delegate to registry prune with deprecation warning."""
22
+ # Show deprecation warning unless --json or --quiet
23
+ if not args.json:
24
+ print("⚠ Warning: 'oasr clean' is deprecated. Use 'oasr registry prune' instead.", file=sys.stderr)
25
+ print(" This command will be removed in v0.6.0.\n", file=sys.stderr)
137
26
 
138
- if args.json:
139
- print(
140
- json.dumps(
141
- {
142
- "removed_skills": removed_skills,
143
- "removed_manifests": removed_manifests,
144
- },
145
- indent=2,
146
- )
147
- )
148
- else:
149
- for name in removed_skills:
150
- print(f"Removed skill: {name}")
151
- for name in removed_manifests:
152
- print(f"Removed manifest: {name}")
153
- print(f"\nCleaned {len(removed_skills)} skill(s), {len(removed_manifests)} manifest(s)")
27
+ # Delegate to registry prune
28
+ from commands.registry import run_prune
154
29
 
155
- return 0
30
+ return run_prune(args)
commands/registry.py CHANGED
@@ -64,6 +64,13 @@ def register(subparsers) -> None:
64
64
  sync_p.add_argument("--config", type=Path, help="Override config file path")
65
65
  sync_p.set_defaults(func=run_sync)
66
66
 
67
+ # registry prune
68
+ prune_p = registry_subparsers.add_parser("prune", help="Clean up corrupted/missing skills and orphaned artifacts")
69
+ prune_p.add_argument("-y", "--yes", action="store_true", help="Skip confirmation prompt")
70
+ prune_p.add_argument("--json", action="store_true", help="Output in JSON format")
71
+ prune_p.add_argument("--dry-run", action="store_true", help="Show what would be cleaned without doing it")
72
+ prune_p.set_defaults(func=run_prune)
73
+
67
74
 
68
75
  def run_validate(args: argparse.Namespace) -> int:
69
76
  """Validate registry manifests (default oasr registry behavior)."""
@@ -301,3 +308,140 @@ def run_sync(args: argparse.Namespace) -> int:
301
308
  print(f"{len(pruned)} skill(s) pruned")
302
309
 
303
310
  return 1 if missing_count > 0 else 0
311
+
312
+
313
+ def run_prune(args: argparse.Namespace) -> int:
314
+ """Clean up corrupted/missing skills and orphaned artifacts (oasr registry prune)."""
315
+ from manifest import delete_manifest, list_manifests
316
+
317
+ entries = load_registry()
318
+ registered_names = {e.name for e in entries}
319
+ manifest_names = set(list_manifests())
320
+
321
+ to_remove_skills = []
322
+ to_remove_manifests = []
323
+
324
+ # Check for remote skills and show progress header
325
+ remote_count = 0
326
+ for entry in entries:
327
+ manifest = load_manifest(entry.name)
328
+ if manifest and is_remote_source(manifest.source_path):
329
+ remote_count += 1
330
+
331
+ if remote_count > 0 and not args.json:
332
+ print(f"Checking {remote_count} remote skill(s)...", file=sys.stderr)
333
+
334
+ for entry in entries:
335
+ manifest = load_manifest(entry.name)
336
+ if manifest:
337
+ # Show progress for remote skills
338
+ is_remote = is_remote_source(manifest.source_path)
339
+ if is_remote and not args.json:
340
+ platform = (
341
+ "GitHub"
342
+ if "github.com" in manifest.source_path
343
+ else "GitLab"
344
+ if "gitlab.com" in manifest.source_path
345
+ else "remote"
346
+ )
347
+ print(f" ↓ {entry.name} (checking {platform}...)", file=sys.stderr, flush=True)
348
+
349
+ status = check_manifest(manifest)
350
+
351
+ if is_remote and not args.json:
352
+ print(f" ✓ {entry.name} (checked)", file=sys.stderr)
353
+
354
+ if status.status == "missing":
355
+ to_remove_skills.append(
356
+ {
357
+ "name": entry.name,
358
+ "reason": "source missing",
359
+ "path": entry.path,
360
+ }
361
+ )
362
+
363
+ orphaned = manifest_names - registered_names
364
+ for name in orphaned:
365
+ to_remove_manifests.append(
366
+ {
367
+ "name": name,
368
+ "reason": "orphaned manifest (not in registry)",
369
+ }
370
+ )
371
+
372
+ if not to_remove_skills and not to_remove_manifests:
373
+ if args.json:
374
+ print(json.dumps({"cleaned": 0, "message": "nothing to clean"}))
375
+ else:
376
+ print("Nothing to clean.")
377
+ return 0
378
+
379
+ if args.json:
380
+ result = {
381
+ "skills_to_remove": to_remove_skills,
382
+ "manifests_to_remove": to_remove_manifests,
383
+ "dry_run": args.dry_run,
384
+ }
385
+ if not args.dry_run and not args.yes:
386
+ result["requires_confirmation"] = True
387
+ print(json.dumps(result, indent=2))
388
+ if args.dry_run:
389
+ return 0
390
+ else:
391
+ print("The following will be cleaned:\n")
392
+
393
+ if to_remove_skills:
394
+ print("Skills with missing sources:")
395
+ for s in to_remove_skills:
396
+ print(f" ✗ {s['name']} ({s['path']})")
397
+
398
+ if to_remove_manifests:
399
+ print("\nOrphaned manifests:")
400
+ for m in to_remove_manifests:
401
+ print(f" ✗ {m['name']}")
402
+
403
+ print()
404
+
405
+ if args.dry_run:
406
+ print("(dry run - no changes made)")
407
+ return 0
408
+
409
+ if not args.yes and not args.json:
410
+ try:
411
+ response = input("Proceed with cleanup? [y/N] ").strip().lower()
412
+ if response not in ("y", "yes"):
413
+ print("Aborted.")
414
+ return 1
415
+ except (EOFError, KeyboardInterrupt):
416
+ print("\nAborted.")
417
+ return 1
418
+
419
+ removed_skills = []
420
+ removed_manifests = []
421
+
422
+ for s in to_remove_skills:
423
+ remove_skill(s["name"])
424
+ removed_skills.append(s["name"])
425
+
426
+ for m in to_remove_manifests:
427
+ delete_manifest(m["name"])
428
+ removed_manifests.append(m["name"])
429
+
430
+ if args.json:
431
+ print(
432
+ json.dumps(
433
+ {
434
+ "removed_skills": removed_skills,
435
+ "removed_manifests": removed_manifests,
436
+ },
437
+ indent=2,
438
+ )
439
+ )
440
+ else:
441
+ for name in removed_skills:
442
+ print(f"Removed skill: {name}")
443
+ for name in removed_manifests:
444
+ print(f"Removed manifest: {name}")
445
+ print(f"\nCleaned {len(removed_skills)} skill(s), {len(removed_manifests)} manifest(s)")
446
+
447
+ return 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oasr
3
- Version: 0.4.1
3
+ Version: 0.4.2
4
4
  Summary: CLI for managing agent skills across IDE integrations
5
5
  Project-URL: Homepage, https://github.com/jgodau/asr
6
6
  Project-URL: Repository, https://github.com/jgodau/asr
@@ -1,7 +1,7 @@
1
1
  __init__.py,sha256=cYuwXNht5J2GDPEbHz57rmXRyWzaUgAaCXz8okR0rKE,84
2
2
  __main__.py,sha256=Due_Us-4KNlLZhf8MkmoP1hWS5qMWmpZvz2ZaCqPHT4,120
3
3
  adapter.py,sha256=WEpYkKDTb7We0zU9i6Z-r5ydtUdghNhxTZ5Eq58h4fU,10027
4
- cli.py,sha256=J8DqiaLGoUpKOWHMfkOBkQoC-R01ro4zTh2JAjjWVlo,2672
4
+ cli.py,sha256=gb2A9vxiD4q67oRynUJr9W0vHUHm02S2qR56SwAU1MM,2672
5
5
  discovery.py,sha256=WWF8SN2LH88mOUBJLavM7rvXcxi6uDQGpqRK20GysxA,3298
6
6
  manifest.py,sha256=feNCjkFWfhoVubevKjLtKoIEuzT1YGQn6wWgs9XM8_o,12229
7
7
  registry.py,sha256=zGutwVP39xaYqc3KDEXMWCV1tORYpqc5JISO8OaWP1Q,4470
@@ -25,7 +25,7 @@ agents/registry.py,sha256=i6YUAdNUXq1LNqLDNqSg2RlHxqF5Ig5Z40ryteXoLu4,1434
25
25
  commands/__init__.py,sha256=g_ZxSKLVZwCVAPpn-Ga_gj53BS2473SOg72ivGph--U,147
26
26
  commands/adapter.py,sha256=_68v3t-dRU0mszzL4udKs1bKennyg7RfBTaK2fDGTsE,3215
27
27
  commands/add.py,sha256=NJLQ-8-3zy7o6S9VLfL_wauP-Vz0oNGwN3nvtiwxNYM,15255
28
- commands/clean.py,sha256=KtC5RJRdSBuz7ekEryMHOvHbqzV2vxUlp8_Mv-4b9TU,4893
28
+ commands/clean.py,sha256=RQBAfe6iCLsjMqUyVR55JdYX9MBqgrUuIrA8rFKs1J0,1102
29
29
  commands/clone.py,sha256=4APH34-yHjiXQIQwBnKOSEQ_sxV24_GKypcOJMfncvs,5912
30
30
  commands/config.py,sha256=PKuOX7CPRAy2j5NG1rhHYDFJT1XvZnOTF2qJW04v34Q,4940
31
31
  commands/diff.py,sha256=37JMjvfAEfvK7-4X5iFbD-IGkS8ae4YSY7ZDIZF5B9E,5766
@@ -34,7 +34,7 @@ commands/find.py,sha256=zgqwUnaG5aLX6gJIU2ZeQzxsFh2s7oDNNtmV-e-62Jg,1663
34
34
  commands/help.py,sha256=5yhIpgGs1xPs2f39lg-ELE7D0tV_uUTjxQsgkWusIwo,1449
35
35
  commands/info.py,sha256=zywaUQsrvcPXcX8W49P7Jqnr90pX8nBPqnH1XcIs0Uk,4396
36
36
  commands/list.py,sha256=P3E_PItNoqAStNTcLCY7dV-Db2_Yb7TRCYXgP4G19rQ,3185
37
- commands/registry.py,sha256=7bPnt7kuiXsCHKUTr29zikXky5socN3jPEl9H1D2WVA,11280
37
+ commands/registry.py,sha256=nerDoR0Uvd_2WlDMQZshJunMW31HsENklAy19A00x4U,16065
38
38
  commands/rm.py,sha256=bOPXgifAbgx7kp4ZuNanJbv4wP1l1sIsI8vD6Rlgi8g,4160
39
39
  commands/status.py,sha256=8si3iEVu0AUE2qojSCVoU0BLwpLm4UiFyY-Ln7Uo36k,3944
40
40
  commands/sync.py,sha256=ZQoB5hBqrzvM6LUQVlKqHQVJib4dB5qe5M-pVG2vtGM,4946
@@ -47,9 +47,9 @@ config/schema.py,sha256=KUebMCokJkymPyNcT2Bwm5BKwXk0SVGdoW24PRNX350,1387
47
47
  skillcopy/__init__.py,sha256=YUglUkDzKfnCt4ar_DU33ksI9fGyn2UYbV7qn2c_BcU,2322
48
48
  skillcopy/local.py,sha256=QH6484dCenjg8pfNOyTRbQQBklEWhkkTnfQok5ssf_4,1049
49
49
  skillcopy/remote.py,sha256=83jRA2VfjtSDGO-YM1x3WGJjKvWzK1RmSTL7SdUOz8s,3155
50
- oasr-0.4.1.dist-info/METADATA,sha256=Fl64OJMF77F8Q3BfgvUmJFqVNx-2AzXpk7giGf5nmHo,17924
51
- oasr-0.4.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
52
- oasr-0.4.1.dist-info/entry_points.txt,sha256=VnMuOi6XYMbzAD2bP0X5uV1sQXjOqoDWJ33Lsxwq8u8,52
53
- oasr-0.4.1.dist-info/licenses/LICENSE,sha256=nQ1j9Ldb8FlJ-z7y2WuXPIlyfnYC7YPasjGdOBgcfP4,10561
54
- oasr-0.4.1.dist-info/licenses/NOTICE,sha256=EsfkCN0ZRDS0oj3ADvMKeKrAXaPlC8YfpSjvjGVv9jE,207
55
- oasr-0.4.1.dist-info/RECORD,,
50
+ oasr-0.4.2.dist-info/METADATA,sha256=kzX9QzNlvAXMGO7RuiMpXUBbKj0Zj8LgdobXV3leyoE,17924
51
+ oasr-0.4.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
52
+ oasr-0.4.2.dist-info/entry_points.txt,sha256=VnMuOi6XYMbzAD2bP0X5uV1sQXjOqoDWJ33Lsxwq8u8,52
53
+ oasr-0.4.2.dist-info/licenses/LICENSE,sha256=nQ1j9Ldb8FlJ-z7y2WuXPIlyfnYC7YPasjGdOBgcfP4,10561
54
+ oasr-0.4.2.dist-info/licenses/NOTICE,sha256=EsfkCN0ZRDS0oj3ADvMKeKrAXaPlC8YfpSjvjGVv9jE,207
55
+ oasr-0.4.2.dist-info/RECORD,,
File without changes