Spaces:
Sleeping
Sleeping
| """uofa init β scaffold a new UofA project.""" | |
| from __future__ import annotations | |
| import json | |
| import re | |
| import shutil | |
| from pathlib import Path | |
| from uofa_cli.integrity import generate_keypair | |
| from uofa_cli.output import step_header, result_line, info | |
| from uofa_cli import paths | |
| HELP = "scaffold a new UofA project (template + keys + uofa.toml)" | |
| def add_arguments(parser): | |
| parser.add_argument("name", help="project name (e.g., my-turbine-study)") | |
| parser.add_argument("--profile", choices=["minimal", "complete"], default="complete", | |
| help="starting profile (default: complete)") | |
| parser.add_argument("--dir", type=Path, default=Path("."), help="parent directory (default: cwd)") | |
| parser.add_argument("--provider", default="ollama", | |
| help="LLM provider for uofa extract (default: ollama)") | |
| parser.add_argument("--model", default="llama3.2", | |
| help="LLM model for uofa extract (default: llama3.2)") | |
| def run(args) -> int: | |
| project_dir = args.dir / args.name | |
| if project_dir.exists(): | |
| raise FileExistsError(f"Directory already exists: {project_dir}") | |
| step_header(f"Initializing UofA project: {args.name}") | |
| # ββ Create directories βββββββββββββββββββββββββββββββββββ | |
| project_dir.mkdir(parents=True) | |
| keys_dir = project_dir / "keys" | |
| keys_dir.mkdir() | |
| evidence_dir = project_dir / "evidence" | |
| evidence_dir.mkdir() | |
| (evidence_dir / ".gitkeep").touch() | |
| # ββ Copy and customize JSON-LD template βββββββββββββββββββ | |
| template_name = f"uofa-{'minimal' if args.profile == 'minimal' else 'complete'}-skeleton.jsonld" | |
| template = paths.templates_dir() / template_name | |
| if not template.exists(): | |
| raise FileNotFoundError(f"Template not found: {template}") | |
| with open(template, "r", encoding="utf-8") as f: | |
| content = f.read() | |
| content = content.replace("example.org/my-project", f"example.org/{args.name}") | |
| content = content.replace("YOUR PROJECT NAME", args.name) | |
| content = content.replace( | |
| '"../../spec/context/v0.5.jsonld"', | |
| '"https://raw.githubusercontent.com/cloudronin/uofa/main/spec/context/v0.5.jsonld"' | |
| ) | |
| content = content.replace( | |
| '"../../../spec/context/v0.5.jsonld"', | |
| '"https://raw.githubusercontent.com/cloudronin/uofa/main/spec/context/v0.5.jsonld"' | |
| ) | |
| output_file = project_dir / f"{args.name}-cou1.jsonld" | |
| with open(output_file, "w") as f: | |
| f.write(content) | |
| result_line(f"Template ({args.profile})", True, str(output_file)) | |
| # ββ Generate keypair ββββββββββββββββββββββββββββββββββββββ | |
| key_path = keys_dir / f"{args.name}.key" | |
| key_path, pub_path = generate_keypair(key_path) | |
| result_line("Keypair generated", True, str(key_path.name)) | |
| # ββ Copy Excel template from pack βββββββββββββββββββββββββ | |
| active_pack = paths.resolve_active_packs(args) | |
| pack_name = active_pack[0] if active_pack else "vv40" | |
| xlsx_template = _find_pack_template(pack_name) | |
| if pack_name == "vv40" or pack_name == "core": | |
| xlsx_dest_name = "uofa-template.xlsx" | |
| else: | |
| xlsx_dest_name = f"uofa-template-{pack_name}.xlsx" | |
| if xlsx_template and xlsx_template.exists(): | |
| xlsx_dest = project_dir / xlsx_dest_name | |
| shutil.copy2(xlsx_template, xlsx_dest) | |
| result_line("Excel template", True, xlsx_dest_name) | |
| else: | |
| xlsx_dest_name = "uofa-template.xlsx" | |
| info(" (no Excel template found for this pack)") | |
| # ββ Write uofa.toml ββββββββββββββββββββββββββββββββββββββ | |
| toml_content = _generate_toml( | |
| name=args.name, | |
| pack=pack_name, | |
| profile=args.profile, | |
| template=xlsx_dest_name, | |
| provider=args.provider, | |
| model=args.model, | |
| ) | |
| toml_path = project_dir / "uofa.toml" | |
| toml_path.write_text(toml_content) | |
| result_line("uofa.toml", True) | |
| # ββ Write README.md βββββββββββββββββββββββββββββββββββββββ | |
| readme_content = _generate_readme( | |
| project_name=args.name, | |
| pack_name=pack_name, | |
| template_filename=xlsx_dest_name, | |
| ) | |
| (project_dir / "README.md").write_text(readme_content) | |
| result_line("README.md", True) | |
| # ββ Create .gitignore βββββββββββββββββββββββββββββββββββββ | |
| gitignore = project_dir / ".gitignore" | |
| gitignore.write_text( | |
| "# Private keys β never commit\n" | |
| "*.key\n" | |
| ) | |
| result_line(".gitignore", True) | |
| # ββ Next steps ββββββββββββββββββββββββββββββββββββββββββββ | |
| print() | |
| info("Next steps:") | |
| info(f" 1. Fill in {xlsx_dest_name}") | |
| info(f" 2. Import: cd {args.name} && uofa import") | |
| info(f" 3. Check: uofa check {args.name}-cou1.jsonld") | |
| return 0 | |
| def _find_pack_template(pack_name: str) -> Path | None: | |
| """Find the Excel template for a given pack.""" | |
| try: | |
| manifest = paths.pack_manifest(pack_name) | |
| template_rel = manifest.get("template") | |
| if template_rel: | |
| template_path = paths.pack_dir(pack_name) / template_rel | |
| if template_path.exists(): | |
| return template_path | |
| except (FileNotFoundError, KeyError): | |
| pass | |
| # Fallback: core pack template | |
| try: | |
| manifest = paths.pack_manifest("core") | |
| template_rel = manifest.get("template") | |
| if template_rel: | |
| template_path = paths.pack_dir("core") / template_rel | |
| if template_path.exists(): | |
| return template_path | |
| except (FileNotFoundError, KeyError): | |
| pass | |
| return None | |
| def _generate_toml(name, pack, profile, template, provider, model): | |
| """Generate uofa.toml content.""" | |
| return ( | |
| "# UofA Project Configuration\n" | |
| f"# Generated by: uofa init {name}\n" | |
| "\n" | |
| "[project]\n" | |
| f'name = "{name}"\n' | |
| f'pack = "{pack}"\n' | |
| f'profile = "{profile}"\n' | |
| "\n" | |
| "[paths]\n" | |
| 'output = "."\n' | |
| 'evidence = "evidence"\n' | |
| f'template = "{template}"\n' | |
| "\n" | |
| "[extract]\n" | |
| f'provider = "{provider}"\n' | |
| f'model = "{model}"\n' | |
| ) | |
| _PACK_DISPLAY = { | |
| "vv40": "ASME V&V 40-2018", | |
| "nasa-7009b": "NASA-STD-7009B", | |
| "core": "Core (standards-agnostic)", | |
| } | |
| def _generate_readme(project_name, pack_name, template_filename): | |
| """Generate project README.md content.""" | |
| pack_display = _PACK_DISPLAY.get(pack_name, pack_name) | |
| return ( | |
| f"# {project_name} β UofA Project\n" | |
| "\n" | |
| "## Quick Start\n" | |
| "\n" | |
| "1. **Fill in the Excel template:**\n" | |
| f" Open `{template_filename}` and fill in your assessment data.\n" | |
| "\n" | |
| "2. **Import to JSON-LD:**\n" | |
| " ```bash\n" | |
| f" uofa import {template_filename}\n" | |
| " ```\n" | |
| " (Pack and signing key are read from `uofa.toml` β no flags needed.)\n" | |
| "\n" | |
| "3. **Check your evidence package:**\n" | |
| " ```bash\n" | |
| f" uofa check {project_name}-cou1.jsonld\n" | |
| " ```\n" | |
| "\n" | |
| "4. **Fix any weakeners**, re-import, and check again until clean.\n" | |
| "\n" | |
| "## Project Structure\n" | |
| "\n" | |
| "- `uofa.toml` β Project configuration (pack, profile, paths)\n" | |
| "- `evidence/` β Place your evidence files here\n" | |
| f"- `{template_filename}` β Excel template for your credibility assessment\n" | |
| "- `keys/` β Signing keys (keep `*.key` private, share `*.pub`)\n" | |
| "\n" | |
| "## Commands\n" | |
| "\n" | |
| "All commands auto-detect project settings from `uofa.toml`:\n" | |
| "\n" | |
| "```bash\n" | |
| f"uofa import {template_filename} # Excel -> signed JSON-LD\n" | |
| f"uofa check {project_name}-cou1.jsonld # Full C1+C2+C3 pipeline\n" | |
| f"uofa rules {project_name}-cou1.jsonld # Weakener detection only\n" | |
| "uofa diff cou1.jsonld cou2.jsonld # Compare two COUs\n" | |
| "```\n" | |
| "\n" | |
| f"## Pack: {pack_display}\n" | |
| "\n" | |
| f"This project uses the **{pack_display}** standards pack.\n" | |
| ) | |