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 +1 -1
- commands/clean.py +14 -139
- commands/registry.py +144 -0
- {oasr-0.4.1.dist-info → oasr-0.4.2.dist-info}/METADATA +1 -1
- {oasr-0.4.1.dist-info → oasr-0.4.2.dist-info}/RECORD +9 -9
- {oasr-0.4.1.dist-info → oasr-0.4.2.dist-info}/WHEEL +0 -0
- {oasr-0.4.1.dist-info → oasr-0.4.2.dist-info}/entry_points.txt +0 -0
- {oasr-0.4.1.dist-info → oasr-0.4.2.dist-info}/licenses/LICENSE +0 -0
- {oasr-0.4.1.dist-info → oasr-0.4.2.dist-info}/licenses/NOTICE +0 -0
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.
|
|
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
|
|
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(
|
|
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
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
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
|
-
|
|
139
|
-
|
|
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
|
|
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,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=
|
|
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=
|
|
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=
|
|
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.
|
|
51
|
-
oasr-0.4.
|
|
52
|
-
oasr-0.4.
|
|
53
|
-
oasr-0.4.
|
|
54
|
-
oasr-0.4.
|
|
55
|
-
oasr-0.4.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|