npm run sync— Sync template bundles (run after editing templates/ or bundles.json)npm run sync:templates/npm run i18n— Hub i18n:index.json→index.{locale}.json(NOT MCP)npm run sync:bundles— Regenerate manifest and copy assets to packagesnpm run mcp— Syncindex.json→index.mcp.json(MCP tool index)npm run mcp:check— MCP sync dry-runnpm run mcp:ai— AI English descriptions for stale MCP templatesnpm run mcp:models— AI model profiles inmodels_registry.jsonnpm run validate:templates— Validate template JSON filesnpm run validate:manifests— Validate package manifestsnpm run validate:comfyui-nodes— Compare templates to ComfyUI node baseline (local: live/object_info)python scripts/sync/sync_bundles.py— Same asnpm run sync:bundlespython scripts/sync/sync_is_app.py— WriteisAppintoindex.jsonfrom each workflow'sextra.linearMode(--dry-runto report,--checkfor the CI gate)python scripts/validate/validate_templates.py— Same asnpm run validate:templatespython scripts/comfyui_node_compat/check.py --static-scan --clone-comfyui --no-fail— CI-style static compat scanpython scripts/sync/sync_frozen_inventory.py— Regenerate frozen bundle template inventory frombundles.jsonpython scripts/ci/check_frozen_policy.py --base-ref origin/main— Dry-run frozen-bundle PR reminder locally
MCP index pipeline: see skill .claude/skills/managing-mcp-index/SKILL.md and scripts/mcp/docs/MCP_AI_ENHANCEMENT.md. Do not confuse with hub i18n (i18n) or site AI (site/scripts/generate-ai.ts).
Frozen legacy media bundles: see scripts/docs/frozen_bundles.md (policy, inventory, CI reminders, publishing).
Sole entry point: scripts/maintenance/archive_templates.py (full docstring in that file; also listed in scripts/README.md).
Archive a template:
- Add
"status": "archived"to the template entry intemplates/index.json - Run
python3 scripts/maintenance/archive_templates.py
The script moves the workflow JSON and thumbnails to archived/, removes the entry from bundles.json and all templates/index*.json files, moves i18n to archived/archived_i18n.json, and adds entries to archived/index*.json (including MCP).
Restore a template:
- Set
"status": "active"on the template inarchived/index.json - Run the same script (restore runs first, then any pending archives)
After archive or restore: if bundles.json frozen-bundle rows changed, run python scripts/sync/sync_frozen_inventory.py. See scripts/docs/frozen_bundles.md — archive does not require a legacy media wheel bump unless you intend a PyPI release.
- Monorepo with Nx, Python packages, and Astro site
templates/— Source workflow JSON files and thumbnails (index.json is the manifest)packages/— Python packages: core (loader + manifest), json (all workflow JSON), media_* (legacy frozen assets), media_assets_* (new assets)site/— Astro static site (independently managed; see below)scripts/— Python validation/sync scripts for CI and local dev (seescripts/README.md)
Do not confuse with site/scripts/ — that is a separate TypeScript toolchain for the Astro site. Site CI never uses root scripts/.
Root scripts/ is organized by role:
| Directory | Put here | Examples |
|---|---|---|
scripts/sync/ |
Sync / generate data | sync_data.py, sync_bundles.py |
scripts/mcp/ |
MCP index pipeline | sync_index.py, enhance_descriptions.py |
scripts/validate/ |
Validation & analysis (CI) | validate_templates.py, check_links.py, analyze_models.py |
scripts/comfyui_node_compat/ |
ComfyUI node baseline vs templates | check.py |
scripts/blueprints/ |
Blueprint-specific import | import_blueprints.py |
scripts/ci/ |
Release pipeline only | ci_version_manager.py, check_frozen_policy.py, check_pypi_quota.py |
scripts/data/ |
Static config JSON | i18n.json, whitelist.json, version_policy.json, frozen_bundle_inventory.json, mcp/* |
scripts/lib/ |
Shared importable modules | paths.py, locale_index_files.py, ai/ |
scripts/maintenance/ |
Local-only / one-off tools | archive_templates.py, check_templates.sh |
scripts/docs/ |
Script-specific markdown docs | whitelist.md, check_input_assets.md |
| Old path | New path |
|---|---|
scripts/sync_bundles.py |
scripts/sync/sync_bundles.py |
scripts/sync_data.py |
scripts/sync/sync_data.py |
scripts/validate_templates.py |
scripts/validate/validate_templates.py |
scripts/i18n.json |
scripts/data/i18n.json |
scripts/whitelist.json |
scripts/data/whitelist.json |
scripts/locale_index_files.py |
scripts/lib/locale_index_files.py |
scripts/ci_version_manager.py |
scripts/ci/ci_version_manager.py |
scripts/sync-mcp-index.py |
scripts/mcp/sync_index.py |
Full index and CI mapping: scripts/README.md.
New Python CLI script
- Place it in the correct subdirectory (
sync/,validate/,maintenance/, etc.) — not thescripts/root. - Use
snake_case.pynaming (no hyphens). - Derive repo root via
scripts/lib/paths.py— do not hardcodescripts/i18n.jsonor rebuild paths fromPath(__file__).parent. - If the script lives in a subdirectory, use
REPO_ROOTfrompaths.pyinstead ofPath(__file__).parents[1].
Importing shared modules from scripts/lib/
import sys
from pathlib import Path
_lib_dir = Path(__file__).resolve().parent.parent / "lib"
if str(_lib_dir) not in sys.path:
sys.path.insert(0, str(_lib_dir))
from paths import REPO_ROOT, TEMPLATES_DIR, I18N_FILE, WHITELIST_FILE # noqa: E402New config / data JSON → scripts/data/. Update scripts/lib/paths.py if other scripts need the path.
New shared Python module (not a CLI) → scripts/lib/. Add __init__.py if needed; keep modules import-only.
Generated output → scripts/.output/ (gitignored) or repo root reports (model_analysis_report.md). Never commit generated JSON into scripts/data/.
CI workflow changes — when a workflow uses sparse-checkout and the script imports scripts/lib/paths.py, include both the script path and scripts/lib/paths.py (and any scripts/data/* it reads). Update on.*.paths triggers to match the new file location.
Docs & references — update paths in: .github/workflows/, .claude/skills/, docs/, packages/core/tests/ (if loading scripts via importlib), tools/project.json, root package.json scripts, and skills that mention commands.
Tests loading scripts — use the new path, e.g. REPO_ROOT / "scripts" / "sync" / "sync_bundles.py".
The site/ directory is an independent Astro 5 project with Vue 3 interactive islands.
For full site-specific instructions, see site/AGENTS.md.
Island architecture summary:
.astrofor static/SSR content and data fetching —.vuewithclient:loadfor interactive UI- Serialize content collections to plain JSON objects before passing as props to Vue islands
- Each
client:loadVue component is a separate Vue app —provide/inject/$emitdon't cross islands - Cross-island state: shared composables in
site/src/composables/with module-levelref()singletons - Astro→Vue bridge: Vue island attaches
addEventListenerto Astro DOM elements by ID inonMounted()— no<script>tags withdispatchEventin.astrofiles
- Python: Ruff linter, line-length 100, target py312. Select rules: E, F
- Templates: JSON workflow files with embedded model metadata. Thumbnails named
{template}-1.webp - Naming: snake_case for Python/templates
- Bump root
pyproject.tomlversion only when intentionally releasing to PyPI (releaselabel). Template-only / archive PRs usually leave the root version unchanged.
- All Vue components use
<script setup lang="ts">with Composition API only — no Options API, no mixins - Cross-component communication via shared composables (
site/src/composables/) with module-level reactive refs — NEVER usedocument.dispatchEvent(new CustomEvent(...))or event bus patterns between Vue components - Astro-to-Vue bridge: attach listeners to specific DOM elements by ID in
onMounted(), not via inline<script>tags withdispatchEvent - Props via
defineProps<T>(), emits viadefineEmits<T>(), reactivity viaref(),computed(),watch()