Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ jobs:
version: 9.15.4
- uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
cache: pnpm
registry-url: https://registry.npmjs.org
- run: pnpm install --frozen-lockfile
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ apps/docs/tsconfig.tsbuildinfo
apps/docs/AGENTS.md
apps/docs/CLAUDE.md
dist/
.dist/
packages/agents/dist-test/
coverage/
.pnpm-store/
.turbo/
Expand Down
1 change: 1 addition & 0 deletions .knip.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"entry": ["scripts/*.ts"],
"ignoreDependencies": [
"@semantic-release/commit-analyzer",
"@semantic-release/github",
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions biome.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
"!**/.omx",
"!**/node_modules",
"!**/dist",
"!**/dist-test",
"!**/.dist",
"!**/coverage",
"!**/.next",
"!**/.source",
Expand Down
8 changes: 5 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"name": "agent-compat",
"private": true,
"type": "module",
"description": "Workspace for the @jstn-sdk/agents compatibility SDK",
"packageManager": "pnpm@9.15.4",
"engines": {
Expand All @@ -12,9 +13,10 @@
"test": "pnpm --filter @jstn-sdk/agents test",
"verify": "pnpm --filter @jstn-sdk/agents verify",
"publish:agents": "pnpm --filter @jstn-sdk/agents publish --access public",
"publish:github": "node scripts/publish-github-package.mjs",
"live-smoke": "node scripts/live-smoke.mjs",
"matrix:check": "node scripts/check-support-matrix.mjs",
"scripts:build": "tsc -p scripts/tsconfig.json",
"publish:github": "pnpm scripts:build && node .dist/scripts/publish-github-package.js",
"live-smoke": "pnpm scripts:build && node .dist/scripts/live-smoke.js",
"matrix:check": "pnpm scripts:build && node .dist/scripts/check-support-matrix.js",
"check": "pnpm format:check && pnpm build && pnpm typecheck && pnpm test && pnpm matrix:check && pnpm docs:content && pnpm knip",
"format": "biome format --write .",
"format:check": "biome format .",
Expand Down
4 changes: 2 additions & 2 deletions packages/agents/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
"scripts": {
"build": "tsc -p tsconfig.json",
"typecheck": "tsc --noEmit -p tsconfig.json",
"test": "pnpm build && node --test",
"verify": "pnpm build && node --test test/conformance.test.js"
"test": "pnpm build && tsc -p tsconfig.test.json && node --test dist-test/test/*.test.js",
"verify": "pnpm build && tsc -p tsconfig.test.json && node --test dist-test/test/conformance.test.js"
},
"engines": {
"node": ">=18"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { test } from "node:test";
import { Agents } from "../dist/index.js";
import { verifyAdapter } from "../dist/testing/index.js";
import { Agents } from "../src/index.js";
import { verifyAdapter } from "../src/testing/index.js";

test("every registered adapter passes compile/write/read/validate conformance", async (t) => {
const manifest = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import test from "node:test";
import { Agents } from "../dist/index.js";
import { Agents } from "../src/index.js";

const invalid = (value: unknown) => value as never;

test("detect, compile, validate", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "agents-"));
Expand Down Expand Up @@ -63,7 +65,7 @@ test("detect, compile, validate", async () => {
assert.match(rendered, /## Roles/);

const report = await Agents.validate(root);
assert.equal(report.cursor, "✓");
assert.equal((report as Record<string, any>).cursor, "✓");
assert.equal(report.valid, true);
});

Expand Down Expand Up @@ -182,7 +184,6 @@ test("compiles native artifacts for all targets and preserves unmanaged files",
},
},
{
root,
output: root,
targets: [
"cursor",
Expand Down Expand Up @@ -293,40 +294,40 @@ test("dry-run and validation expose real artifact state", async () => {
"<!-- agents:begin -->\n---\ndescription: broken\n---\n<!-- agents:end -->\n",
);
const report = await Agents.validate(root);
assert.equal(report.cursor, "×");
assert.equal(report.cursorValidation.status, "invalid");
assert.equal(report.results.cursor.status, "invalid");
const legacyReport = report as Record<string, any>;
assert.equal(legacyReport.cursor, "×");
assert.equal(legacyReport.cursorValidation.status, "invalid");
assert.equal(legacyReport.results.cursor.status, "invalid");
await fs.rm(root, { recursive: true, force: true });
});

test("rejects malformed manifests and unknown targets", async () => {
await assert.rejects(
() => Agents.compile({ version: 2 }, { targets: ["cursor"] }),
() => Agents.compile(invalid({ version: 2 }), { targets: ["cursor"] }),
/manifest.version/,
);
await assert.rejects(
() =>
Agents.compile(
{
invalid({
version: 1,
workflows: { implementation: { steps: "not-an-array" } },
},
}),
{ targets: ["cursor"] },
),
/workflow implementation\.steps/,
);
await assert.rejects(
() =>
Agents.compile(
{ version: 1, targets: { auto: "yes" } },
{ targets: ["cursor"] },
),
Agents.compile(invalid({ version: 1, targets: { auto: "yes" } }), {
targets: ["cursor"],
}),
/manifest\.targets\.auto/,
);
await assert.rejects(
() =>
Agents.compile(
{ version: 1, roles: { reviewer: { permissions: "read" } } },
invalid({ version: 1, roles: { reviewer: { permissions: "read" } } }),
{ targets: ["cursor"] },
),
/role reviewer\.permissions/,
Expand All @@ -336,17 +337,22 @@ test("rejects malformed manifests and unknown targets", async () => {
["workflows", []],
["roles", []],
["targets", []],
]) {
] as const) {
await assert.rejects(
() =>
Agents.compile({ version: 1, [field]: value }, { targets: ["cursor"] }),
Agents.compile(invalid({ version: 1, [field]: value }), {
targets: ["cursor"],
}),
new RegExp(`manifest\\.${field} must be an object`),
);
}
await assert.rejects(
() =>
Agents.compile(
{ version: 1, skills: { review: { instructions: ["ok", 1] } } },
invalid({
version: 1,
skills: { review: { instructions: ["ok", 1] } },
}),
{ targets: ["cursor"] },
),
/skill review\.instructions must be an array of strings/,
Expand Down Expand Up @@ -410,7 +416,7 @@ test("scopes validation and reports shared output collisions", async () => {
const report = await Agents.validate(root, { targets: ["cursor"] });
assert.deepEqual(report.targets, ["cursor"]);
assert.equal(report.valid, true);
assert.equal(report.results.codex, undefined);
assert.equal((report as Record<string, any>).results.codex, undefined);

const collision = await Agents.compile(
{ version: 1, project: { name: "collision" }, instructions: [] },
Expand Down
11 changes: 11 additions & 0 deletions packages/agents/tsconfig.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "./tsconfig.json",
"compilerOptions": {
"rootDir": ".",
"outDir": "./dist-test",
"declaration": false,
"declarationMap": false,
"sourceMap": false
},
"include": ["src/**/*.ts", "test/**/*.ts"]
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { readFile } from "node:fs/promises";
import { Agents } from "../packages/agents/dist/index.js";
import { join } from "node:path";
import { pathToFileURL } from "node:url";

const { Agents } = (await import(
pathToFileURL(join(process.cwd(), "packages/agents/dist/index.js")).href
)) as typeof import("../packages/agents/dist/index.js");

const matrix = await readFile("docs/support-matrix.md", "utf8");
const adapters = Agents.list();
Expand Down
25 changes: 19 additions & 6 deletions scripts/live-smoke.mjs → scripts/live-smoke.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ import { tmpdir } from "node:os";
import path from "node:path";
import { execFile } from "node:child_process";
import { promisify } from "node:util";
import { Agents } from "../packages/agents/dist/index.js";
import { pathToFileURL } from "node:url";

const { Agents } = (await import(
pathToFileURL(path.join(process.cwd(), "packages/agents/dist/index.js")).href
)) as typeof import("../packages/agents/dist/index.js");

const run = promisify(execFile);
const manifest = {
version: 1,
version: 1 as const,
project: { name: "live-runtime-smoke" },
instructions: ["Use the generated project instructions."],
};
Expand All @@ -16,7 +20,7 @@ const runtimes = [
["claude-code", "claude"],
["cursor", "cursor"],
["hermes", "hermes"],
];
] as const;
const root = await mkdtemp(path.join(tmpdir(), "agent-compat-live-"));
let failures = 0;

Expand All @@ -27,19 +31,28 @@ try {
targets: [id],
});
const validation = await Agents.validate(root, { targets: [id] });
if (!compiled.success || validation.results[id]?.status !== "valid") {
const result = (validation.results as Record<string, { status: string }>)[
id
];
if (!compiled.success || result?.status !== "valid") {
console.error(`${id}: FAIL generated artifact validation`);
failures += 1;
continue;
}
try {
await run(binary, ["--help"], { timeout: 15_000, maxBuffer: 20_000 });
} catch (error) {
if (error.code === "ENOENT") {
if (
error &&
typeof error === "object" &&
"code" in error &&
error.code === "ENOENT"
) {
console.log(`${id}: SKIP (${binary} is not installed)`);
continue;
}
console.error(`${id}: FAIL runtime startup (${error.message})`);
const message = error instanceof Error ? error.message : String(error);
console.error(`${id}: FAIL runtime startup (${message})`);
failures += 1;
continue;
}
Expand Down
4 changes: 4 additions & 0 deletions scripts/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"private": true,
"type": "module"
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path";
import { spawn } from "node:child_process";

const packageRoot = new URL("../packages/agents/", import.meta.url);
const packageRoot = new URL("../../packages/agents/", import.meta.url);
const packageJson = JSON.parse(
await readFile(new URL("package.json", packageRoot), "utf8"),
);
Expand Down Expand Up @@ -33,7 +33,7 @@ try {
)}\n`,
);

await new Promise((resolve, reject) => {
await new Promise<void>((resolve, reject) => {
const child = spawn(
"npm",
[
Expand All @@ -43,11 +43,7 @@ try {
"--registry",
"https://npm.pkg.github.com",
],
{
cwd: stage,
stdio: "inherit",
env: process.env,
},
{ cwd: stage, stdio: "inherit", env: process.env },
);
child.on("error", reject);
child.on("exit", (code) =>
Expand Down
17 changes: 17 additions & 0 deletions scripts/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"rootDir": ".",
"outDir": "../.dist/scripts",
"strict": true,
"skipLibCheck": true,
"types": ["node"],
"typeRoots": [
"../node_modules/@types",
"../packages/agents/node_modules/@types"
]
},
"include": ["./*.ts"]
}
Loading