test-marketplace-manifest.sh 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #!/usr/bin/env bash
  2. set -euo pipefail
  3. SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
  4. REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
  5. MARKETPLACE="$REPO_ROOT/.agents/plugins/marketplace.json"
  6. python3 - "$MARKETPLACE" "$REPO_ROOT" <<'PY'
  7. import json
  8. import sys
  9. from pathlib import Path
  10. marketplace_path = Path(sys.argv[1])
  11. repo_root = Path(sys.argv[2])
  12. if not marketplace_path.exists():
  13. raise AssertionError(".agents/plugins/marketplace.json must exist")
  14. marketplace = json.loads(marketplace_path.read_text(encoding="utf-8"))
  15. def assert_equal(actual, expected, label):
  16. if actual != expected:
  17. raise AssertionError(f"{label}: expected {expected!r}, got {actual!r}")
  18. assert_equal(marketplace.get("name"), "superpowers-dev", "marketplace name")
  19. assert_equal(
  20. marketplace.get("interface", {}).get("displayName"),
  21. "Superpowers Dev",
  22. "marketplace display name",
  23. )
  24. plugins = marketplace.get("plugins")
  25. if not isinstance(plugins, list):
  26. raise AssertionError("plugins must be a list")
  27. matching_plugins = [plugin for plugin in plugins if plugin.get("name") == "superpowers"]
  28. assert_equal(len(matching_plugins), 1, "superpowers plugin entry count")
  29. plugin = matching_plugins[0]
  30. assert_equal(plugin.get("source"), {"source": "url", "url": "./"}, "plugin source")
  31. assert_equal(
  32. plugin.get("policy"),
  33. {"installation": "AVAILABLE", "authentication": "ON_INSTALL"},
  34. "plugin policy",
  35. )
  36. assert_equal(plugin.get("category"), "Developer Tools", "plugin category")
  37. plugin_manifest = repo_root / ".codex-plugin" / "plugin.json"
  38. if not plugin_manifest.exists():
  39. raise AssertionError(".codex-plugin/plugin.json must exist")
  40. manifest = json.loads(plugin_manifest.read_text(encoding="utf-8"))
  41. assert_equal(manifest.get("name"), plugin.get("name"), "plugin manifest name")
  42. # Codex auto-discovers a plugin's hooks/hooks.json whenever the Codex manifest
  43. # has no `hooks` field: load_plugin_hooks falls back to a hardcoded
  44. # DEFAULT_HOOKS_CONFIG_FILE = "hooks/hooks.json" and registers it. That file is
  45. # the Claude Code SessionStart hook, it is tracked in this repo, and this
  46. # marketplace installs the whole repo root (source url "./"), so on Codex the
  47. # fallback re-registers the SessionStart hook and its install-time trust prompt.
  48. # Declaring an empty inline hooks object ({}) parses as an empty inline hook set
  49. # and suppresses the auto-discovery. An absent field, an empty array ([]), and
  50. # an empty inline list all collapse back to the fallback, so the value must be
  51. # exactly an empty object.
  52. hooks_config = repo_root / "hooks" / "hooks.json"
  53. if not hooks_config.exists():
  54. raise AssertionError("hooks/hooks.json must exist (Claude Code SessionStart hook)")
  55. assert_equal(
  56. manifest.get("hooks"),
  57. {},
  58. "Codex manifest must declare empty hooks {} to suppress hooks/hooks.json auto-discovery",
  59. )
  60. print("Codex marketplace manifest looks good")
  61. PY