diff --git a/.gitignore b/.gitignore index 40b010c..5c367c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ .worktrees/ node_modules/ +.pi/ diff --git a/README.md b/README.md index 427d077..f49ada7 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,17 @@ ## Install -Use it as a local package root today: +Local path: ```bash pi install /absolute/path/to/web-search ``` -After this folder is moved into its own repository, the same package can be installed from git. +Git: + +```bash +pi install https://gitea.rwiesner.com/pi/pi-web-search +``` ## Resources diff --git a/package.json b/package.json index 4f565af..cee70fc 100644 --- a/package.json +++ b/package.json @@ -1,8 +1,13 @@ { "name": "pi-web-search", "version": "0.1.0", + "description": "Pi extension package that adds web_search and web_fetch tools backed by pluggable providers such as Exa and Tavily.", "type": "module", "keywords": ["pi-package"], + "repository": { + "type": "git", + "url": "https://gitea.rwiesner.com/pi/pi-web-search" + }, "scripts": { "test": "tsx --test src/*.test.ts src/**/*.test.ts" }, diff --git a/src/.npmignore b/src/.npmignore new file mode 100644 index 0000000..8c1ce17 --- /dev/null +++ b/src/.npmignore @@ -0,0 +1,2 @@ +*.test.ts +**/*.test.ts diff --git a/src/package-manifest.test.ts b/src/package-manifest.test.ts index 43469bb..c0f1a95 100644 --- a/src/package-manifest.test.ts +++ b/src/package-manifest.test.ts @@ -1,12 +1,25 @@ import test from "node:test"; import assert from "node:assert/strict"; -import { existsSync, readFileSync } from "node:fs"; +import { readFileSync } from "node:fs"; import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; +import { execFileSync } from "node:child_process"; const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), ".."); const pkg = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8")); +function getPackedPaths(cwd: string) { + const out = execFileSync("npm", ["pack", "--dry-run", "--json"], { + cwd, + encoding: "utf8", + stdio: ["ignore", "pipe", "pipe"], + timeout: 30_000, + }); + const parsed = JSON.parse(out); + // npm pack --dry-run --json returns array with first entry containing files + return (parsed[0]?.files ?? []).map((f: { path: string }) => f.path); +} + test("package.json exposes pi-web-search as a standalone pi package", () => { assert.equal(pkg.name, "pi-web-search"); assert.equal(pkg.type, "module"); @@ -16,15 +29,42 @@ test("package.json exposes pi-web-search as a standalone pi package", () => { extensions: ["./index.ts"], }); + // description + repository exact match + assert.equal( + pkg.description, + "Pi extension package that adds web_search and web_fetch tools backed by pluggable providers such as Exa and Tavily." + ); + assert.deepEqual(pkg.repository, { + type: "git", + url: "https://gitea.rwiesner.com/pi/pi-web-search", + }); + assert.equal(pkg.peerDependencies["@mariozechner/pi-coding-agent"], "*"); assert.equal(pkg.peerDependencies["@mariozechner/pi-tui"], "*"); assert.equal(pkg.peerDependencies["@sinclair/typebox"], "*"); assert.ok("exa-js" in (pkg.dependencies ?? {})); assert.ok(!("@sinclair/typebox" in (pkg.dependencies ?? {}))); - assert.equal(pkg.bundledDependencies, undefined); assert.deepEqual(pkg.files, ["index.ts", "src"]); - assert.ok(existsSync(resolve(packageRoot, "index.ts"))); - assert.ok(existsSync(resolve(packageRoot, "src/runtime.ts"))); - assert.ok(existsSync(resolve(packageRoot, "src/tools/web-search.ts"))); + // ensure manifest does not bundle dependencies by default + assert.equal(pkg.bundledDependencies, undefined); +}); + +test("README contains local and git install examples", () => { + const readme = readFileSync(resolve(packageRoot, "README.md"), "utf8"); + assert.match(readme, /pi install \/absolute\/path\/to\/web-search/); + assert.match(readme, /pi install https:\/\/gitea.rwiesner.com\/pi\/pi-web-search/); +}); + +test("npm pack includes expected assets and excludes .test.ts files", () => { + const packedPaths = getPackedPaths(packageRoot); + + // meaningful pack assertions + assert.ok(packedPaths.includes("index.ts"), "index.ts should be included in package"); + assert.ok(packedPaths.includes("src/runtime.ts"), "src/runtime.ts should be included in package"); + assert.ok(packedPaths.includes("src/tools/web-search.ts"), "src/tools/web-search.ts should be included in package"); + assert.ok(packedPaths.includes("src/tools/web-fetch.ts"), "src/tools/web-fetch.ts should be included in package"); + + // no test files packed + assert.deepEqual(packedPaths.filter((p) => p.endsWith(".test.ts")), []); });