@remixmate/cli 0.9.63 → 0.9.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/cli.js +13 -0
  2. package/dist/handlers/index.js +2 -0
  3. package/dist/handlers/template-init.d.ts +10 -0
  4. package/dist/handlers/template-init.js +21 -0
  5. package/dist/manifest.json +53 -3
  6. package/dist/template/args.d.ts +25 -0
  7. package/dist/template/args.js +69 -0
  8. package/dist/template/check.d.ts +23 -0
  9. package/dist/template/check.js +25 -0
  10. package/dist/template/commands.d.ts +25 -0
  11. package/dist/template/commands.js +146 -0
  12. package/dist/template/constants.d.ts +25 -0
  13. package/dist/template/constants.js +32 -0
  14. package/dist/template/init.d.ts +22 -0
  15. package/dist/template/init.js +115 -0
  16. package/dist/template/list.d.ts +23 -0
  17. package/dist/template/list.js +46 -0
  18. package/dist/template/preview.d.ts +60 -0
  19. package/dist/template/preview.js +318 -0
  20. package/dist/template/proc.d.ts +26 -0
  21. package/dist/template/proc.js +43 -0
  22. package/dist/template/project.d.ts +37 -0
  23. package/dist/template/project.js +110 -0
  24. package/dist/template/publish.d.ts +43 -0
  25. package/dist/template/publish.js +182 -0
  26. package/dist/template/render-client.d.ts +39 -0
  27. package/dist/template/render-client.js +79 -0
  28. package/package.json +1 -1
  29. package/skills/template-authoring/SKILL.md +69 -0
  30. package/skills/template-authoring/skill.json +32 -0
  31. package/skills/template-authoring/version.json +7 -0
  32. package/skills/template-registry/SKILL.md +2 -2
  33. package/skills/template-registry/scripts/list_templates.py +46 -13
  34. package/skills/template-registry/scripts/registry_loader.py +17 -0
  35. package/skills/template-registry/skill.json +1 -1
@@ -72,9 +72,10 @@ Flags:
72
72
  (samples, layouts); also opens the browser
73
73
  when stdout is an interactive terminal
74
74
 
