From b7842234c02f8963a2b947823c0e59331d39f9af Mon Sep 17 00:00:00 2001 From: Julian lechner Date: Thu, 17 Sep 2026 15:27:03 +0200 Subject: [PATCH] feat(prompts): inventory skill metadata and duplicate names --- scripts/lib/prompt-inventory.py | 47 ++++++++++++++++++++++++++++++++ scripts/test-prompt-inventory.py | 29 ++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 scripts/lib/prompt-inventory.py create mode 100644 scripts/test-prompt-inventory.py diff --git a/scripts/lib/prompt-inventory.py b/scripts/lib/prompt-inventory.py new file mode 100644 index 0000000..6f62373 --- /dev/null +++ b/scripts/lib/prompt-inventory.py @@ -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() diff --git a/scripts/test-prompt-inventory.py b/scripts/test-prompt-inventory.py new file mode 100644 index 0000000..245fea0 --- /dev/null +++ b/scripts/test-prompt-inventory.py @@ -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()