Files
Julius Brussee 82864c8f4c feat: ship Caveman 2.0.0 — engine, proxy, SDKs, unified CLI
Single-commit port of the Caveman 2 tree onto the original repo history
(candidate caveman-2@88878a4 applied over main@3098342). No unrelated-history
merge; original history untouched.

Preserved v1 surfaces:
- skills/caveman/SKILL.md byte-for-byte, plus plugin mirror and caveman.skill zip
- legacy CI (Node 18/20/22 installer + Python 3.11 suites) alongside engine-ci
- cli/install.js compatibility shim forwarding to bin/install.js
- .codex/config.toml ships both hooks and codex_hooks for older Codex (#617)
- README Trendshift + Skills.sh badges and local star-history asset
- v1.10.0-pinned curl installs; benchmarks/tmp-starcharts ignore rules

Fixed for public same-repo release:
- SECURITY.md vulnerability reporting points at this repository
- release-binaries.yml publishes to this repo via job-scoped GITHUB_TOKEN
  (cross-repo PAT removed); binary-release environment still gates signing
- packages/cli/PUBLISHING.md rewritten for packages/ layout and same-repo flow
- profile-contract and provider-catalog lanes skip lane-scope gates on
  initial import so the launch PR can gate on their real validation steps
- ANNOUNCEMENT.md network claim corrected: compression is local; requests
  still go to the provider you configured

Caveman 2 stays opt-in: bin-v1.0.0 binaries and @caveman-ai/cli publish
separately after this lands and soaks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BLvb5QniwYjmcawyssQNw9
2026-08-11 16:46:10 +02:00

77 lines
3.1 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert";
import { spawn } from "node:child_process";
import { mkdtempSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
const here = dirname(fileURLToPath(import.meta.url));
const cli = join(here, "..", "dist", "index.js");
const { RECIPES } = await import(pathToFileURL(join(here, "..", "dist", "recipes.generated.js")).href);
function run(args, env = {}) {
return new Promise((resolve, reject) => {
const child = spawn("node", [cli, ...args], {
env: {
...process.env,
NO_COLOR: "1",
HOME: mkdtempSync(join(tmpdir(), "cave-snippets-home-")),
CAVEMAN_HOME: mkdtempSync(join(tmpdir(), "cave-snippets-cave-")),
...env,
},
});
let stdout = "";
let stderr = "";
child.stdout.on("data", (d) => (stdout += d));
child.stderr.on("data", (d) => (stderr += d));
child.on("exit", (code) => resolve({ code, stdout, stderr }));
child.on("error", reject);
});
}
test("snippets list mode shows every recipe id", async () => {
assert.equal(RECIPES.length, 12, "registry fixture should contain 12 recipes");
const out = await run(["snippets"]);
assert.equal(out.code, 0, out.stderr);
for (const recipe of RECIPES) {
assert.match(out.stdout, new RegExp(`(^|\\n)${recipe.id}\\s+${recipe.display_name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}`));
}
assert.match(out.stdout, /usage: caveman snippets <id> \[--app <slug>\]/);
});
test("snippets renders selected recipe with gateway and app templates resolved", async () => {
const out = await run(["snippets", "openai-py", "--app", "checkout"], {
CAVE_GATEWAY_URL: "https://gateway.example.test",
});
assert.equal(out.code, 0, out.stderr);
assert.match(out.stdout, /# One constructor argument/);
assert.match(out.stdout, /\/w\/checkout\/openai\/v1/);
assert.doesNotMatch(out.stdout, /\{\{/);
});
test("unknown snippet recipe fails closed and lists valid ids", async () => {
const out = await run(["snippets", "not-a-recipe"]);
assert.notEqual(out.code, 0, "unknown recipe must exit non-zero");
assert.match(out.stderr, /unknown snippet recipe: not-a-recipe/);
for (const recipe of RECIPES) assert.match(out.stderr, new RegExp(recipe.id));
});
test("every snippet recipe renders without unresolved template markers", async () => {
for (const recipe of RECIPES) {
const out = await run(["snippets", recipe.id, "--app", "checkout"], {
CAVE_GATEWAY_URL: "http://127.0.0.1:8787",
});
assert.equal(out.code, 0, `${recipe.id}: ${out.stderr}`);
assert.doesNotMatch(out.stdout, /\{\{/, `${recipe.id} left a template marker unresolved`);
}
});
test("sdk snippet keeps legacy output and points to registry snippets", async () => {
const out = await run(["sdk", "snippet"]);
assert.equal(out.code, 0, out.stderr);
assert.match(out.stdout, /OpenAI baseURL: \$CAVE_GATEWAY_URL\/openai\/v1/);
assert.match(out.stdout, /TypeScript SDK: import \{ Cave \} from "@caveman-ai\/sdk"/);
assert.match(out.stdout, /caveman snippets openai-ts --app my-service/);
});