75
- Samples (showcase_loader.py): list and detail views add each template's sample
76
- count and web detail-page link when ab-api serves them; when it does not, the
77
- views are unchanged apart from one stderr warning.
75
+ Links & samples: list and detail views add each template's web detail-page
76
+ link (registry document-level ``pageUrls``, falling back to the samples
77
+ endpoint) and sample count (showcase_loader.py) when ab-api serves them; when
78
+ it does not, the views are unchanged apart from one stderr warning.
78
79
  """
79
80
 
80
81
  from __future__ import annotations
@@ -94,6 +95,7 @@ from registry_loader import ( # noqa: E402
94
95
  RegistryUnreachableError,
95
96
  get_template,
96
97
  list_templates as load_visible_templates,
98
+ registry_page_urls,
97
99
  )
98
100
  from showcase_loader import load_showcases, summarize_group # noqa: E402
99
101
 
@@ -198,6 +200,23 @@ def _open_in_browser(url: str) -> None:
198
200
  pass
199
201
 
200
202
 
203
+ def _page_url(template_id: object, page_urls: dict[str, str], groups: dict | None) -> str | None:
204
+ """Web detail page link for one template.
205
+
206
+ The registry's document-level ``pageUrls`` comes first: it rides on the call
207
+ every list makes anyway, so the link survives the samples endpoint being
208
+ down. The samples group's ``pageUrl`` is the fallback (same URL, built by
209
+ the same backend helper).
210
+ """
211
+ if not isinstance(template_id, str):
212
+ return None
213
+ if page_urls.get(template_id):
214
+ return page_urls[template_id]
215
+ group = (groups or {}).get(template_id)
216
+ url = group.get("pageUrl") if isinstance(group, dict) else None
217
+ return url if isinstance(url, str) and url else None
218
+
219
+
201
220
  def _report_unknown_ids(missing: list[str], templates: list[dict]) -> None:
202
221
  for tid in missing:
203
222
  # Distinguish "gated out by status" from "does not exist" —
@@ -459,6 +478,7 @@ def main() -> None:
459
478
  sys.exit(1)
460
479
 
461
480
  by_id = {t.get("templateId"): t for t in templates}
481
+ page_urls = registry_page_urls()
462
482
 
463
483
  # ----- open mode (--open) -------------------------------------------------
464
484
  #
@@ -468,8 +488,9 @@ def main() -> None:
468
488
  if args.open_id not in by_id:
469
489
  _report_unknown_ids([args.open_id], templates)
470
490
  sys.exit(1)
471
- groups = load_showcases([args.open_id], 1)
472
- page_url = (groups or {}).get(args.open_id, {}).get("pageUrl")
491
+ page_url = _page_url(args.open_id, page_urls, None)
492
+ if not page_url:
493
+ page_url = _page_url(args.open_id, {}, load_showcases([args.open_id], 1))
473
494
  if not page_url:
474
495
  print(
475
496
  "❌ No web detail page link for this template: the backend does not "
@@ -520,9 +541,16 @@ def main() -> None:
520
541
  # chars), which alone pushed the plain call past the tool-result cap and
521
542
  # got it spilled to a file the agent cannot read. Humans can `| jq`.
522
543
  detail: dict = {"templates": picked}
523
- # Samples sit beside the definitions, never inside them: the definition
524
- # is the template's authoring contract, samples are web content.
544
+ # Samples and links sit beside the definitions, never inside them: the
545
+ # definition is the template's authoring contract, these are web content.
525
546
  groups = load_showcases([str(t.get("templateId")) for t in picked], _DETAIL_SHOWCASES)
547
+ links = {
548
+ str(t.get("templateId")): url
549
+ for t in picked
550
+ if (url := _page_url(t.get("templateId"), page_urls, groups))
551
+ }
552
+ if links:
553
+ detail["pageUrls"] = links
526
554
  if groups is not None:
527
555
  detail["showcases"] = {
528
556
  tid: summarize_group(g, _DETAIL_SHOWCASES) for tid, g in groups.items()
@@ -554,6 +582,8 @@ def main() -> None:
554
582
  summaries = []
555
583
  for t in visible:
556
584
  s = _summarize(t)
585
+ if url := _page_url(t.get("templateId"), page_urls, groups):
586
+ s["pageUrl"] = url
557
587
  # Absent (not null) when the endpoint is unavailable, so a consumer
558
588
  # never mistakes "unknown" for "zero samples".
559
589
  if groups is not None and t.get("templateId") in groups:
@@ -576,6 +606,7 @@ def main() -> None:
576
606
  return
577
607
 
578
608
  groups = load_showcases([str(t.get("templateId")) for t in visible], _LIST_SHOWCASES)
609
+ any_link = False
579
610
 
580
611
  header = (
581
612
  f"\n{'Template ID':<22} {'Name':<18} {'Aspect':<12} "
@@ -596,12 +627,14 @@ def main() -> None:
596
627
  hint = _truncate(tpl.get("llmHint"), _SUMMARY_HINT_CHARS)
597
628
  if hint:
598
629
  print(f"{' ' * 22} ↳ {hint}")
630
+ # Two lines, link on its own: an agent summarizing the table keeps a
631
+ # labelled `detail:` line, a URL trailing a count reads as an aside.
599
632
  group = (groups or {}).get(tpl.get("templateId"))
600
633
  if group is not None:
601
- line = f"samples: {int(group.get('showcaseCount') or 0)}"
602
- if group.get("pageUrl"):
603
- line += f" · {group['pageUrl']}"
604
- print(f"{' ' * 22} ↳ {line}")
634
+ print(f"{' ' * 22} ↳ samples: {int(group.get('showcaseCount') or 0)}")
635
+ if url := _page_url(tpl.get("templateId"), page_urls, groups):
636
+ print(f"{' ' * 22} ↳ detail: {url}")
637
+ any_link = True
605
638
 
606
639
  print(f"\n{len(visible)} template(s) shown")
607
640
  first_id = str(visible[0].get("templateId", "<templateId>"))
@@ -609,8 +642,8 @@ def main() -> None:
609
642
  f"Full definition of one template: --template-id {first_id} --json-output "
610
643
  "(llmHint / customPayloadSchema / slideSchemas / slotMapping / variants)",
611
644
  )
612
- if groups:
613
- print(f"Watch samples in the browser: --open {first_id}")
645
+ if any_link:
646
+ print(f"Watch samples / layouts in the browser: open a detail link, or --open {first_id}")
614
647
 
615
648
 
616
649
  if __name__ == "__main__":
@@ -306,6 +306,23 @@ def load_registry_data(
306
306
  return _load_from_url(url, ttl=ttl_seconds, timeout=http_timeout)
307
307
 
308
308
 
309
+ def registry_page_urls() -> dict[str, str]:
310
+ """Document-level ``pageUrls`` (templateId → web detail page), built by ab-api.
311
+
312
+ Kept beside ``templates`` rather than inside each definition: a definition is
313
+ the template's authoring contract, a link is site information. Absent on
314
+ older backends, offline registries, or when the backend has no site address
315
+ configured — returns ``{}`` then (never raises: it is display-only).
316
+ """
317
+ try:
318
+ raw = load_registry_data().get("pageUrls")
319
+ except Exception: # noqa: BLE001 — the caller already loaded the registry once
320
+ return {}
321
+ if not isinstance(raw, dict):
322
+ return {}
323
+ return {k: v for k, v in raw.items() if isinstance(k, str) and isinstance(v, str) and v}
324
+
325
+
309
326
  # ════════════════════════════════════════════════════════════════════════════
310
327
  # Indexed views — O(1) template-id lookup + status / beta gating
311
328
  #
@@ -19,7 +19,7 @@
19
19
  "filter_aspect": { "type": "string", "description": "Show only templates that declare this aspect ratio. E.g. '9:16' / '16:9' / '1:1'." },
20
20
  "filter_language": { "type": "string", "description": "Show only templates whose contentLanguage includes this code ('zh' or 'en'). Templates with no declared language are always shown (treated as language-agnostic)." },
21
21
  "include_beta": { "type": "boolean", "description": "Also list templates with status='beta'. Default off (only 'stable' shows). The ENABLE_BETA_TEMPLATES env var has the same effect process-wide." },
22
- "json_output": { "type": "boolean", "description": "Emit a JSON result instead of the human-readable table: { templates: [...] } — summaries, or full definitions when template_id / full is set — or { examples: [...] }. When the backend serves samples, list summaries carry showcases { count, pageUrl, items } and the template_id view carries a top-level showcases map." },
22
+ "json_output": { "type": "boolean", "description": "Emit a JSON result instead of the human-readable table: { templates: [...] } — summaries, or full definitions when template_id / full is set — or { examples: [...] }. List summaries carry pageUrl (the template's web detail page — show it to the user as a link) and, when the backend serves samples, showcases { count, pageUrl, items }; the template_id view carries top-level pageUrls / showcases maps." },
23
23
  "open": { "type": "string", "description": "Print the web detail page URL of <templateId> (playable samples, layouts, copy-ready prompt) and open it in the browser when run from an interactive terminal. Relay the printed URL to the user." }
24
24
  },
25
25
  "required": []