feat(prompts): inventory skill metadata and duplicate names
This commit is contained in:
1 parent
6ce4fcc848
commit
b7842234c0
2 files changed
+76
No files matched your search
@@ -0,0 +1,47 @@
|
|||||||
|
import argparse
|
||||||
|
from collections import Counter
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
def inventory(roots):
|
||||||
|
names = Counter()
|
||||||
|
count = metadata_bytes = body_bytes = 0
|
||||||
|
for root in roots:
|
||||||
|
for path in sorted(root.rglob('SKILL.md')):
|
||||||
|
content = path.read_text(encoding='utf-8-sig')
|
||||||
|
count += 1
|
||||||
|
body_bytes += len(content.encode('utf-8'))
|
||||||
|
header = re.match(r'\A---\r?\n(.*?)^---\s*$', content, re.MULTILINE | re.DOTALL)
|
||||||
|
if header:
|
||||||
|
metadata_bytes += len(header.group(1).encode('utf-8'))
|
||||||
|
name = re.search(r'^name:\s*(.+)$', header.group(1), re.MULTILINE)
|
||||||
|
if name:
|
||||||
|
names[name.group(1).strip().strip('\"\'')] += 1
|
||||||
|
return count, metadata_bytes, body_bytes, sorted(name for name, total in names.items() if total > 1)
|
||||||
|
|
||||||
|
|
||||||
|
def report(label, roots, warn_duplicates=True):
|
||||||
|
count, metadata, bodies, duplicates = inventory(roots)
|
||||||
|
print(f'{label}: {count} skill files, {metadata} frontmatter bytes (~{(metadata + 3) // 4} tokens), {bodies} lazy file bytes')
|
||||||
|
if duplicates and warn_duplicates:
|
||||||
|
print(f'WARN {label}: repeated skill names ({len(duplicates)}): {", ".join(duplicates)}')
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument('--home', type=Path, default=Path.home())
|
||||||
|
args = parser.parse_args()
|
||||||
|
root = Path(__file__).resolve().parents[2]
|
||||||
|
for client in ('codex', 'claude'):
|
||||||
|
report(f'Generated {client} catalog', [root / 'generated' / f'{client}-bash' / 'skills'])
|
||||||
|
directories = [args.home / f'.{client}' / 'skills']
|
||||||
|
if client == 'codex':
|
||||||
|
directories.append(args.home / '.agents' / 'skills')
|
||||||
|
report(f'Installed {client} user catalog', directories)
|
||||||
|
report(f'Cached {client} plugins (all versions)', [args.home / f'.{client}' / 'plugins' / 'cache'], warn_duplicates=False)
|
||||||
|
print('Coverage: baseline covers generated instructions only. Inventory is not measured session context; plugin caches include inactive versions. Client prompts, tool schemas and dynamic hook output are excluded. Token counts use bytes/4 estimates.')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
SPEC = importlib.util.spec_from_file_location('prompt_inventory', Path(__file__).parent / 'lib/prompt-inventory.py')
|
||||||
|
MODULE = importlib.util.module_from_spec(SPEC)
|
||||||
|
SPEC.loader.exec_module(MODULE)
|
||||||
|
|
||||||
|
|
||||||
|
class PromptInventoryTests(unittest.TestCase):
|
||||||
|
def test_counts_metadata_separately_and_reports_duplicate_names(self):
|
||||||
|
with TemporaryDirectory() as temporary:
|
||||||
|
root = Path(temporary)
|
||||||
|
metadata = 'name: "example"\ndescription: Example skill.\n'
|
||||||
|
for name in ('first', 'second'):
|
||||||
|
directory = root / name
|
||||||
|
directory.mkdir()
|
||||||
|
(directory / 'SKILL.md').write_text(f'---\n{metadata}---\n\nBody\n---\nMore body', encoding='utf-8-sig')
|
||||||
|
count, header_bytes, body_bytes, duplicates = MODULE.inventory([root, root / 'missing'])
|
||||||
|
self.assertEqual(count, 2)
|
||||||
|
self.assertEqual(header_bytes, 2 * len(metadata.encode()))
|
||||||
|
self.assertGreater(body_bytes, header_bytes)
|
||||||
|
self.assertEqual(duplicates, ['example'])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
||||||
Reference in new issue
Block a user