diff --git a/manifests/install-modules.json b/manifests/install-modules.json index de93c523c..42aa59a4d 100644 --- a/manifests/install-modules.json +++ b/manifests/install-modules.json @@ -199,7 +199,23 @@ "skills/springboot-tdd", "skills/springboot-verification", "skills/ui-to-vue", - "skills/vue-patterns" + "skills/vue-patterns", + "skills/accessibility", + "skills/bun-runtime", + "skills/design-system", + "skills/django-celery", + "skills/flutter-dart-code-review", + "skills/frontend-a11y", + "skills/generating-python-installer", + "skills/hexagonal-architecture", + "skills/motion-advanced", + "skills/motion-foundations", + "skills/motion-patterns", + "skills/nextjs-turbopack", + "skills/nuxt4-patterns", + "skills/react-native-patterns", + "skills/tinystruct-patterns", + "skills/vite-patterns" ], "targets": [ "claude", @@ -233,7 +249,8 @@ "skills/jpa-patterns", "skills/mysql-patterns", "skills/postgres-patterns", - "skills/prisma-patterns" + "skills/prisma-patterns", + "skills/redis-patterns" ], "targets": [ "claude", @@ -280,7 +297,28 @@ "skills/strategic-compact", "skills/tdd-workflow", "skills/verification-loop", - "skills/windows-desktop-e2e" + "skills/windows-desktop-e2e", + "skills/agent-self-evaluation", + "skills/architecture-decision-records", + "skills/browser-qa", + "skills/ck", + "skills/click-path-audit", + "skills/codebase-onboarding", + "skills/codehealth-mcp", + "skills/config-gc", + "skills/context-budget", + "skills/delivery-gate", + "skills/ecc-guide", + "skills/ecc-recipes", + "skills/growth-log", + "skills/inherit-legacy-style", + "skills/intent-driven-development", + "skills/loop-design-check", + "skills/product-lens", + "skills/repo-scan", + "skills/rules-distill", + "skills/santa-method", + "skills/git-workflow" ], "targets": [ "claude", @@ -313,7 +351,10 @@ "skills/data-throughput-accelerator", "skills/latency-critical-systems", "skills/parallel-execution-optimizer", - "skills/recursive-decision-ledger" + "skills/recursive-decision-ledger", + "skills/agent-eval", + "skills/benchmark", + "skills/benchmark-methodology" ], "targets": [ "claude", @@ -354,7 +395,12 @@ "skills/security-bounty-hunter", "skills/springboot-security", "skills/evm-token-decimals", - "the-security-guide.md" + "the-security-guide.md", + "skills/gateguard", + "skills/healthcare-cdss-patterns", + "skills/healthcare-emr-patterns", + "skills/healthcare-eval-harness", + "skills/safety-guard" ], "targets": [ "claude", @@ -387,7 +433,8 @@ "skills/scientific-db-uspto-database", "skills/scientific-pkg-gget", "skills/scientific-thinking-literature-review", - "skills/scientific-thinking-scholar-evaluation" + "skills/scientific-thinking-scholar-evaluation", + "skills/documentation-lookup" ], "targets": [ "claude", @@ -422,7 +469,11 @@ "skills/product-capability", "skills/social-graph-ranker", "skills/seo", - "skills/market-research" + "skills/market-research", + "skills/brand-discovery", + "skills/competitive-platform-analysis", + "skills/competitive-report-structure", + "skills/marketing-campaign" ], "targets": [ "claude", @@ -465,7 +516,8 @@ "skills/project-flow-ops", "skills/terminal-ops", "skills/unified-notifications-ops", - "skills/workspace-surface-audit" + "skills/workspace-surface-audit", + "skills/mailtrap-email-integration" ], "targets": [ "claude", @@ -525,7 +577,8 @@ "description": "Social publishing and distribution skills.", "paths": [ "skills/crosspost", - "skills/x-api" + "skills/x-api", + "skills/social-publisher" ], "targets": [ "claude", @@ -557,7 +610,8 @@ "skills/remotion-video-creation", "skills/ui-demo", "skills/video-editing", - "skills/videodb" + "skills/videodb", + "skills/taste" ], "targets": [ "claude", @@ -613,7 +667,8 @@ "skills/swift-actor-persistence", "skills/swift-concurrency-6-2", "skills/swift-protocol-di-testing", - "skills/swiftui-patterns" + "skills/swiftui-patterns", + "skills/ios-icon-gen" ], "targets": [ "claude", @@ -661,7 +716,20 @@ "skills/search-first", "skills/team-agent-orchestration", "skills/token-budget-advisor", - "skills/team-builder" + "skills/team-builder", + "skills/agent-payment-x402", + "skills/autonomous-agent-harness", + "skills/gan-style-harness", + "skills/hermes-imports", + "skills/openclaw-persona-forge", + "skills/opensource-pipeline", + "skills/orch-add-feature", + "skills/orch-build-mvp", + "skills/orch-change-feature", + "skills/orch-fix-defect", + "skills/orch-pipeline", + "skills/orch-refine-code", + "skills/plan-orchestrate" ], "targets": [ "claude", @@ -695,7 +763,14 @@ "skills/netmiko-ssh-automation", "skills/network-bgp-diagnostics", "skills/network-config-validation", - "skills/network-interface-health" + "skills/network-interface-health", + "skills/canary-watch", + "skills/flox-environments", + "skills/homelab-pihole-dns", + "skills/homelab-vlan-segmentation", + "skills/homelab-wireguard-vpn", + "skills/kubernetes-patterns", + "skills/uncloud" ], "targets": [ "claude", @@ -721,7 +796,10 @@ "kind": "skills", "description": "Production machine-learning engineering workflows for data contracts, reproducible training, evaluation, deployment, monitoring, and rollback.", "paths": [ - "skills/mle-workflow" + "skills/mle-workflow", + "skills/ml-adoption-playbook", + "skills/pytorch-patterns", + "skills/recsys-pipeline-architect" ], "targets": [ "claude", diff --git a/package.json b/package.json index 5e0be9b02..efadd5efd 100644 --- a/package.json +++ b/package.json @@ -318,6 +318,84 @@ "skills/windows-desktop-e2e/", "skills/workspace-surface-audit/", "skills/x-api/", + "skills/accessibility/", + "skills/agent-eval/", + "skills/agent-payment-x402/", + "skills/agent-self-evaluation/", + "skills/architecture-decision-records/", + "skills/autonomous-agent-harness/", + "skills/benchmark/", + "skills/benchmark-methodology/", + "skills/brand-discovery/", + "skills/browser-qa/", + "skills/bun-runtime/", + "skills/canary-watch/", + "skills/ck/", + "skills/click-path-audit/", + "skills/codebase-onboarding/", + "skills/codehealth-mcp/", + "skills/competitive-platform-analysis/", + "skills/competitive-report-structure/", + "skills/config-gc/", + "skills/context-budget/", + "skills/delivery-gate/", + "skills/design-system/", + "skills/django-celery/", + "skills/documentation-lookup/", + "skills/ecc-guide/", + "skills/ecc-recipes/", + "skills/flox-environments/", + "skills/flutter-dart-code-review/", + "skills/frontend-a11y/", + "skills/gan-style-harness/", + "skills/gateguard/", + "skills/generating-python-installer/", + "skills/git-workflow/", + "skills/growth-log/", + "skills/healthcare-cdss-patterns/", + "skills/healthcare-emr-patterns/", + "skills/healthcare-eval-harness/", + "skills/hermes-imports/", + "skills/hexagonal-architecture/", + "skills/homelab-pihole-dns/", + "skills/homelab-vlan-segmentation/", + "skills/homelab-wireguard-vpn/", + "skills/inherit-legacy-style/", + "skills/intent-driven-development/", + "skills/ios-icon-gen/", + "skills/kubernetes-patterns/", + "skills/loop-design-check/", + "skills/mailtrap-email-integration/", + "skills/marketing-campaign/", + "skills/ml-adoption-playbook/", + "skills/motion-advanced/", + "skills/motion-foundations/", + "skills/motion-patterns/", + "skills/nextjs-turbopack/", + "skills/nuxt4-patterns/", + "skills/openclaw-persona-forge/", + "skills/opensource-pipeline/", + "skills/orch-add-feature/", + "skills/orch-build-mvp/", + "skills/orch-change-feature/", + "skills/orch-fix-defect/", + "skills/orch-pipeline/", + "skills/orch-refine-code/", + "skills/plan-orchestrate/", + "skills/product-lens/", + "skills/pytorch-patterns/", + "skills/react-native-patterns/", + "skills/recsys-pipeline-architect/", + "skills/redis-patterns/", + "skills/repo-scan/", + "skills/rules-distill/", + "skills/safety-guard/", + "skills/santa-method/", + "skills/social-publisher/", + "skills/taste/", + "skills/tinystruct-patterns/", + "skills/uncloud/", + "skills/vite-patterns/", "the-security-guide.md", "!**/__pycache__/**", "!**/*.pyc", diff --git a/scripts/ci/validate-install-manifests.js b/scripts/ci/validate-install-manifests.js index 7f1f4f245..bea312ce3 100644 --- a/scripts/ci/validate-install-manifests.js +++ b/scripts/ci/validate-install-manifests.js @@ -16,6 +16,11 @@ const COMPONENTS_MANIFEST_PATH = path.join(REPO_ROOT, 'manifests/install-compone const MODULES_SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/install-modules.schema.json'); const PROFILES_SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/install-profiles.schema.json'); const COMPONENTS_SCHEMA_PATH = path.join(REPO_ROOT, 'schemas/install-components.schema.json'); +const CURATED_SKILLS_DIR = path.join(REPO_ROOT, 'skills'); +// Empty by default; add only curated skills that are intentionally unshipped. +const INTENTIONALLY_UNSHIPPED_SKILL_IDS = new Set([ + 'skill-comply', // meta/measurement dev-skill; ships committed .pyc artifacts and a nested .gitignore, revisit after packaging cleanup +]); const COMPONENT_FAMILY_PREFIXES = { baseline: 'baseline:', language: 'lang:', @@ -36,6 +41,18 @@ function normalizeRelativePath(relativePath) { return String(relativePath).replace(/\\/g, '/').replace(/\/+$/, ''); } +function isCuratedSkillReferenced(claimedPaths, skillId) { + const skillRoot = `skills/${skillId}`; + + for (const claimedPath of claimedPaths.keys()) { + if (claimedPath === skillRoot || claimedPath.startsWith(`${skillRoot}/`)) { + return true; + } + } + + return false; +} + function validateSchema(ajv, schemaPath, data, label) { const schema = readJson(schemaPath, `${label} schema`); const validate = ajv.compile(schema); @@ -131,6 +148,30 @@ function validateInstallManifests() { } } + if (fs.existsSync(CURATED_SKILLS_DIR)) { + const entries = fs.readdirSync(CURATED_SKILLS_DIR, { withFileTypes: true }); + for (const entry of entries) { + if (!entry.isDirectory() || entry.name.startsWith('.')) { + continue; + } + + const skillMdPath = path.join(CURATED_SKILLS_DIR, entry.name, 'SKILL.md'); + if (!fs.existsSync(skillMdPath)) { + continue; + } + + if ( + !INTENTIONALLY_UNSHIPPED_SKILL_IDS.has(entry.name) + && !isCuratedSkillReferenced(claimedPaths, entry.name) + ) { + console.error( + `ERROR: curated skill skills/${entry.name} is not referenced by any install module` + ); + hasErrors = true; + } + } + } + const profiles = profilesData.profiles || {}; const components = Array.isArray(componentsData.components) ? componentsData.components : []; const expectedProfileIds = ['core', 'developer', 'security', 'research', 'full']; diff --git a/tests/ci/validators.test.js b/tests/ci/validators.test.js index 5d75ccbb4..8c34d0029 100644 --- a/tests/ci/validators.test.js +++ b/tests/ci/validators.test.js @@ -52,6 +52,29 @@ function writeInstallComponentsManifest(testDir, components) { }); } +function writeInstallModulesManifest(testDir, modules) { + writeJson(path.join(testDir, 'manifests', 'install-modules.json'), { + version: 1, + modules, + }); +} + +function writeInstallProfilesManifest(testDir, profiles) { + writeJson(path.join(testDir, 'manifests', 'install-profiles.json'), { + version: 1, + profiles, + }); +} + +function writeSkillFixture(testDir, skillId, description) { + const skillDir = path.join(testDir, 'skills', skillId); + fs.mkdirSync(skillDir, { recursive: true }); + fs.writeFileSync( + path.join(skillDir, 'SKILL.md'), + `---\nname: ${skillId}\ndescription: ${description}\n---\n# ${skillId}\n` + ); +} + function stripShebang(source) { let s = source; if (s.charCodeAt(0) === 0xFEFF) s = s.slice(1); @@ -2793,6 +2816,99 @@ function runTests() { assert.ok(result.stdout.includes('Validated'), 'Should output validation count'); })) passed++; else failed++; + if (test('fails when a curated skill is not referenced by any install module', () => { + const testDir = createTestDir(); + try { + writeInstallModulesManifest(testDir, [ + { + id: 'skill-alpha', + kind: 'skills', + description: 'Alpha skill', + paths: ['skills/alpha'], + targets: ['claude'], + dependencies: [], + defaultInstall: false, + cost: 'light', + stability: 'stable', + }, + { + id: 'skill-beta', + kind: 'skills', + description: 'Beta skill', + paths: ['skills/beta'], + targets: ['claude'], + dependencies: [], + defaultInstall: false, + cost: 'light', + stability: 'stable', + }, + ]); + writeInstallProfilesManifest(testDir, { + core: { description: 'Core', modules: ['skill-alpha', 'skill-beta'] }, + developer: { description: 'Developer', modules: ['skill-alpha', 'skill-beta'] }, + security: { description: 'Security', modules: ['skill-alpha', 'skill-beta'] }, + research: { description: 'Research', modules: ['skill-alpha', 'skill-beta'] }, + full: { description: 'Full', modules: ['skill-alpha', 'skill-beta'] }, + }); + writeSkillFixture(testDir, 'alpha', 'Alpha skill'); + writeSkillFixture(testDir, 'beta', 'Beta skill'); + + let result = runValidatorWithDirs('validate-install-manifests', { + REPO_ROOT: testDir, + MODULES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-modules.json'), + PROFILES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-profiles.json'), + COMPONENTS_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-components.json'), + MODULES_SCHEMA_PATH: modulesSchemaPath, + PROFILES_SCHEMA_PATH: profilesSchemaPath, + COMPONENTS_SCHEMA_PATH: componentsSchemaPath, + }); + assert.strictEqual(result.code, 0, `Should pass with both skills referenced, got stderr: ${result.stderr}`); + + writeInstallModulesManifest(testDir, [ + { + id: 'skill-alpha', + kind: 'skills', + description: 'Alpha skill', + paths: ['skills/alpha'], + targets: ['claude'], + dependencies: [], + defaultInstall: false, + cost: 'light', + stability: 'stable', + }, + { + id: 'skill-beta', + kind: 'skills', + description: 'Beta skill', + paths: ['skills/beta-restored'], + targets: ['claude'], + dependencies: [], + defaultInstall: false, + cost: 'light', + stability: 'stable', + }, + ]); + writeSkillFixture(testDir, 'beta-restored', 'Beta skill restored'); + + result = runValidatorWithDirs('validate-install-manifests', { + REPO_ROOT: testDir, + MODULES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-modules.json'), + PROFILES_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-profiles.json'), + COMPONENTS_MANIFEST_PATH: path.join(testDir, 'manifests', 'install-components.json'), + MODULES_SCHEMA_PATH: modulesSchemaPath, + PROFILES_SCHEMA_PATH: profilesSchemaPath, + COMPONENTS_SCHEMA_PATH: componentsSchemaPath, + }); + assert.strictEqual(result.code, 1, 'Should fail when beta is no longer referenced'); + assert.ok( + result.stderr.includes('curated skill skills/beta is not referenced by any install module'), + `Should report unreferenced skill, got: ${result.stderr}` + ); + } finally { + cleanupTestDir(testDir); + } + })) passed++; else failed++; + if (test('exits 0 when install manifests do not exist', () => { const testDir = createTestDir(); const result = runValidatorWithDirs('validate-install-manifests', { diff --git a/tests/scripts/install-apply.test.js b/tests/scripts/install-apply.test.js index da1a258b4..dc7b6408c 100644 --- a/tests/scripts/install-apply.test.js +++ b/tests/scripts/install-apply.test.js @@ -372,6 +372,28 @@ function runTests() { } })) passed++; else failed++; + if (test('full profile dry-runs include delivery-gate in the install plan', () => { + const homeDir = createTempDir('install-apply-home-'); + const projectDir = createTempDir('install-apply-project-'); + + try { + const result = run(['--profile', 'full', '--dry-run', '--json'], { cwd: projectDir, homeDir }); + assert.strictEqual(result.code, 0, result.stderr); + const parsed = JSON.parse(result.stdout); + assert.strictEqual(parsed.dryRun, true); + assert.ok(parsed.plan.selectedModuleIds.includes('workflow-quality')); + assert.ok( + parsed.plan.operations.some(operation => ( + String(operation.sourceRelativePath || '').replace(/\\/g, '/').startsWith('skills/delivery-gate/') + )), + 'Full profile dry-run should include the delivery-gate skill' + ); + } finally { + cleanup(homeDir); + cleanup(projectDir); + } + })) passed++; else failed++; + if (test('supports minimal profile dry-runs without hooks through the installer', () => { const homeDir = createTempDir('install-apply-home-'); const projectDir = createTempDir('install-apply-project-');