Files
magnus919_agent-skills/scripts/gen-codex-plugin.rb
T
Magnus HedemarkandGitHub 0f2aaf1583 feat: add Codex plugin packaging (single-plugin, metadata-only) (#83)
* feat: add Codex plugin packaging (single-plugin, metadata-only)

Adds .codex-plugin/plugin.json with a skills array listing all public
skills, plus .agents/plugins/marketplace.json for one-command install:

  codex plugin marketplace add magnus919/agent-skills
  codex plugin install magnus919

Same pattern as mattpocock/skills — one plugin, explicit skill paths,
no dist/, no curation, no duplication. Bundle-internal helpers excluded
by the shared glob. CI check mode fails if the manifest drifts.

Closes #79

AI-assisted: yes (Jasper/Hermes Agent)

* chore: trigger CI

* chore: regenerate Codex plugin manifest to include neckbeard bundle
2026-07-21 03:13:20 -04:00

108 lines
3.2 KiB
Ruby

#!/usr/bin/env ruby
# frozen_string_literal: true
# Generates and validates the Codex plugin packaging:
# .codex-plugin/plugin.json — single plugin manifest listing all public skills
# .agents/plugins/marketplace.json — one-entry marketplace pointing at repo root
#
# Usage:
# ruby scripts/gen-codex-plugin.rb # validate committed files are current
# ruby scripts/gen-codex-plugin.rb --write # regenerate both files
require "fileutils"
require "json"
require "yaml"
ROOT = File.expand_path("..", __dir__)
PLUGIN_JSON_PATH = File.join(ROOT, ".codex-plugin", "plugin.json")
MARKETPLACE_PATH = File.join(ROOT, ".agents", "plugins", "marketplace.json")
PLUGIN_NAME = "magnus919"
PLUGIN_VERSION = "1.0.0"
# Same glob as gen-claude-marketplace.rb: top-level skills + bundle entrypoints.
# Excludes bundle-internal helpers (bundles/<x>/skills/<sub>/SKILL.md).
PUBLIC_SKILLS = (Dir.glob("#{ROOT}/*/SKILL.md") + Dir.glob("#{ROOT}/bundles/*/SKILL.md")).sort
def build_plugin_json
skill_paths = PUBLIC_SKILLS.map do |skill|
"./#{File.basename(File.dirname(skill))}"
end.sort
{
"name" => PLUGIN_NAME,
"version" => PLUGIN_VERSION,
"description" => "AI agent skills — reusable workflows, protocols, and knowledge packs for agentic systems.",
"author" => {
"name" => "Magnus Hedemark",
"url" => "https://github.com/magnus919"
},
"homepage" => "https://github.com/magnus919/agent-skills",
"repository" => "https://github.com/magnus919/agent-skills",
"license" => "MIT",
"keywords" => %w[agent-skills engineering product research infrastructure methodology],
"skills" => skill_paths
}
end
def build_marketplace
{
"name" => PLUGIN_NAME,
"interface" => {
"displayName" => "Magnus Agent Skills"
},
"plugins" => [
{
"name" => PLUGIN_NAME,
"source" => {
"source" => "local",
"path" => "./"
},
"policy" => {
"installation" => "AVAILABLE",
"authentication" => "ON_INSTALL"
},
"category" => "Developer Tools"
}
]
}
end
def render(obj)
JSON.pretty_generate(obj) + "\n"
end
expected_plugin = render(build_plugin_json)
expected_marketplace = render(build_marketplace)
if ARGV.include?("--write")
FileUtils.mkdir_p(File.dirname(PLUGIN_JSON_PATH))
FileUtils.mkdir_p(File.dirname(MARKETPLACE_PATH))
File.write(PLUGIN_JSON_PATH, expected_plugin)
File.write(MARKETPLACE_PATH, expected_marketplace)
puts "Wrote .codex-plugin/plugin.json and .agents/plugins/marketplace.json (#{PUBLIC_SKILLS.length} skills)."
exit 0
end
errors = []
if !File.file?(PLUGIN_JSON_PATH)
errors << ".codex-plugin/plugin.json is missing."
elsif File.read(PLUGIN_JSON_PATH) != expected_plugin
errors << ".codex-plugin/plugin.json is out of date."
end
if !File.file?(MARKETPLACE_PATH)
errors << ".agents/plugins/marketplace.json is missing."
elsif File.read(MARKETPLACE_PATH) != expected_marketplace
errors << ".agents/plugins/marketplace.json is out of date."
end
if errors.empty?
puts "Codex plugin packaging is current (#{PUBLIC_SKILLS.length} skills)."
else
warn errors.join("\n")
warn "Run: ruby scripts/gen-codex-plugin.rb --write"
exit 1
end