diff --git a/packages/agents-audit/CHANGELOG.md b/packages/agents-audit/CHANGELOG.md index 46292ce..240b5a8 100644 --- a/packages/agents-audit/CHANGELOG.md +++ b/packages/agents-audit/CHANGELOG.md @@ -1,5 +1,17 @@ # Changelog +## [0.4.3] - 2026-07-17 + +### Patch Changes + +- Fix the `agents-audit` CLI entry-point guard so it fires when invoked through npm's `.bin` symlink (`npx agents-audit`, `npm exec agents-audit`). The guard previously compared `resolve(process.argv[1])` against the resolved module URL, which never matched through a symlink — every subcommand (`generate`, `scan`) silently no-op'd and exited 0 instead of running. It now compares real paths via `realpathSync`. + + Also hardens `scripts/verify-package-tarball.mjs` for `agents-audit`: after packing and installing the tarball fresh, it now runs `npx agents-audit generate` and asserts `.agents/workspace.json` actually exists and parses, rather than trusting a clean exit code. + +- Updated dependencies + - @workspacejson/rules@0.4.3 + - @workspacejson/spec@0.4.3 + ## 0.4.2 ### Patch Changes diff --git a/packages/agents-audit/package.json b/packages/agents-audit/package.json index 7e8ceb7..a04d44c 100644 --- a/packages/agents-audit/package.json +++ b/packages/agents-audit/package.json @@ -1,6 +1,6 @@ { "name": "agents-audit", - "version": "0.4.2", + "version": "0.4.3", "description": "Audit tool for AGENTS.md hygiene - reads .agents/workspace.json for richer findings", "license": "Apache-2.0", "author": "workspace-json contributors", diff --git a/packages/agents-audit/src/cli.ts b/packages/agents-audit/src/cli.ts index 477e5de..9153abb 100644 --- a/packages/agents-audit/src/cli.ts +++ b/packages/agents-audit/src/cli.ts @@ -1,5 +1,6 @@ #!/usr/bin/env node import { resolve } from 'node:path'; +import { realpathSync } from 'node:fs'; import { createRequire } from 'node:module'; import { fileURLToPath } from 'node:url'; import { Command } from 'commander'; @@ -147,7 +148,10 @@ export async function runCli(argv: string[] = process.argv): Promise { return exitCode; } -if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { +// npm exposes package bins through node_modules/.bin symlinks. Resolve both +// paths before comparing them so the executable runs whether invoked directly +// or through npx/npm exec. +if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) { const exitCode = await runCli(process.argv); process.exit(exitCode); } diff --git a/packages/rules/CHANGELOG.md b/packages/rules/CHANGELOG.md index 9b15e5b..f6ce1ae 100644 --- a/packages/rules/CHANGELOG.md +++ b/packages/rules/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog +## [0.4.3] - 2026-07-17 + +### Patch Changes + +- Fix the `agents-audit` CLI entry-point guard so it fires when invoked through npm's `.bin` symlink (`npx agents-audit`, `npm exec agents-audit`). The guard previously compared `resolve(process.argv[1])` against the resolved module URL, which never matched through a symlink — every subcommand (`generate`, `scan`) silently no-op'd and exited 0 instead of running. It now compares real paths via `realpathSync`. + + Also hardens `scripts/verify-package-tarball.mjs` for `agents-audit`: after packing and installing the tarball fresh, it now runs `npx agents-audit generate` and asserts `.agents/workspace.json` actually exists and parses, rather than trusting a clean exit code. + +- Updated dependencies + - @workspacejson/spec@0.4.3 + ## 0.4.2 ### Patch Changes diff --git a/packages/rules/package.json b/packages/rules/package.json index 95d38a9..5a3a3c6 100644 --- a/packages/rules/package.json +++ b/packages/rules/package.json @@ -1,6 +1,6 @@ { "name": "@workspacejson/rules", - "version": "0.4.2", + "version": "0.4.3", "description": "Rule engine for auditing AGENTS.md hygiene using .agents/workspace.json", "license": "Apache-2.0", "author": "workspace-json contributors", diff --git a/packages/spec/CHANGELOG.md b/packages/spec/CHANGELOG.md index 281be99..4d33b45 100644 --- a/packages/spec/CHANGELOG.md +++ b/packages/spec/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## [0.4.3] - 2026-07-17 + +### Patch Changes + +- Fix the `agents-audit` CLI entry-point guard so it fires when invoked through npm's `.bin` symlink (`npx agents-audit`, `npm exec agents-audit`). The guard previously compared `resolve(process.argv[1])` against the resolved module URL, which never matched through a symlink — every subcommand (`generate`, `scan`) silently no-op'd and exited 0 instead of running. It now compares real paths via `realpathSync`. + + Also hardens `scripts/verify-package-tarball.mjs` for `agents-audit`: after packing and installing the tarball fresh, it now runs `npx agents-audit generate` and asserts `.agents/workspace.json` actually exists and parses, rather than trusting a clean exit code. + ## [0.4.2] - 2026-07-16 ### Added diff --git a/packages/spec/package.json b/packages/spec/package.json index 37ae573..4fdf5b1 100644 --- a/packages/spec/package.json +++ b/packages/spec/package.json @@ -1,6 +1,6 @@ { "name": "@workspacejson/spec", - "version": "0.4.2", + "version": "0.4.3", "description": "JSON Schema and TypeScript types for workspace.json", "license": "Apache-2.0", "author": "workspace-json contributors", diff --git a/scripts/verify-package-tarball.mjs b/scripts/verify-package-tarball.mjs index 705a29b..528e603 100644 --- a/scripts/verify-package-tarball.mjs +++ b/scripts/verify-package-tarball.mjs @@ -1,6 +1,7 @@ #!/usr/bin/env node -import { existsSync, readFileSync, rmSync } from "node:fs"; +import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs"; +import { tmpdir } from "node:os"; import { basename, join } from "node:path"; import { spawnSync } from "node:child_process"; @@ -40,6 +41,7 @@ try { assertNoWorkspaceProtocol(manifest, "package"); assertFixedGroupDependencies(manifest); assertRuntimeFiles(manifest, files); + if (packageName === "agents-audit") assertAgentsAuditBinGenerates(tarballPath); console.log(`Verified ${basename(tarballPath)} with ${packer}: packed manifest and runtime files are release-safe.`); } finally { rmSync(tarballPath, { force: true }); @@ -93,3 +95,47 @@ function assertRuntimeFiles(manifest, files) { function normalizeArchivePath(file) { return file.replace(/^\.\//, "").replaceAll("\\", "/").replace(/\/{2,}/g, "/"); } + +function assertAgentsAuditBinGenerates(tarballPath) { + const smokeDirectory = mkdtempSync(join(tmpdir(), "agents-audit-pack-")); + try { + writeFileSync(join(smokeDirectory, "package.json"), JSON.stringify({ private: true })); + // agents-audit's packed manifest depends on @workspacejson/rules and + // @workspacejson/spec at this same fixed-group version, which is not yet + // on the npm registry pre-publish. Pack and install those siblings from + // disk too, so the install resolves locally instead of hitting the registry. + const siblingTarballs = ["../rules", "../spec"].map((relative) => + packSibling(join(packageDirectory, relative), smokeDirectory), + ); + run("npm", ["install", "--ignore-scripts", "--no-package-lock", ...siblingTarballs, tarballPath], smokeDirectory); + run("npx", ["--no-install", "agents-audit", "generate"], smokeDirectory); + + const artifact = join(smokeDirectory, ".agents", "workspace.json"); + if (!existsSync(artifact)) { + throw new Error("Packed agents-audit bin exited without creating .agents/workspace.json."); + } + JSON.parse(readFileSync(artifact, "utf8")); + } finally { + rmSync(smokeDirectory, { recursive: true, force: true }); + } +} + +function packSibling(siblingDirectory, destinationDirectory) { + const siblingManifest = JSON.parse(readFileSync(join(siblingDirectory, "package.json"), "utf8")); + const siblingTarballName = `${siblingManifest.name.replace(/^@/, "").replaceAll("/", "-")}-${siblingManifest.version}.tgz`; + const packArgs = packer === "npm" + ? ["pack", "--ignore-scripts", "--pack-destination", destinationDirectory] + : ["pack", "--pack-destination", destinationDirectory]; + const packed = spawnSync(packer, packArgs, { cwd: siblingDirectory, encoding: "utf8" }); + process.stdout.write(packed.stdout); + process.stderr.write(packed.stderr); + if (packed.status !== 0) throw new Error(`${packer} pack failed for ${siblingDirectory}.`); + return join(destinationDirectory, siblingTarballName); +} + +function run(command, args, cwd) { + const result = spawnSync(command, args, { cwd, encoding: "utf8" }); + process.stdout.write(result.stdout); + process.stderr.write(result.stderr); + if (result.status !== 0) throw new Error(`${command} ${args.join(" ")} failed with exit ${result.status ?? 1}.`); +}