📦 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
@@ -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.`);