← Files Vibe CodingARCHIVED FILE

scripts/catalog.py

4.23 KB · Oct 3, 2026 · 06:36 UTC

↓ Download file

#!/usr/bin/env python3
"""Search the shipped recipes and regenerate their offline catalog."""

import argparse
import hashlib
import json
import sys
from pathlib import Path
from catalog_site import load_locales, render_catalog, LANGUAGES
from search_index import build_index, match_score

ROOT = Path(__file__).resolve().parents[1]
MODE_NAMES = load_locales()['en']['modes']


def load_catalog():
    return json.loads((ROOT / 'catalog.json').read_text(encoding='utf-8'))


def contained_path(raw):
    p=(ROOT/raw).resolve()
    if not p.is_relative_to(ROOT) or not p.is_file():
        raise ValueError(f'Invalid recipe path: {raw}')
    return p


def search(rows, query='', mode=None, skill=None):
    index = build_index(rows, load_locales(), ROOT)
    result = []
    for row in rows:
        if mode and row['mode'] != mode: continue
        if skill and row['skill'] != skill: continue
        score = match_score(index[row['id']], query)
        if score is not None: result.append((score, row))
    return [row for _, row in sorted(result, key=lambda item: -item[0])]


def render_html(rows):
    return render_catalog(rows)


def refresh():
    catalog=load_catalog()
    english = load_locales()['en']
    for row in catalog['recipes']:
        row['title'] = english['titles'][row['id']]
        row['category'] = english['categories'][row['skill']]
        row['summary'] = english['summaries'][row['skill']]
        row['example'] = english['ui']['prompt'].format(skill=row['skill'], title=row['title'], id=row['id'])
        row['sha256']=hashlib.sha256(contained_path(row['path']).read_bytes()).hexdigest()
    catalog['skills']=len(list((ROOT/'skills').glob('*/SKILL.md')))
    (ROOT/'catalog.json').write_text(json.dumps(catalog,ensure_ascii=False,indent=2)+'\n',encoding='utf-8', newline='\n')
    (ROOT/'CATALOG.html').write_text(render_html(catalog['recipes']),encoding='utf-8', newline='\n')
    entries = ['# Vibe Coding catalog', '', 'Choose the requested domain and operation. Each link opens its focused skill. In Codex use `$skill-name`; in Claude Code use `/vibe-coding:skill-name`.', '', '| Skill | When to use | Workflows |', '|---|---|---|']
    for skill in sorted({r['skill'] for r in catalog['recipes']}):
        count = sum(r['skill'] == skill for r in catalog['recipes'])
        entries.append(f"| [`{skill}`](../skills/{skill}/SKILL.md) | {english['summaries'][skill]} | {count} |")
    (ROOT/'references/catalog.md').write_text('\n'.join(entries)+'\n', encoding='utf-8', newline='\n')
    print(f'Refreshed {len(catalog["recipes"])} recipes; wrote CATALOG.html')




def main():
    parser=argparse.ArgumentParser(description=__doc__)
    sub=parser.add_subparsers(dest='command',required=True)
    p=sub.add_parser('search');p.add_argument('query',nargs='?',default='');p.add_argument('--mode',choices=MODE_NAMES);p.add_argument('--skill');p.add_argument('--json',action='store_true');p.add_argument('--lang',choices=LANGUAGES,default='en');p.add_argument('--host',choices=['codex','claude'],default='codex')
    p=sub.add_parser('show');p.add_argument('id')
    sub.add_parser('refresh')
    args=parser.parse_args()
    if args.command=='refresh':refresh();return
    rows=load_catalog()['recipes']
    if args.command=='show':
        matches=[r for r in rows if r['id']==args.id]
        if not matches:parser.error('Unknown recipe ID. Use search first.')
        print(contained_path(matches[0]['path']).read_text(encoding='utf-8'));return
    rows=search(rows,args.query,args.mode,args.skill)
    locale = load_locales()[args.lang]
    rows = [{**r, 'title': locale['titles'][r['id']], 'category': locale['categories'][r['skill']], 'summary': locale['summaries'][r['skill']], 'example': locale['ui']['promptClaude' if args.host=='claude' else 'prompt'].format(skill=r['skill'], title=locale['titles'][r['id']], id=r['id'])} for r in rows]
    if args.json:print(json.dumps(rows,ensure_ascii=False,indent=2))
    else:
        for row in rows:
            command = '/vibe-coding:'+row['skill'] if args.host=='claude' else '$'+row['skill']
            print(f'{row["id"]} | {command} | {locale["modes"][row["mode"]]}\n  {row["title"]} — {row["category"]}')
        print(f'Found: {len(rows)}')


if __name__=='__main__':
    sys.stdout.reconfigure(encoding='utf-8')
    main()

SHA-256: db48db6661a443af848d3b357528949ab70b3f68c7a40404157f9f54152cca14