diff --git a/packages/gittensory-miner/README.md b/packages/gittensory-miner/README.md index 4959b3ec47..42b08ab419 100644 --- a/packages/gittensory-miner/README.md +++ b/packages/gittensory-miner/README.md @@ -56,6 +56,7 @@ From a local checkout: ```sh npm install npm --workspace @jsonbored/gittensory-miner run build +npm link --workspace @jsonbored/gittensory-miner ``` ## Commands diff --git a/test/unit/miner-package-skeleton.test.ts b/test/unit/miner-package-skeleton.test.ts new file mode 100644 index 0000000000..96bc727fbb --- /dev/null +++ b/test/unit/miner-package-skeleton.test.ts @@ -0,0 +1,70 @@ +import { execFileSync } from "node:child_process"; +import { readFileSync } from "node:fs"; +import { join } from "node:path"; +import { describe, expect, it } from "vitest"; + +import { runCapture } from "./support/miner-cli-harness.js"; + +const minerRoot = join(process.cwd(), "packages/gittensory-miner"); +const mcpRoot = join(process.cwd(), "packages/gittensory-mcp"); +const readmePath = join(minerRoot, "README.md"); + +type PackageJson = { + name: string; + license: string; + type: string; + bin: Record; + files: string[]; + publishConfig: { access: string }; + dependencies: Record; + engines: { node: string }; + scripts: { build: string }; +}; + +function readPackageJson(root: string): PackageJson { + return JSON.parse(readFileSync(join(root, "package.json"), "utf8")) as PackageJson; +} + +describe("gittensory-miner package skeleton (#2287)", () => { + it("mirrors gittensory-mcp packaging conventions", () => { + const miner = readPackageJson(minerRoot); + const mcp = readPackageJson(mcpRoot); + + expect(miner.name).toBe("@jsonbored/gittensory-miner"); + expect(miner.license).toBe("AGPL-3.0-only"); + expect(miner.type).toBe("module"); + expect(miner.bin).toEqual({ "gittensory-miner": "bin/gittensory-miner.js" }); + expect(miner.publishConfig).toEqual(mcp.publishConfig); + expect(miner.dependencies["@jsonbored/gittensory-engine"]).toBeDefined(); + expect(miner.engines.node).toMatch(/^>=22(?:\.\d+){0,2}$/); + expect(miner.files).toEqual(expect.arrayContaining(["bin", "lib"])); + expect(miner.scripts.build.startsWith("node --check bin/gittensory-miner.js")).toBe(true); + }); + + it("starts the CLI bin with a node shebang", () => { + const bin = readFileSync(join(minerRoot, "bin/gittensory-miner.js"), "utf8"); + expect(bin.startsWith("#!/usr/bin/env node\n")).toBe(true); + }); + + it("is discoverable from the repo root workspace", () => { + const listing = execFileSync( + "npm", + ["ls", "--workspace", "@jsonbored/gittensory-miner", "--depth=0"], + { cwd: process.cwd(), encoding: "utf8" }, + ); + expect(listing).toContain("@jsonbored/gittensory-miner@"); + }); + + it("serves --help and --version from the bin entry", () => { + expect(runCapture(["--help", "--no-update-check"])).toContain("gittensory-miner --help"); + expect(runCapture(["--version", "--no-update-check"])).toContain("@jsonbored/gittensory-miner/"); + }); + + it("documents foundation scope and local checkout install paths in the README", () => { + const readme = readFileSync(readmePath, "utf8"); + expect(readme).toContain("foundation phase"); + expect(readme).toContain("npm link --workspace @jsonbored/gittensory-miner"); + expect(readme).toContain("gittensory-miner --help"); + expect(readme).toContain("gittensory-miner --version"); + }); +});