← Files Product & App ArchitectARCHIVED FILE

scripts/validate_package.py

2.62 KB · Nov 11, 2026 · 12:42 UTC

↓ Download file

See the change to this file →

from pathlib import Path
import json,re,zipfile,sys
ROOT=Path(__file__).resolve().parents[1]
issues=[];passes=0

def check(ok,msg):
    global passes
    if ok:passes+=1
    else:issues.append(msg)

p=json.loads((ROOT/'plugin.json').read_text())
check(p['name']=='product-app-architect','Identity changed')
check(p['version']=='0.2.0','Version mismatch')
check(p.get('author',{}).get('name')=='Tekris Labs','Publisher mismatch')
check('mcp.json' not in [x.name for x in ROOT.iterdir()],'Unexpected MCP backend')
ui=p['extensions']['com.openai']['interface']
check(len(ui['displayName'])<=30,'Display name too long')
check(len(ui['shortDescription'])<=30,'Subtitle too long')
check(len(ui['longDescription'])<=4000,'Long description too long')
check(len(ui['defaultPrompt'])==3,'Starter prompts not 3')
for prompt in ui['defaultPrompt']:check(0<len(prompt)<=128,'Starter prompt exceeds limits')
check(len(ui['capabilities'])<=20,'Too many capabilities')
for cap in ui['capabilities']:check(0<len(cap)<=120,'Capability too long')
for n in ('websiteURL','supportURL','privacyPolicyURL','termsOfServiceURL'):
    check(str(ui.get(n,'')).startswith('https://tekrislabs.com/'),f'Missing publisher URL: {n}')
for n in ('logo','composerIcon'):
    x=ui[n].removeprefix('./')
    check((ROOT/x).is_file(),f'Missing asset: {n}')
    check('..' not in Path(x).parts,f'Unsafe asset path: {n}')
check((ROOT/p['extensions']['com.openai']['onboardingSkill'].removeprefix('./')).is_file(),'Onboarding skill missing')
for f in sorted((ROOT/'skills').glob('*/SKILL.md')):
    c=f.read_text()
    check(bool(re.match(r'^---\nname: [a-z0-9-]+\ndescription: .+\n---\n',c)),f'Invalid skill frontmatter: {f}')
    for target in re.findall(r'`(\.\./\.\./(?:references|templates)/[^`]+)`',c):
        check((f.parent/target).resolve().is_file(),f'Broken relative link: {f.name} {target}')
check(len(list((ROOT/'skills').glob('*/SKILL.md')))==17,'Unexpected skills count')
check(len(list((ROOT/'references/original').glob('*.md')))==7,'Original technical source missing')
check(len(list((ROOT/'templates').glob('*.md')))==10,'Missing templates')
check(len(json.loads((ROOT/'benchmarks/cases.json').read_text())['cases'])==24,'Wrong benchmark count')
legacy=json.loads((ROOT/'.codex-plugin/plugin.json').read_text())
check(legacy['name']==p['name'] and legacy['version']==p['version'],'Legacy identity mismatch')
check(legacy['interface']['developerName']=='Tekris Labs','Legacy publisher mismatch')
check(legacy['interface']['defaultPrompt']==ui['defaultPrompt'],'Prompt mismatch')
print(f'{passes} structural checks passed; {len(issues)} failed')
for item in issues: print('FAIL:',item)
sys.exit(bool(issues))

SHA-256: 0ff6ce98ea98d13e546688fac86ebe2698a13c4ee060fc618c4e301acdedd4b8