📦 deps(thirdparty): update snapshots

This commit is contained in:
ci[bot]
2026-07-13 16:04:08 +00:00
parent b3cc57caff
commit 82f7c6e56a
265 changed files with 24975 additions and 16612 deletions
+220 -20
View File
@@ -41,7 +41,9 @@ function parseArgs(argv = process.argv.slice(2)) {
let riskArg = null;
let categoryArg = null;
let tagsArg = null;
let skillsArg = null;
let versionInfo = false;
let dryRun = false;
let cursor = false,
claude = false,
gemini = false,
@@ -56,7 +58,7 @@ function parseArgs(argv = process.argv.slice(2)) {
versionInfo = true;
continue;
}
if (["--path", "--release", "--tag", "--risk", "--category", "--tags"].includes(a[i])) {
if (["--path", "--release", "--tag", "--risk", "--category", "--tags", "--skills"].includes(a[i])) {
const value = a[i + 1];
if (!value || value.startsWith("--")) {
throw new Error(`Option ${a[i]} requires a value.`);
@@ -67,9 +69,14 @@ function parseArgs(argv = process.argv.slice(2)) {
if (a[i] === "--risk") riskArg = value;
if (a[i] === "--category") categoryArg = value;
if (a[i] === "--tags") tagsArg = value;
if (a[i] === "--skills") skillsArg = value;
i += 1;
continue;
}
if (a[i] === "--dry-run") {
dryRun = true;
continue;
}
if (a[i] === "--cursor") {
cursor = true;
continue;
@@ -109,7 +116,9 @@ function parseArgs(argv = process.argv.slice(2)) {
riskArg,
categoryArg,
tagsArg,
skillsArg,
versionInfo,
dryRun,
cursor,
claude,
gemini,
@@ -179,6 +188,8 @@ Options:
--risk <csv> Install only skills matching these risk labels
--category <csv> Install only skills matching these categories
--tags <csv> Install only skills matching these tags
--skills <csv> Set exact managed skill names, ids, or nested skill paths
--dry-run Preview installs/updates/removals for every target without writing
--version Print the installer version
--release <ver> Clone tag v<ver> (e.g. 4.6.0 -> v4.6.0)
--tag <tag> Clone this tag or branch (e.g. v4.6.0, main)
@@ -191,6 +202,7 @@ Examples:
npx agentic-awesome-skills --agy
npx agentic-awesome-skills --path .agents/skills --category development,backend --risk safe,none
npx agentic-awesome-skills --path .agents/skills --tags debugging,typescript-legacy-
npx agentic-awesome-skills --codex --skills frontend-design,game-development/2d-games --dry-run
npx agentic-awesome-skills --release 4.6.0
npx agentic-awesome-skills --path ./my-skills
npx agentic-awesome-skills --claude --codex Install to multiple targets
@@ -230,6 +242,19 @@ function parseSelectorArg(raw) {
};
}
function parseExactSkillArg(raw) {
if (typeof raw !== "string" || !raw.trim()) {
return [];
}
const values = raw.split(",").map((value) => value.trim());
if (values.some((value) => !value)) {
throw new Error("--skills must be a comma-separated list of non-empty exact skill names, ids, or paths.");
}
return uniqueValues(values);
}
function hasActiveSelector(selector) {
return selector.include.length > 0 || selector.exclude.length > 0;
}
@@ -422,21 +447,51 @@ function replaceManagedEntry(
}
/** Copy contents of repo's skills/ into target so each skill is target/skill-name/ (for Claude Code etc.). */
function getInstallEntries(tempDir, selectors = buildInstallSelectors({})) {
function resolveExactSkillSelections(repoSkills, skillEntries, requestedSkills = []) {
if (requestedSkills.length === 0) {
return null;
}
const skills = skillEntries.map((skillId) => readSkill(repoSkills, skillId));
const resolvedEntries = new Set();
for (const requestedSkill of requestedSkills) {
const matches = skills.filter((skill) => (
skill.name === requestedSkill ||
skill.id === requestedSkill ||
path.basename(skill.id) === requestedSkill
));
if (matches.length === 0) {
throw new Error(`Unknown skill requested by --skills: ${requestedSkill}`);
}
if (matches.length > 1) {
throw new Error(
`Ambiguous skill requested by --skills: ${requestedSkill}. Use one exact nested skill path: ${matches.map((skill) => skill.id).join(", ")}`,
);
}
resolvedEntries.add(matches[0].id);
}
return resolvedEntries;
}
function getInstallEntries(tempDir, selectors = buildInstallSelectors({}), requestedSkills = []) {
const repoSkills = path.join(tempDir, "skills");
if (!fs.existsSync(repoSkills)) {
console.error("Cloned repo has no skills/ directory.");
process.exit(1);
throw new Error("Cloned repo has no skills/ directory.");
}
const skillEntries = listSkillIdsRecursive(repoSkills);
const filteredEntries = hasInstallSelectors(selectors)
? skillEntries.filter((skillId) => matchesInstallSelectors(readSkill(repoSkills, skillId), selectors))
: skillEntries;
const selectedEntries = resolveExactSkillSelections(repoSkills, skillEntries, requestedSkills);
const filteredEntries = skillEntries.filter((skillId) => (
(!selectedEntries || selectedEntries.has(skillId)) &&
(!hasInstallSelectors(selectors) || matchesInstallSelectors(readSkill(repoSkills, skillId), selectors))
));
if (hasInstallSelectors(selectors) && filteredEntries.length === 0) {
console.error("No skills matched the requested --risk/--category/--tags filters.");
process.exit(1);
if ((selectedEntries || hasInstallSelectors(selectors)) && filteredEntries.length === 0) {
throw new Error("No skills matched the requested --skills/--risk/--category/--tags selection.");
}
const entries = [...filteredEntries];
@@ -550,6 +605,22 @@ function readInstallManifest(targetPath) {
}
}
function normalizeManifestEntries(entries) {
const normalized = [];
const invalid = [];
for (const entry of entries) {
try {
normalized.push(normalizeInstallEntry(entry));
} catch (error) {
invalid.push(entry);
}
}
return {
entries: uniqueValues(normalized).sort(),
invalid: uniqueValues(invalid).sort(),
};
}
function writeInstallManifest(targetPath, installEntries) {
const manifestPath = resolveInstallManifestPath(targetPath);
const normalizedEntries = [...new Set(installEntries.map(normalizeInstallEntry).filter(Boolean))].sort();
@@ -572,14 +643,17 @@ function writeInstallManifest(targetPath, installEntries) {
function pruneRemovedEntries(targetPath, previousEntries, installEntries) {
const next = new Set(installEntries.map(normalizeInstallEntry));
for (const entry of previousEntries) {
const normalizedEntry = normalizeInstallEntry(entry);
const normalizedPrevious = normalizeManifestEntries(previousEntries);
for (const entry of normalizedPrevious.invalid) {
console.warn(` Skipping unsafe managed entry path from manifest: ${entry}`);
}
for (const normalizedEntry of normalizedPrevious.entries) {
if (next.has(normalizedEntry)) {
continue;
}
const candidate = resolveManagedPath(targetPath, entry);
const candidate = resolveManagedPath(targetPath, normalizedEntry);
if (!candidate) {
console.warn(` Skipping unsafe managed entry path from manifest: ${entry}`);
console.warn(` Skipping unsafe managed entry path from manifest: ${normalizedEntry}`);
continue;
}
assertSafeDestinationPath(candidate, targetPath);
@@ -658,7 +732,16 @@ function resolveInstallRef(opts) {
return DEFAULT_RELEASE_REF;
}
function installForTarget(tempDir, target, selectors = buildInstallSelectors({})) {
function installForTarget(
tempDir,
target,
selectors = buildInstallSelectors({}),
installEntries = null,
requestedSkills = [],
) {
// Resolve all selection errors before creating or changing the target.
const resolvedInstallEntries = installEntries || getInstallEntries(tempDir, selectors, requestedSkills);
if (fs.existsSync(target.path)) {
ensureTargetIsDirectory(target.path);
const targetStats = fs.lstatSync(target.path);
@@ -698,10 +781,9 @@ function installForTarget(tempDir, target, selectors = buildInstallSelectors({})
fs.mkdirSync(target.path, { recursive: true });
}
const installEntries = getInstallEntries(tempDir, selectors);
const managedEntries = getManagedEntries(installEntries, target);
const managedEntries = getManagedEntries(resolvedInstallEntries, target);
const previousEntries = readInstallManifest(target.path);
installSkillsIntoTarget(tempDir, target.path, installEntries);
installSkillsIntoTarget(tempDir, target.path, resolvedInstallEntries);
pruneRemovedEntries(target.path, previousEntries, managedEntries);
writeInstallManifest(target.path, managedEntries);
console.log(` ✓ Installed to ${target.path}`);
@@ -737,7 +819,7 @@ function getPostInstallMessages(targets, selectors = buildInstallSelectors({}))
if (targets.some((target) => isOpenCodeStylePath(target.path))) {
const baseMessage =
"For Antigravity 2.0, OpenCode, or other .agents/skills installs, prefer a reduced install with --risk, --category, or --tags to avoid context overload.";
"For Antigravity 2.0, OpenCode, or other .agents/skills installs, prefer a reduced install with --skills, --risk, --category, or --tags to avoid context overload.";
messages.push(baseMessage);
if (!hasInstallSelectors(selectors)) {
messages.push(
@@ -749,6 +831,82 @@ function getPostInstallMessages(targets, selectors = buildInstallSelectors({}))
return messages;
}
function buildDryRunTargetPlan(target, installEntries) {
const desiredEntries = uniqueValues(getManagedEntries(installEntries, target)).sort();
const targetExists = fs.existsSync(target.path);
if (targetExists) {
const stats = fs.lstatSync(target.path);
if (stats.isSymbolicLink()) {
throw new Error(`Refusing to preview through symlinked target: ${target.path}`);
}
if (!stats.isDirectory()) {
throw new Error(`Install path exists but is not a directory: ${target.path}`);
}
}
const previous = normalizeManifestEntries(readInstallManifest(target.path));
const desiredSet = new Set(desiredEntries);
const remove = previous.entries.filter((entry) => !desiredSet.has(entry));
// Match the apply-time destination checks without creating any path. This
// makes the preview fail before a later install could encounter a symlinked
// managed destination or an unsafe stale manifest entry.
for (const entry of [...desiredEntries, ...remove]) {
const candidate = resolveManagedPath(target.path, entry);
if (!candidate) {
throw new Error(`Refusing unsafe managed entry in dry-run plan: ${entry}`);
}
assertSafeDestinationPath(candidate, target.path);
}
return {
name: target.name,
path: target.path,
targetExists,
replacesRepositoryClone: targetExists && fs.existsSync(path.join(target.path, ".git")),
installOrUpdate: desiredEntries,
remove,
ignoredUnsafeManifestEntries: previous.invalid,
};
}
function buildDryRunPlan(ref, targets, installEntries) {
return {
ref: ref || "default release",
targets: targets.map((target) => buildDryRunTargetPlan(target, installEntries)),
skills: installEntries.filter((entry) => entry !== "docs").sort(),
};
}
function printDryRunPlan(plan) {
console.log("\nDry run: no target files or directories will be created, changed, or removed.");
console.log(`Ref: ${plan.ref}`);
console.log(`Exact skill set (${plan.skills.length}):`);
for (const skill of plan.skills) {
console.log(` ${skill}`);
}
console.log("Target mutation plans:");
for (const target of plan.targets) {
console.log(` ${target.name}: ${target.path}`);
console.log(` target: ${target.targetExists ? "existing directory" : "will be created"}`);
if (target.replacesRepositoryClone) {
console.log(" migration: existing repository clone will be backed up and replaced");
}
console.log(` install/update managed entries (${target.installOrUpdate.length}):`);
for (const entry of target.installOrUpdate) {
console.log(` ${entry}`);
}
console.log(` remove stale managed entries (${target.remove.length}):`);
for (const entry of target.remove) {
console.log(` ${entry}`);
}
for (const entry of target.ignoredUnsafeManifestEntries) {
console.log(` ignored unsafe manifest entry: ${entry}`);
}
}
}
function main() {
let opts;
try {
@@ -759,6 +917,14 @@ function main() {
return;
}
const selectors = buildInstallSelectors(opts);
let requestedSkills;
try {
requestedSkills = parseExactSkillArg(opts.skillsArg);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exitCode = 1;
return;
}
const ref = resolveInstallRef(opts);
if (opts.help) {
@@ -788,10 +954,38 @@ function main() {
}
run("git", buildCloneArgs(REPO, tempDir, ref));
// Resolve the exact set once before touching any target. This keeps an
// unknown/ambiguous --skills value or an empty filter intersection atomic
// across multi-target installs.
let installEntries;
try {
installEntries = getInstallEntries(tempDir, selectors, requestedSkills);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exitCode = 1;
return;
}
// Preflight every target before mutating the first one. The same plan is
// printed for --dry-run and acts as the multi-target safety gate for apply.
let dryRunPlan;
try {
dryRunPlan = buildDryRunPlan(ref, targets, installEntries);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exitCode = 1;
return;
}
if (opts.dryRun) {
printDryRunPlan(dryRunPlan);
return;
}
console.log(`\nInstalling for ${targets.length} target(s):`);
for (const target of targets) {
console.log(`\n${target.name}:`);
installForTarget(tempDir, target, selectors);
installForTarget(tempDir, target, selectors, installEntries);
}
for (const message of getPostInstallMessages(targets, selectors)) {
@@ -821,6 +1015,8 @@ module.exports = {
replaceManagedEntry,
getPostInstallMessages,
buildCloneArgs,
buildDryRunPlan,
buildDryRunTargetPlan,
buildInstallSelectors,
getInstallEntries,
getManagedEntries,
@@ -831,10 +1027,14 @@ module.exports = {
main,
matchesInstallSelectors,
normalizeInstallEntry,
normalizeManifestEntries,
parseExactSkillArg,
parseSelectorArg,
printDryRunPlan,
parseArgs,
pruneRemovedEntries,
readInstallManifest,
resolveExactSkillSelections,
resolveInstallRef,
writeInstallManifest,
};
@@ -17,6 +17,8 @@
"mixedFiles": [
"README.md",
"package.json",
"apps/web-app/index.html",
"apps/web-app/public/llms.txt",
"docs/users/getting-started.md",
"docs/users/bundles.md",
"docs/users/claude-code-skills.md",
@@ -831,6 +831,18 @@ def coerce_metadata_text(value):
return value
return str(value)
def coerce_metadata_list(value):
if isinstance(value, set):
values = [coerce_metadata_text(item) for item in sorted(value, key=str)]
elif isinstance(value, (list, tuple)):
values = [coerce_metadata_text(item) for item in value]
elif isinstance(value, str):
values = value.split(",") if "," in value else value.split()
else:
return []
return list(dict.fromkeys(item.strip() for item in values if item and item.strip()))
def parse_frontmatter(content):
"""
Parses YAML frontmatter, sanitizing unquoted values containing @.
@@ -936,6 +948,11 @@ def generate_index(skills_dir, output_file, compatibility_report=None):
license_source = coerce_metadata_text(metadata.get("license_source"))
date_added = coerce_metadata_text(metadata.get("date_added"))
category = coerce_metadata_text(metadata.get("category"))
tags_value = metadata.get("tags")
nested_metadata = metadata.get("metadata")
if tags_value is None and isinstance(nested_metadata, Mapping):
tags_value = nested_metadata.get("tags")
tags = coerce_metadata_list(tags_value)
if name is not None:
skill_info["name"] = name
@@ -955,6 +972,8 @@ def generate_index(skills_dir, output_file, compatibility_report=None):
skill_info["license_source"] = license_source
if date_added is not None:
skill_info["date_added"] = date_added
if tags:
skill_info["tags"] = tags
# Category: prefer frontmatter, then folder structure, then conservative inference
if category is not None:
@@ -174,12 +174,15 @@ function prepareRelease(projectRoot, version) {
console.log(`[release] package.json already set to ${version}; keeping current version.`);
}
runReleaseSuite(projectRoot);
runCommand(
"npm",
["run", "sync:metadata", "--", "--refresh-volatile"],
projectRoot,
);
// Volatile metadata is an input to catalog timestamps. Refresh it before the
// canonical release sync so the tagged tree is exactly what publish CI will
// regenerate and verify.
runReleaseSuite(projectRoot);
const refreshedReleaseNotes = ensureChangelogSection(projectRoot, version);
const notesPath = writeReleaseNotes(projectRoot, version, refreshedReleaseNotes);
@@ -655,6 +655,238 @@ def _materialize_plugin_skills(root: Path, destination_root: Path, skill_ids: li
_copy_skill_directory(root, skill_id, destination_root)
def _skill_tree_files(root: Path, skill_ids: list[str]) -> dict[str, Path]:
files: dict[str, Path] = {}
skills_root = root / "skills"
resolved_skills_root = skills_root.resolve()
def collect(source_path: Path, relative_path: Path) -> None:
resolved_source = source_path.resolve(strict=True)
resolved_source.relative_to(resolved_skills_root)
if resolved_source.is_dir():
for child in resolved_source.iterdir():
collect(child, relative_path / child.name)
return
files[relative_path.as_posix()] = resolved_source
for skill_id in skill_ids:
source_root = skills_root / skill_id
if not source_root.is_dir():
raise ValueError(f"Expected canonical skill directory is missing: {skill_id}")
for child in source_root.iterdir():
collect(child, Path(skill_id) / child.name)
return files
def _assert_skill_mirror_matches(
root: Path,
destination_root: Path,
skill_ids: list[str],
label: str,
) -> None:
if destination_root.is_symlink():
raise ValueError(f"{label} skills directory must not be a symlink: {destination_root}")
if not destination_root.is_dir():
raise ValueError(f"{label} skills directory is missing: {destination_root}")
expected_files = _skill_tree_files(root, skill_ids)
actual_files = {
path.relative_to(destination_root).as_posix(): path
for path in destination_root.rglob("*")
if path.is_file()
}
mirrored_symlinks = sorted(
path.relative_to(destination_root).as_posix()
for path in destination_root.rglob("*")
if path.is_symlink()
)
if mirrored_symlinks:
raise ValueError(f"{label} contains unexpected symlink: {mirrored_symlinks[0]}")
expected_paths = set(expected_files)
actual_paths = set(actual_files)
missing = sorted(expected_paths - actual_paths)
if missing:
raise ValueError(f"{label} is missing mirrored file: {missing[0]}")
unexpected = sorted(actual_paths - expected_paths)
if unexpected:
raise ValueError(f"{label} contains unexpected mirrored file: {unexpected[0]}")
for relative_path in sorted(expected_paths):
if expected_files[relative_path].read_bytes() != actual_files[relative_path].read_bytes():
raise ValueError(f"{label} contains stale mirrored file: {relative_path}")
def _assert_json_matches(
path: Path,
expected: dict[str, Any],
label: str,
allowed_root: Path,
) -> None:
relative_path = path.relative_to(allowed_root)
current_path = allowed_root
for part in relative_path.parts:
current_path /= part
if current_path.is_symlink():
raise ValueError(f"{label} path must not contain a symlink: {current_path}")
if not path.is_file():
raise ValueError(f"{label} is missing: {path}")
expected_content = (json.dumps(expected, indent=2, ensure_ascii=False) + "\n").encode("utf-8")
if path.read_bytes() != expected_content:
raise ValueError(f"{label} is out of sync: {path}")
def _assert_plugin_metadata_layout(
plugin_root: Path,
expected_relative_paths: set[str],
label: str,
) -> None:
if plugin_root.is_symlink():
raise ValueError(f"{label} root must not be a symlink: {plugin_root}")
if not plugin_root.is_dir():
raise ValueError(f"{label} root is missing: {plugin_root}")
actual_relative_paths = {
path.relative_to(plugin_root).as_posix()
for path in plugin_root.rglob("*")
if path.is_file() and "skills" not in path.relative_to(plugin_root).parts[:1]
}
unexpected_symlinks = sorted(
path.relative_to(plugin_root).as_posix()
for path in plugin_root.rglob("*")
if path.is_symlink() and "skills" not in path.relative_to(plugin_root).parts[:1]
)
if unexpected_symlinks:
raise ValueError(f"{label} contains unexpected metadata symlink: {unexpected_symlinks[0]}")
if actual_relative_paths != expected_relative_paths:
missing = sorted(expected_relative_paths - actual_relative_paths)
unexpected = sorted(actual_relative_paths - expected_relative_paths)
detail = f"missing {missing[0]}" if missing else f"unexpected {unexpected[0]}"
raise ValueError(f"{label} metadata layout is out of sync: {detail}")
def check_editorial_bundle_plugins(
root: Path,
metadata: dict[str, Any],
bundles: list[dict[str, Any]],
compatibility: dict[str, dict[str, Any]],
) -> None:
bundle_support = {
bundle["id"]: _bundle_target_status(bundle, compatibility)
for bundle in bundles
}
codex_skill_ids = _supported_skill_ids(compatibility, "codex")
claude_skill_ids = _supported_skill_ids(compatibility, "claude")
_assert_json_matches(
root / CODEX_MARKETPLACE_PATH,
_render_codex_marketplace(bundles, bundle_support),
"Codex marketplace",
root,
)
_assert_json_matches(
root / CLAUDE_MARKETPLACE_PATH,
_render_claude_marketplace(metadata, bundles, bundle_support),
"Claude marketplace",
root,
)
_assert_json_matches(
root / CLAUDE_PLUGIN_PATH,
_root_claude_plugin_manifest(metadata, len(claude_skill_ids)),
"root Claude plugin manifest",
root,
)
codex_root = root / "plugins" / ROOT_CODEX_PLUGIN_NAME
claude_root = root / "plugins" / ROOT_CLAUDE_PLUGIN_DIRNAME
_assert_json_matches(
codex_root / ".codex-plugin" / "plugin.json",
_root_codex_plugin_manifest(metadata, len(codex_skill_ids)),
"root Codex plugin manifest",
root,
)
_assert_json_matches(
claude_root / ".claude-plugin" / "plugin.json",
_root_claude_plugin_manifest(metadata, len(claude_skill_ids)),
"root Claude plugin manifest",
root,
)
_assert_plugin_metadata_layout(
codex_root,
{".codex-plugin/plugin.json"},
"root Codex plugin",
)
_assert_plugin_metadata_layout(
claude_root,
{".claude-plugin/plugin.json"},
"root Claude plugin",
)
_assert_skill_mirror_matches(root, codex_root / "skills", codex_skill_ids, "root Codex plugin")
_assert_skill_mirror_matches(root, claude_root / "skills", claude_skill_ids, "root Claude plugin")
expected_bundle_names = {
_bundle_plugin_name(bundle["id"])
for bundle in bundles
if bundle_support[bundle["id"]]["codex"] or bundle_support[bundle["id"]]["claude"]
}
actual_bundle_names = {
path.name
for path in (root / "plugins").glob("agentic-bundle-*")
if path.is_dir()
}
if actual_bundle_names != expected_bundle_names:
missing = sorted(expected_bundle_names - actual_bundle_names)
unexpected = sorted(actual_bundle_names - expected_bundle_names)
detail = f"missing {missing[0]}" if missing else f"unexpected {unexpected[0]}"
raise ValueError(f"Generated bundle plugin directories are out of sync: {detail}")
for bundle in bundles:
support = bundle_support[bundle["id"]]
if not support["codex"] and not support["claude"]:
continue
plugin_root = root / "plugins" / _bundle_plugin_name(bundle["id"])
skill_ids = [skill["id"] for skill in bundle["skills"]]
_assert_skill_mirror_matches(
root,
plugin_root / "skills",
skill_ids,
f'bundle plugin {bundle["id"]}',
)
manifest_specs = (
(
"codex",
plugin_root / ".codex-plugin" / "plugin.json",
_bundle_codex_plugin_manifest(metadata, bundle),
),
(
"claude",
plugin_root / ".claude-plugin" / "plugin.json",
_bundle_claude_plugin_manifest(metadata, bundle),
),
)
expected_manifest_paths: set[str] = set()
for target, manifest_path, expected_manifest in manifest_specs:
if support[target]:
expected_manifest_paths.add(manifest_path.relative_to(plugin_root).as_posix())
_assert_json_matches(
manifest_path,
expected_manifest,
f'bundle {bundle["id"]} {target} manifest',
root,
)
elif manifest_path.exists():
raise ValueError(
f'Bundle {bundle["id"]} contains an unsupported {target} manifest: {manifest_path}'
)
_assert_plugin_metadata_layout(
plugin_root,
expected_manifest_paths,
f'bundle plugin {bundle["id"]}',
)
def _remove_path(path: Path) -> None:
if path.is_symlink() or path.is_file():
path.unlink()
@@ -860,7 +1092,11 @@ def main() -> int:
current_doc = (root / "docs" / "users" / "bundles.md").read_text(encoding="utf-8")
if current_doc != expected_doc:
raise SystemExit("docs/users/bundles.md is out of sync with data/editorial-bundles.json")
print("✅ Editorial bundles manifest and generated doc are in sync.")
try:
check_editorial_bundle_plugins(root, metadata, bundles, compatibility)
except ValueError as exc:
raise SystemExit(str(exc)) from exc
print("✅ Editorial bundles, marketplaces, manifests, and plugin mirrors are in sync.")
return 0
sync_editorial_bundles(root)
print("✅ Editorial bundles synced.")
@@ -191,6 +191,30 @@ def sync_getting_started(content: str, metadata: dict) -> str:
return content
def sync_web_index_shell(content: str, metadata: dict) -> str:
skill_label = metadata["total_skills_label"]
return sync_regex_text(
content,
[
(r"\d[\d,]*\+ installable agentic skills", f"{skill_label} installable agentic skills"),
(r"\d[\d,]*\+ AI coding skills", f"{skill_label} AI coding skills"),
],
)
def sync_llms_text(content: str, metadata: dict) -> str:
skill_label = metadata["total_skills_label"]
return sync_regex_text(
content,
[
(r"Current release: V[\d.]+\.", f"Current release: V{metadata['version']}."),
(r"\d[\d,]*\+ agentic SKILL\.md playbooks", f"{skill_label} agentic SKILL.md playbooks"),
(r"Skill count: \d[\d,]*\+\.", f"Skill count: {skill_label}."),
(r"\d[\d,]*\+ reusable SKILL\.md playbooks", f"{skill_label} reusable SKILL.md playbooks"),
],
)
def sync_bundles_doc(content: str, metadata: dict, base_dir: str | Path | None = None) -> str:
root = Path(base_dir) if base_dir is not None else Path(find_repo_root(__file__))
manifest_path = root / "data" / "editorial-bundles.json"
@@ -316,6 +340,8 @@ def sync_curated_docs(base_dir: str, metadata: dict, dry_run: bool) -> int:
updated_files = 0
updated_files += int(update_text_file(root / "README.md", sync_readme_copy, metadata, dry_run))
updated_files += int(update_text_file(root / "docs" / "users" / "getting-started.md", sync_getting_started, metadata, dry_run))
updated_files += int(update_text_file(root / "apps" / "web-app" / "index.html", sync_web_index_shell, metadata, dry_run))
updated_files += int(update_text_file(root / "apps" / "web-app" / "public" / "llms.txt", sync_llms_text, metadata, dry_run))
updated_files += int(
update_text_file(
root / "docs" / "users" / "bundles.md",
@@ -15,6 +15,11 @@ const publishWorkflow = readText(".github/workflows/publish-npm.yml");
const releaseWorkflowScript = readText("tools/scripts/release_workflow.js");
const hygieneWorkflowPath = path.join(repoRoot, ".github", "workflows", "repo-hygiene.yml");
const prepareReleaseBlock = releaseWorkflowScript.slice(
releaseWorkflowScript.indexOf("function prepareRelease"),
releaseWorkflowScript.indexOf("function publishRelease"),
);
assert.ok(
packageJson.scripts["sync:release-state"],
"package.json should expose a deterministic release-state sync command",
@@ -90,6 +95,8 @@ assert.match(
for (const filePath of [
"README.md",
"package.json",
"apps/web-app/index.html",
"apps/web-app/public/llms.txt",
"docs/users/getting-started.md",
"docs/users/bundles.md",
"docs/users/claude-code-skills.md",
@@ -277,6 +284,11 @@ assert.match(
/runCommand\("npm", \["run", "app:install"\], projectRoot\);[\s\S]*runCommand\("npm", \["run", "app:build"\], projectRoot\);/,
"release workflow should install web-app dependencies before building the app",
);
assert.ok(
prepareReleaseBlock.indexOf('["run", "sync:metadata", "--", "--refresh-volatile"]') <
prepareReleaseBlock.indexOf("runReleaseSuite(projectRoot)"),
"release preparation should refresh volatile metadata before generating canonical release artifacts",
);
assert.match(
publishWorkflow,
/npm pack --dry-run --json/,
@@ -15,6 +15,23 @@ const release = installer.parseArgs(['--release', '14.0.0']);
assert.strictEqual(release.versionArg, '14.0.0');
assert.strictEqual(release.versionInfo, false);
const exactPreview = installer.parseArgs([
'--codex',
'--skills',
'frontend-design,game-development/2d-games',
'--dry-run',
]);
assert.strictEqual(exactPreview.skillsArg, 'frontend-design,game-development/2d-games');
assert.strictEqual(exactPreview.dryRun, true);
assert.deepStrictEqual(
installer.parseExactSkillArg(exactPreview.skillsArg),
['frontend-design', 'game-development/2d-games'],
);
assert.throws(
() => installer.parseExactSkillArg('frontend-design,,backend-dev-guidelines'),
/non-empty exact skill/i,
);
const version = spawnSync(process.execPath, [installerPath, '--version'], { encoding: 'utf8' });
assert.strictEqual(version.status, 0, version.stderr);
assert.strictEqual(version.stdout.trim(), packageVersion);
@@ -0,0 +1,263 @@
const assert = require("assert");
const fs = require("fs");
const os = require("os");
const path = require("path");
const { spawnSync } = require("child_process");
const installer = require(path.resolve(__dirname, "..", "..", "bin", "install.js"));
const packageVersion = require(path.resolve(__dirname, "..", "..", "..", "package.json")).version;
const pinnedRef = `v${packageVersion}`;
function writeSkill(repoRoot, skillPath, frontmatter) {
const skillDir = path.join(repoRoot, "skills", skillPath);
fs.mkdirSync(skillDir, { recursive: true });
fs.writeFileSync(
path.join(skillDir, "SKILL.md"),
`---\n${frontmatter}\n---\n\n# ${skillPath}\n`,
"utf8",
);
}
const tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), "installer-exact-selection-"));
try {
const repoRoot = path.join(tmpRoot, "repo");
fs.mkdirSync(path.join(repoRoot, "skills"), { recursive: true });
fs.mkdirSync(path.join(repoRoot, "docs"), { recursive: true });
fs.writeFileSync(path.join(repoRoot, "docs", "README.md"), "# Docs\n", "utf8");
writeSkill(
repoRoot,
"frontend-design",
"name: frontend-design\ncategory: development\nrisk: safe\ntags: [frontend]",
);
writeSkill(
repoRoot,
path.join("game-development", "2d-games"),
"name: 2D Games\ncategory: games\nrisk: safe\ntags: [game]",
);
writeSkill(
repoRoot,
path.join("legacy-games", "2d-games"),
"name: Legacy 2D Games\ncategory: games\nrisk: safe\ntags: [game]",
);
writeSkill(
repoRoot,
"shared-one",
"name: Shared Skill\ncategory: development\nrisk: safe\ntags: [shared]",
);
writeSkill(
repoRoot,
"shared-two",
"name: Shared Skill\ncategory: development\nrisk: safe\ntags: [shared]",
);
const requested = installer.parseExactSkillArg("frontend-design,game-development/2d-games");
assert.deepStrictEqual(
installer.getInstallEntries(repoRoot, installer.buildInstallSelectors({}), requested),
["frontend-design", "game-development/2d-games", "docs"],
"--skills should resolve exact root ids and full nested paths",
);
assert.deepStrictEqual(
installer.getInstallEntries(
repoRoot,
installer.buildInstallSelectors({ categoryArg: "development" }),
requested,
),
["frontend-design", "docs"],
"--skills must combine with metadata filters using AND",
);
assert.throws(
() => installer.getInstallEntries(
repoRoot,
installer.buildInstallSelectors({ categoryArg: "games" }),
installer.parseExactSkillArg("frontend-design"),
),
/No skills matched/i,
"an empty exact-selection/filter intersection must fail instead of installing docs or a broad fallback",
);
assert.throws(
() => installer.getInstallEntries(
repoRoot,
installer.buildInstallSelectors({}),
installer.parseExactSkillArg("does-not-exist"),
),
/unknown skill requested/i,
"unknown exact skill selections must fail",
);
assert.throws(
() => installer.getInstallEntries(
repoRoot,
installer.buildInstallSelectors({}),
installer.parseExactSkillArg("2d-games"),
),
/ambiguous skill requested/i,
"ambiguous basename selections must require a full nested path",
);
assert.throws(
() => installer.getInstallEntries(
repoRoot,
installer.buildInstallSelectors({}),
installer.parseExactSkillArg("Shared Skill"),
),
/ambiguous skill requested/i,
"ambiguous canonical names must fail",
);
const absentTarget = path.join(tmpRoot, "absent-target");
assert.throws(
() => installer.installForTarget(
repoRoot,
{ name: "Absent", path: absentTarget },
installer.buildInstallSelectors({}),
null,
installer.parseExactSkillArg("does-not-exist"),
),
/unknown skill requested/i,
);
assert.strictEqual(
fs.existsSync(absentTarget),
false,
"invalid --skills selection must fail before an absent target is created",
);
const existingTarget = path.join(tmpRoot, "existing-target");
fs.mkdirSync(existingTarget, { recursive: true });
const sentinelPath = path.join(existingTarget, "sentinel.txt");
fs.writeFileSync(sentinelPath, "keep", "utf8");
const manifestPath = path.join(existingTarget, ".antigravity-install-manifest.json");
fs.writeFileSync(
manifestPath,
JSON.stringify({
schemaVersion: 1,
entries: ["frontend-design", "legacy-skill", "docs", "../outside"],
}),
"utf8",
);
const plan = installer.buildDryRunPlan(
pinnedRef,
[
{ name: "Existing", path: existingTarget },
{ name: "Absent", path: absentTarget },
],
["game-development/2d-games", "frontend-design", "docs"],
);
assert.deepStrictEqual(plan.targets[0].remove, ["legacy-skill"]);
assert.deepStrictEqual(plan.targets[0].ignoredUnsafeManifestEntries, ["../outside"]);
assert.strictEqual(plan.targets[1].targetExists, false);
const output = [];
const originalLog = console.log;
console.log = (message) => output.push(String(message));
try {
installer.printDryRunPlan(plan);
} finally {
console.log = originalLog;
}
assert.strictEqual(fs.readFileSync(sentinelPath, "utf8"), "keep");
assert.strictEqual(fs.existsSync(absentTarget), false);
assert.match(output.join("\n"), new RegExp(`Ref: ${pinnedRef.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`));
assert.match(output.join("\n"), new RegExp(existingTarget.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")));
assert.match(output.join("\n"), /Exact skill set \(2\):/);
assert.match(output.join("\n"), /frontend-design/);
assert.match(output.join("\n"), /remove stale managed entries \(1\)/i);
assert.match(output.join("\n"), /legacy-skill/);
assert.match(output.join("\n"), /ignored unsafe manifest entry: \.\.\/outside/i);
// Exercise the real CLI boundary with a deterministic fake git clone. This
// proves --dry-run returns before installForTarget can mutate either an
// existing target or a missing one.
const fakeBin = path.join(tmpRoot, "fake-bin");
fs.mkdirSync(fakeBin, { recursive: true });
const fakeGit = path.join(fakeBin, "git");
fs.writeFileSync(
fakeGit,
`#!/usr/bin/env node
const fs = require("fs");
const destination = process.argv[process.argv.length - 1];
fs.appendFileSync(process.env.FAKE_GIT_LOG, JSON.stringify(process.argv.slice(2)) + "\\n");
fs.cpSync(process.env.FAKE_GIT_SOURCE, destination, { recursive: true, force: true });
`,
"utf8",
);
fs.chmodSync(fakeGit, 0o755);
const fakeGitLog = path.join(tmpRoot, "fake-git-log.jsonl");
const cliEnv = {
...process.env,
FAKE_GIT_SOURCE: repoRoot,
FAKE_GIT_LOG: fakeGitLog,
PATH: `${fakeBin}${path.delimiter}${process.env.PATH || ""}`,
};
const installerPath = path.resolve(__dirname, "..", "..", "bin", "install.js");
const beforeDryRunEntries = fs.readdirSync(existingTarget).sort();
const dryRun = spawnSync(
process.execPath,
[installerPath, "--path", existingTarget, "--release", packageVersion, "--skills", "frontend-design", "--dry-run"],
{ encoding: "utf8", env: cliEnv },
);
assert.strictEqual(dryRun.status, 0, dryRun.stderr);
assert.match(dryRun.stdout, new RegExp(`Ref: ${pinnedRef.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`));
assert.match(dryRun.stdout, /Exact skill set \(1\):/);
assert.match(dryRun.stdout, /remove stale managed entries \(1\)/i);
assert.match(dryRun.stdout, /legacy-skill/);
assert.deepStrictEqual(fs.readdirSync(existingTarget).sort(), beforeDryRunEntries);
assert.strictEqual(fs.readFileSync(sentinelPath, "utf8"), "keep");
const cloneArgs = JSON.parse(fs.readFileSync(fakeGitLog, "utf8").trim().split("\n")[0]);
assert.deepStrictEqual(
cloneArgs.slice(0, 5),
["clone", "--depth", "1", "--branch", pinnedRef],
"the real CLI dry run must clone the explicitly pinned release",
);
const outsideSentinel = path.join(tmpRoot, "outside");
fs.writeFileSync(outsideSentinel, "outside stays", "utf8");
assert.doesNotThrow(() => installer.pruneRemovedEntries(existingTarget, ["../outside"], []));
assert.strictEqual(fs.readFileSync(outsideSentinel, "utf8"), "outside stays");
const unknownTarget = path.join(tmpRoot, "unknown-target");
const unknown = spawnSync(
process.execPath,
[installerPath, "--path", unknownTarget, "--skills", "does-not-exist", "--dry-run"],
{ encoding: "utf8", env: cliEnv },
);
assert.strictEqual(unknown.status, 1);
assert.match(unknown.stderr, /Unknown skill requested/);
assert.strictEqual(fs.existsSync(unknownTarget), false);
const multiHome = path.join(tmpRoot, "multi-home");
const firstTarget = path.join(multiHome, ".claude", "skills");
const codexHome = path.join(tmpRoot, "codex-home");
const unsafeTarget = path.join(codexHome, "skills");
const unsafeRealTarget = path.join(tmpRoot, "unsafe-real-target");
fs.mkdirSync(firstTarget, { recursive: true });
fs.mkdirSync(codexHome, { recursive: true });
fs.mkdirSync(unsafeRealTarget, { recursive: true });
const firstSentinel = path.join(firstTarget, "first-sentinel.txt");
fs.writeFileSync(firstSentinel, "first stays", "utf8");
let createdUnsafeSymlink = false;
try {
fs.symlinkSync(unsafeRealTarget, unsafeTarget, "dir");
createdUnsafeSymlink = true;
} catch (error) {
// Some platforms disallow unprivileged directory symlinks.
}
if (createdUnsafeSymlink) {
const multiTarget = spawnSync(
process.execPath,
[installerPath, "--claude", "--codex", "--skills", "frontend-design"],
{
encoding: "utf8",
env: { ...cliEnv, HOME: multiHome, CODEX_HOME: codexHome },
},
);
assert.strictEqual(multiTarget.status, 1);
assert.match(multiTarget.stderr, /symlinked target/i);
assert.deepStrictEqual(fs.readdirSync(firstTarget), ["first-sentinel.txt"]);
assert.strictEqual(fs.readFileSync(firstSentinel, "utf8"), "first stays");
}
} finally {
fs.rmSync(tmpRoot, { recursive: true, force: true });
}
@@ -65,7 +65,13 @@ class EditorialBundlesTests(unittest.TestCase):
self.assertEqual(actual, expected)
def test_get_bundle_skills_reads_json_manifest_by_name_and_id(self):
expected = ["concise-planning", "git-pushing", "kaizen", "lint-and-validate", "systematic-debugging"]
expected = [
"concise-planning",
"git-pushing",
"lint-and-validate",
"systematic-debugging",
"test-driven-development",
]
self.assertEqual(get_bundle_skills.get_bundle_skills(["Essentials"]), expected)
self.assertEqual(get_bundle_skills.get_bundle_skills(["essentials"]), expected)
web_wizard_skills = get_bundle_skills.get_bundle_skills(["web-wizard"])
@@ -91,13 +97,13 @@ class EditorialBundlesTests(unittest.TestCase):
sample_skill_dir = essentials_plugin / "concise-planning"
self.assertTrue((sample_skill_dir / "SKILL.md").is_file())
def test_generated_plugin_count_matches_manifest(self):
generated_plugins = sorted(
def test_generated_plugins_cover_manifest_during_source_only_prs(self):
generated_plugins = {
path.name
for path in (REPO_ROOT / "plugins").iterdir()
if path.is_dir() and path.name.startswith("agentic-bundle-")
)
expected_plugins = sorted(
}
expected_plugins = {
f'agentic-bundle-{bundle["id"]}'
for bundle in self.manifest_bundles
if any(
@@ -107,8 +113,22 @@ class EditorialBundlesTests(unittest.TestCase):
)
for target in ("codex", "claude")
)
}
self.assertFalse(
expected_plugins - generated_plugins,
f"generated bundle plugins are missing: {sorted(expected_plugins - generated_plugins)}",
)
self.assertEqual(generated_plugins, expected_plugins)
def test_plugin_sync_prunes_stale_bundle_directories(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = pathlib.Path(temp_dir)
stale_plugin = root / "plugins" / "agentic-bundle-retired"
stale_plugin.mkdir(parents=True)
(stale_plugin / "marker.txt").write_text("stale", encoding="utf-8")
editorial_bundles.sync_editorial_bundle_plugins(root, {}, [], {})
self.assertFalse(stale_plugin.exists())
def test_codex_bundle_plugin_names_keep_qualified_skill_names_valid(self):
max_name_length = 64
@@ -207,6 +227,113 @@ class EditorialBundlesTests(unittest.TestCase):
f"Claude root plugin inclusion mismatch for {skill_id}",
)
def test_skill_mirror_check_rejects_stale_and_unexpected_files(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = pathlib.Path(temp_dir)
canonical = root / "skills" / "sample"
mirror = root / "plugin" / "skills" / "sample"
canonical.mkdir(parents=True)
mirror.mkdir(parents=True)
(canonical / "SKILL.md").write_text("canonical\n", encoding="utf-8")
(mirror / "SKILL.md").write_text("stale\n", encoding="utf-8")
with self.assertRaisesRegex(ValueError, "stale mirrored file: sample/SKILL.md"):
editorial_bundles._assert_skill_mirror_matches(
root,
root / "plugin" / "skills",
["sample"],
"sample plugin",
)
(mirror / "SKILL.md").write_text("canonical\n", encoding="utf-8")
(mirror / "unexpected.txt").write_text("extra\n", encoding="utf-8")
with self.assertRaisesRegex(ValueError, "unexpected mirrored file: sample/unexpected.txt"):
editorial_bundles._assert_skill_mirror_matches(
root,
root / "plugin" / "skills",
["sample"],
"sample plugin",
)
def test_skill_mirror_check_rejects_symlinks(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = pathlib.Path(temp_dir)
canonical = root / "skills" / "sample"
mirror = root / "plugin" / "skills" / "sample"
canonical.mkdir(parents=True)
mirror.mkdir(parents=True)
(canonical / "SKILL.md").write_text("canonical\n", encoding="utf-8")
(mirror / "SKILL.md").symlink_to(canonical / "SKILL.md")
with self.assertRaisesRegex(ValueError, "unexpected symlink: sample/SKILL.md"):
editorial_bundles._assert_skill_mirror_matches(
root,
root / "plugin" / "skills",
["sample"],
"sample plugin",
)
mirror_root_link = root / "linked-skills"
mirror_root_link.symlink_to(root / "plugin" / "skills", target_is_directory=True)
with self.assertRaisesRegex(ValueError, "skills directory must not be a symlink"):
editorial_bundles._assert_skill_mirror_matches(
root,
mirror_root_link,
["sample"],
"sample plugin",
)
def test_plugin_metadata_layout_rejects_unexpected_files(self):
with tempfile.TemporaryDirectory() as temp_dir:
plugin_root = pathlib.Path(temp_dir) / "plugin"
manifest = plugin_root / ".codex-plugin" / "plugin.json"
manifest.parent.mkdir(parents=True)
manifest.write_text("{}\n", encoding="utf-8")
(plugin_root / "README.md").write_text("stale\n", encoding="utf-8")
with self.assertRaisesRegex(ValueError, "metadata layout is out of sync: unexpected README.md"):
editorial_bundles._assert_plugin_metadata_layout(
plugin_root,
{".codex-plugin/plugin.json"},
"sample plugin",
)
def test_json_check_requires_canonical_serialization_and_regular_file(self):
with tempfile.TemporaryDirectory() as temp_dir:
root = pathlib.Path(temp_dir)
manifest = root / "plugin.json"
manifest.write_text('{"name":"sample"}\n', encoding="utf-8")
with self.assertRaisesRegex(ValueError, "is out of sync"):
editorial_bundles._assert_json_matches(
manifest,
{"name": "sample"},
"sample manifest",
root,
)
manifest.write_bytes(b'{\r\n "name": "sample"\r\n}\r\n')
with self.assertRaisesRegex(ValueError, "is out of sync"):
editorial_bundles._assert_json_matches(
manifest,
{"name": "sample"},
"sample manifest",
root,
)
external = root / "external"
external.mkdir()
(external / "plugin.json").write_text('{\n "name": "sample"\n}\n', encoding="utf-8")
linked_parent = root / "linked"
linked_parent.symlink_to(external, target_is_directory=True)
with self.assertRaisesRegex(ValueError, "path must not contain a symlink"):
editorial_bundles._assert_json_matches(
linked_parent / "plugin.json",
{"name": "sample"},
"sample manifest",
root,
)
def test_remove_tree_retries_on_enotempty(self):
target = REPO_ROOT / "plugins" / "agentic-awesome-skills" / "skills"
calls = {"count": 0}
@@ -100,6 +100,38 @@ class GenerateIndexCategoryTests(unittest.TestCase):
self.assertEqual(categories["nested-skill"], "bundles")
self.assertEqual(categories["playwright-skill"], "test-automation")
def test_generate_index_preserves_top_level_and_nested_tags(self):
with tempfile.TemporaryDirectory() as temp_dir:
base = pathlib.Path(temp_dir)
skills_dir = base / "skills"
output_file = base / "skills_index.json"
top_level = skills_dir / "top-level"
top_level.mkdir(parents=True)
(top_level / "SKILL.md").write_text(
"---\nname: top-level\ndescription: Top level tags\ntags: [api, typescript, api]\n---\nbody\n",
encoding="utf-8",
)
nested_tags = skills_dir / "nested-tags"
nested_tags.mkdir(parents=True)
(nested_tags / "SKILL.md").write_text(
"---\nname: nested-tags\ndescription: Nested tags\nmetadata:\n tags: data, python\n---\nbody\n",
encoding="utf-8",
)
skills = generate_index.generate_index(str(skills_dir), str(output_file))
by_id = {skill["id"]: skill for skill in skills}
self.assertEqual(by_id["top-level"]["tags"], ["api", "typescript"])
self.assertEqual(by_id["nested-tags"]["tags"], ["data", "python"])
def test_tag_coercion_is_deterministic_for_yaml_sets(self):
self.assertEqual(
generate_index.coerce_metadata_list({"typescript", "api"}),
["api", "typescript"],
)
def test_generate_index_rejects_duplicate_route_ids(self):
with tempfile.TemporaryDirectory() as temp_dir:
base = pathlib.Path(temp_dir)
@@ -59,6 +59,20 @@ class SyncRepoMetadataTests(unittest.TestCase):
(root / "docs" / "users").mkdir(parents=True)
(root / "docs" / "maintainers").mkdir(parents=True)
(root / "docs" / "integrations" / "jetski-gemini-loader").mkdir(parents=True)
(root / "apps" / "web-app" / "public").mkdir(parents=True)
(root / "apps" / "web-app" / "index.html").write_text(
'<meta name="description" content="Explore 1,273+ installable agentic skills">\n'
'<title>Agentic Awesome Skills GitHub | 1,273+ AI coding skills</title>\n',
encoding="utf-8",
)
(root / "apps" / "web-app" / "public" / "llms.txt").write_text(
"> Installable GitHub library of 1,273+ agentic SKILL.md playbooks.\n"
"- Current release: V8.3.0.\n"
"- Skill count: 1,273+.\n"
"Agentic Awesome Skills is an installable library of 1,273+ reusable SKILL.md playbooks.\n",
encoding="utf-8",
)
(root / "docs" / "users" / "getting-started.md").write_text(
"# Getting Started with Agentic Awesome Skills (V8.3.0)\n",
@@ -107,7 +121,7 @@ class SyncRepoMetadataTests(unittest.TestCase):
updated_files = sync_repo_metadata.sync_curated_docs(str(root), metadata, dry_run=False)
self.assertGreaterEqual(updated_files, 10)
self.assertGreaterEqual(updated_files, 12)
readme = (root / "README.md").read_text(encoding="utf-8")
self.assertIn("1,304+ agentic skills", readme)
self.assertIn("[📚 Browse 1,304+ Skills](#browse-1304-skills)", readme)
@@ -117,6 +131,13 @@ class SyncRepoMetadataTests(unittest.TestCase):
self.assertIn("1,304+ files", (root / "docs" / "users" / "gemini-cli-skills.md").read_text(encoding="utf-8"))
self.assertIn("1,304+ specialized areas", (root / "docs" / "users" / "kiro-integration.md").read_text(encoding="utf-8"))
self.assertIn("Total Bundles: 2", (root / "docs" / "users" / "bundles.md").read_text(encoding="utf-8"))
web_index = (root / "apps" / "web-app" / "index.html").read_text(encoding="utf-8")
self.assertIn("1,304+ installable agentic skills", web_index)
self.assertIn("1,304+ AI coding skills", web_index)
llms_text = (root / "apps" / "web-app" / "public" / "llms.txt").read_text(encoding="utf-8")
self.assertIn("Current release: V8.4.0.", llms_text)
self.assertIn("Skill count: 1,304+.", llms_text)
self.assertIn("1,304+ reusable SKILL.md playbooks", llms_text)
jetski_cortex = (root / "docs" / "integrations" / "jetski-cortex.md").read_text(encoding="utf-8")
self.assertIn("1,304+ skill", jetski_cortex)
self.assertNotIn("1,1", jetski_cortex)
@@ -0,0 +1,39 @@
const assert = require('assert');
const fs = require('fs');
const path = require('path');
const { resolveExactSkillSelections } = require('../../bin/install');
const { listSkillIdsRecursive } = require('../../lib/skill-utils');
const repoRoot = path.resolve(__dirname, '..', '..', '..');
const repoSkills = path.join(repoRoot, 'skills');
const catalog = JSON.parse(fs.readFileSync(path.join(repoRoot, 'skills_index.json'), 'utf8'));
const installerEntries = listSkillIdsRecursive(repoSkills);
assert.strictEqual(
installerEntries.length,
catalog.length,
'the Workbench catalog and installer must see the same number of canonical skills',
);
const resolvedEntries = resolveExactSkillSelections(
repoSkills,
installerEntries,
catalog.map((skill) => skill.id),
);
assert.strictEqual(
resolvedEntries.size,
catalog.length,
'every Workbench id must resolve to exactly one installer entry',
);
for (const skill of catalog) {
const expectedEntry = skill.path.replace(/^skills\//, '');
assert.ok(
resolvedEntries.has(expectedEntry),
`Workbench id ${skill.id} must resolve to ${expectedEntry}`,
);
}
console.log(`Workbench installer contract passed for ${catalog.length} canonical skills.`);