From bd2aec48edb23757c13e32b0f30dabd115d5d10c Mon Sep 17 00:00:00 2001 From: Affaan Mustafa Date: Sun, 12 Apr 2026 23:48:53 -0700 Subject: [PATCH] fix: narrow npm publish surface to the module graph --- package.json | 214 +++++++++++++++++----- tests/scripts/build-opencode.test.js | 2 +- tests/scripts/npm-publish-surface.test.js | 149 +++++++++++++++ 3 files changed, 321 insertions(+), 44 deletions(-) create mode 100644 tests/scripts/npm-publish-surface.test.js diff --git a/package.json b/package.json index 4266e884..70649ddc 100644 --- a/package.json +++ b/package.json @@ -39,69 +39,197 @@ }, "files": [ ".agents/", + ".claude-plugin/", ".codex/", + ".codex-plugin/", ".cursor/", - ".opencode/commands/", - ".opencode/dist/", - ".opencode/instructions/", - ".opencode/plugins/", - ".opencode/prompts/", - ".opencode/tools/", - ".opencode/index.ts", - ".opencode/opencode.json", - ".opencode/package.json", - ".opencode/package-lock.json", - ".opencode/tsconfig.json", - ".opencode/MIGRATION.md", - ".opencode/README.md", + ".gemini/", + ".opencode/", + ".mcp.json", + "AGENTS.md", + "VERSION", + "agent.yaml", "agents/", "commands/", - "contexts/", - "examples/CLAUDE.md", - "examples/user-CLAUDE.md", - "examples/statusline.json", "hooks/", + "install.ps1", + "install.sh", "manifests/", "mcp-configs/", - "plugins/", "rules/", - "schemas/", - "scripts/ci/", + "scripts/catalog.js", + "scripts/claw.js", + "scripts/codex/merge-codex-config.js", + "scripts/codex/merge-mcp-config.js", + "scripts/doctor.js", "scripts/ecc.js", "scripts/gemini-adapt-agents.js", + "scripts/harness-audit.js", "scripts/hooks/", - "scripts/lib/", - "scripts/claw.js", - "scripts/doctor.js", - "scripts/status.js", - "scripts/sessions-cli.js", "scripts/install-apply.js", "scripts/install-plan.js", + "scripts/lib/", "scripts/list-installed.js", "scripts/orchestration-status.js", "scripts/orchestrate-codex-worker.sh", "scripts/orchestrate-worktrees.js", + "scripts/repair.js", + "scripts/session-inspect.js", + "scripts/sessions-cli.js", "scripts/setup-package-manager.js", "scripts/skill-create-output.js", - "scripts/codex/merge-codex-config.js", - "scripts/codex/merge-mcp-config.js", - "scripts/repair.js", - "scripts/harness-audit.js", - "scripts/session-inspect.js", + "scripts/status.js", "scripts/uninstall.js", - "skills/", - "AGENTS.md", - "agent.yaml", - ".claude-plugin/plugin.json", - ".claude-plugin/marketplace.json", - ".claude-plugin/README.md", - ".codex-plugin/plugin.json", - ".codex-plugin/README.md", - ".mcp.json", - "install.sh", - "install.ps1", - "llms.txt", - "VERSION" + "skills/agent-harness-construction/", + "skills/agent-introspection-debugging/", + "skills/agent-sort/", + "skills/agentic-engineering/", + "skills/ai-first-engineering/", + "skills/ai-regression-testing/", + "skills/android-clean-architecture/", + "skills/api-connector-builder/", + "skills/api-design/", + "skills/article-writing/", + "skills/automation-audit-ops/", + "skills/autonomous-loops/", + "skills/backend-patterns/", + "skills/blueprint/", + "skills/brand-voice/", + "skills/carrier-relationship-management/", + "skills/claude-api/", + "skills/claude-devfleet/", + "skills/clickhouse-io/", + "skills/code-tour/", + "skills/coding-standards/", + "skills/compose-multiplatform-patterns/", + "skills/configure-ecc/", + "skills/connections-optimizer/", + "skills/content-engine/", + "skills/content-hash-cache-pattern/", + "skills/continuous-agent-loop/", + "skills/continuous-learning/", + "skills/continuous-learning-v2/", + "skills/cost-aware-llm-pipeline/", + "skills/council/", + "skills/cpp-coding-standards/", + "skills/cpp-testing/", + "skills/crosspost/", + "skills/csharp-testing/", + "skills/customer-billing-ops/", + "skills/customs-trade-compliance/", + "skills/dart-flutter-patterns/", + "skills/dashboard-builder/", + "skills/data-scraper-agent/", + "skills/database-migrations/", + "skills/deep-research/", + "skills/defi-amm-security/", + "skills/deployment-patterns/", + "skills/django-patterns/", + "skills/django-security/", + "skills/django-tdd/", + "skills/django-verification/", + "skills/dmux-workflows/", + "skills/docker-patterns/", + "skills/dotnet-patterns/", + "skills/e2e-testing/", + "skills/ecc-tools-cost-audit/", + "skills/email-ops/", + "skills/energy-procurement/", + "skills/enterprise-agent-ops/", + "skills/eval-harness/", + "skills/evm-token-decimals/", + "skills/exa-search/", + "skills/fal-ai-media/", + "skills/finance-billing-ops/", + "skills/foundation-models-on-device/", + "skills/frontend-design/", + "skills/frontend-patterns/", + "skills/frontend-slides/", + "skills/github-ops/", + "skills/golang-patterns/", + "skills/golang-testing/", + "skills/google-workspace-ops/", + "skills/healthcare-phi-compliance/", + "skills/hipaa-compliance/", + "skills/hookify-rules/", + "skills/inventory-demand-planning/", + "skills/investor-materials/", + "skills/investor-outreach/", + "skills/iterative-retrieval/", + "skills/java-coding-standards/", + "skills/jira-integration/", + "skills/jpa-patterns/", + "skills/knowledge-ops/", + "skills/kotlin-coroutines-flows/", + "skills/kotlin-exposed-patterns/", + "skills/kotlin-ktor-patterns/", + "skills/kotlin-patterns/", + "skills/kotlin-testing/", + "skills/laravel-patterns/", + "skills/laravel-plugin-discovery/", + "skills/laravel-security/", + "skills/laravel-tdd/", + "skills/laravel-verification/", + "skills/lead-intelligence/", + "skills/liquid-glass-design/", + "skills/llm-trading-agent-security/", + "skills/logistics-exception-management/", + "skills/manim-video/", + "skills/market-research/", + "skills/mcp-server-patterns/", + "skills/messages-ops/", + "skills/nanoclaw-repl/", + "skills/nestjs-patterns/", + "skills/nodejs-keccak256/", + "skills/nutrient-document-processing/", + "skills/perl-patterns/", + "skills/perl-security/", + "skills/perl-testing/", + "skills/plankton-code-quality/", + "skills/postgres-patterns/", + "skills/product-capability/", + "skills/production-scheduling/", + "skills/project-flow-ops/", + "skills/prompt-optimizer/", + "skills/python-patterns/", + "skills/python-testing/", + "skills/quality-nonconformance/", + "skills/ralphinho-rfc-pipeline/", + "skills/regex-vs-llm-structured-text/", + "skills/remotion-video-creation/", + "skills/research-ops/", + "skills/returns-reverse-logistics/", + "skills/rust-patterns/", + "skills/rust-testing/", + "skills/search-first/", + "skills/security-bounty-hunter/", + "skills/security-review/", + "skills/security-scan/", + "skills/seo/", + "skills/skill-stocktake/", + "skills/social-graph-ranker/", + "skills/springboot-patterns/", + "skills/springboot-security/", + "skills/springboot-tdd/", + "skills/springboot-verification/", + "skills/strategic-compact/", + "skills/swift-actor-persistence/", + "skills/swift-concurrency-6-2/", + "skills/swift-protocol-di-testing/", + "skills/swiftui-patterns/", + "skills/tdd-workflow/", + "skills/team-builder/", + "skills/terminal-ops/", + "skills/token-budget-advisor/", + "skills/ui-demo/", + "skills/unified-notifications-ops/", + "skills/verification-loop/", + "skills/video-editing/", + "skills/videodb/", + "skills/visa-doc-translate/", + "skills/workspace-surface-audit/", + "skills/x-api/", + "the-security-guide.md" ], "bin": { "ecc": "scripts/ecc.js", diff --git a/tests/scripts/build-opencode.test.js b/tests/scripts/build-opencode.test.js index 5d2b5d7b..d4352d73 100644 --- a/tests/scripts/build-opencode.test.js +++ b/tests/scripts/build-opencode.test.js @@ -35,7 +35,7 @@ function main() { ["package.json exposes the OpenCode build and prepack hooks", () => { assert.strictEqual(packageJson.scripts["build:opencode"], "node scripts/build-opencode.js") assert.strictEqual(packageJson.scripts.prepack, "npm run build:opencode") - assert.ok(packageJson.files.includes(".opencode/dist/")) + assert.ok(packageJson.files.includes(".opencode/")) }], ["build script generates .opencode/dist", () => { const result = spawnSync("node", [buildScript], { diff --git a/tests/scripts/npm-publish-surface.test.js b/tests/scripts/npm-publish-surface.test.js new file mode 100644 index 00000000..3d275393 --- /dev/null +++ b/tests/scripts/npm-publish-surface.test.js @@ -0,0 +1,149 @@ +/** + * Tests for the npm publish surface contract. + */ + +const assert = require("assert") +const fs = require("fs") +const path = require("path") +const { spawnSync } = require("child_process") + +function runTest(name, fn) { + try { + fn() + console.log(` ✓ ${name}`) + return true + } catch (error) { + console.log(` ✗ ${name}`) + console.error(` ${error.message}`) + return false + } +} + +function normalizePublishPath(value) { + return String(value).replace(/\\/g, "/").replace(/\/$/, "") +} + +function isCoveredByAncestor(target, roots) { + const parts = target.split("/") + for (let index = 1; index < parts.length; index += 1) { + const ancestor = parts.slice(0, index).join("/") + if (roots.has(ancestor)) { + return true + } + } + return false +} + +function buildExpectedPublishPaths(repoRoot) { + const modules = JSON.parse( + fs.readFileSync(path.join(repoRoot, "manifests", "install-modules.json"), "utf8") + ).modules + + const extraPaths = [ + "manifests", + "scripts/ecc.js", + "scripts/catalog.js", + "scripts/claw.js", + "scripts/doctor.js", + "scripts/status.js", + "scripts/sessions-cli.js", + "scripts/install-apply.js", + "scripts/install-plan.js", + "scripts/list-installed.js", + "scripts/skill-create-output.js", + "scripts/repair.js", + "scripts/harness-audit.js", + "scripts/session-inspect.js", + "scripts/uninstall.js", + "scripts/gemini-adapt-agents.js", + "scripts/codex/merge-codex-config.js", + "scripts/codex/merge-mcp-config.js", + ".codex-plugin", + ".mcp.json", + "install.sh", + "install.ps1", + "agent.yaml", + "VERSION", + ] + + const combined = new Set( + [...modules.flatMap((module) => module.paths || []), ...extraPaths].map(normalizePublishPath) + ) + + return [...combined] + .filter((publishPath) => !isCoveredByAncestor(publishPath, combined)) + .sort() +} + +function main() { + console.log("\n=== Testing npm publish surface ===\n") + + let passed = 0 + let failed = 0 + + const repoRoot = path.join(__dirname, "..", "..") + const packageJson = JSON.parse( + fs.readFileSync(path.join(repoRoot, "package.json"), "utf8") + ) + + const expectedPublishPaths = buildExpectedPublishPaths(repoRoot) + const actualPublishPaths = packageJson.files.map(normalizePublishPath).sort() + + const tests = [ + ["package.json files align to the module graph and explicit runtime allowlist", () => { + assert.deepStrictEqual(actualPublishPaths, expectedPublishPaths) + }], + ["npm pack publishes the reduced runtime surface", () => { + const result = spawnSync("npm", ["pack", "--dry-run", "--json"], { + cwd: repoRoot, + encoding: "utf8", + shell: process.platform === "win32", + }) + assert.strictEqual(result.status, 0, result.error?.message || result.stderr) + + const packOutput = JSON.parse(result.stdout) + const packagedPaths = new Set(packOutput[0]?.files?.map((file) => file.path) ?? []) + + for (const requiredPath of [ + "scripts/catalog.js", + ".gemini/GEMINI.md", + ".claude-plugin/plugin.json", + ".codex-plugin/plugin.json", + "skills/backend-patterns/SKILL.md", + ]) { + assert.ok( + packagedPaths.has(requiredPath), + `npm pack should include ${requiredPath}` + ) + } + + for (const excludedPath of [ + "contexts/dev.md", + "examples/CLAUDE.md", + "plugins/README.md", + "schemas/hooks.schema.json", + "scripts/ci/catalog.js", + "skills/skill-comply/SKILL.md", + ]) { + assert.ok( + !packagedPaths.has(excludedPath), + `npm pack should not include ${excludedPath}` + ) + } + }], + ] + + for (const [name, fn] of tests) { + if (runTest(name, fn)) { + passed += 1 + } else { + failed += 1 + } + } + + console.log(`\nPassed: ${passed}`) + console.log(`Failed: ${failed}`) + process.exit(failed > 0 ? 1 : 0) +} + +main()