diff --git a/package.json b/package.json index d2eef977..7ec27984 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "dev:cli": "node packages/cli/dist/cli.js", "typecheck": "pnpm -r typecheck && pnpm run typecheck:examples", "typecheck:examples": "tsc -p examples/tsconfig.json --noEmit", - "test": "node --test scripts/release-workflows.test.mjs && pnpm -r test && pnpm run test:e2e:agent-card", + "test": "node --test \"scripts/*.test.mjs\" && pnpm -r test && pnpm run test:e2e:agent-card", "test:e2e:agent-card": "pnpm -r build && tsx scripts/e2e-agent-card.ts", "lint": "pnpm -r lint", "check": "pnpm run lint && pnpm run typecheck && pnpm run test" diff --git a/packages/cli/package.json b/packages/cli/package.json index f1dcd0bb..fe11ac3d 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -16,7 +16,9 @@ "@agentworkforce/local-surface": "workspace:*", "@agentworkforce/persona-kit": "workspace:*", "@agentworkforce/persona-registry": "workspace:*", + "@agentworkforce/review-kit": "workspace:*", "@agentworkforce/runtime": "workspace:*", + "@agentworkforce/turn-kit": "workspace:*", "@agentworkforce/workload-router": "workspace:*", "@relayburn/sdk": "^2.5.2", "@relayfile/local-mount": "^0.10.23", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d3707932..35a56657 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -61,9 +61,15 @@ importers: '@agentworkforce/persona-registry': specifier: workspace:* version: link:../persona-registry + '@agentworkforce/review-kit': + specifier: workspace:* + version: link:../review-kit '@agentworkforce/runtime': specifier: workspace:* version: link:../runtime + '@agentworkforce/turn-kit': + specifier: workspace:* + version: link:../turn-kit '@agentworkforce/workload-router': specifier: workspace:* version: link:../workload-router diff --git a/scripts/authoring-kits.test.mjs b/scripts/authoring-kits.test.mjs new file mode 100644 index 00000000..9f9402b7 --- /dev/null +++ b/scripts/authoring-kits.test.mjs @@ -0,0 +1,72 @@ +import assert from 'node:assert/strict'; +import { readFileSync, readdirSync } from 'node:fs'; +import test from 'node:test'; + +/** + * A persona is authored as `persona.ts` and compiled by the CLI, which resolves + * the file's imports out of the CLI's own install tree — the user's repo + * usually has no `node_modules` at all when the CLI is installed globally. + * + * So every kit a persona.ts can import has to ship inside that tree, or + * `agentworkforce deploy ./persona.ts` dies with "Could not resolve" on a fresh + * install. persona-kit was already there; turn-kit and review-kit were not, + * which is the same failure workforce#325 fixed for persona-kit. + */ +function packageJson(dir) { + return JSON.parse(readFileSync(`packages/${dir}/package.json`, 'utf8')); +} + +function personaAuthoringKits() { + const kits = []; + for (const dir of readdirSync('packages')) { + let sources; + try { + sources = readdirSync(`packages/${dir}/src`, { recursive: true }); + } catch { + continue; // not a source package + } + const authorsPersonas = sources.some((file) => { + if (!file.endsWith('.ts') || file.endsWith('.test.ts')) return false; + const source = readFileSync(`packages/${dir}/src/${file}`, 'utf8'); + // `function`, `const`, `async function`, and nested files all count — a + // kit that hides from this check is a kit that breaks on a fresh install. + return /export (?:async )?(?:function|const) define\w*Persona\b/.test(source); + }); + if (authorsPersonas) kits.push(packageJson(dir).name); + } + return kits.sort(); +} + +test('every persona-authoring kit ships in the CLI install tree', () => { + const kits = personaAuthoringKits(); + // Guard against the discovery silently finding nothing and passing vacuously. + assert.ok( + kits.includes('@agentworkforce/persona-kit'), + `discovery failed to find the authoring kits (found: ${kits.join(', ') || 'none'})` + ); + + const cli = packageJson('cli'); + for (const kit of kits) { + assert.ok( + cli.dependencies?.[kit], + `${kit} exports a define*Persona entry point, so a persona.ts can import it, ` + + 'but @agentworkforce/cli does not depend on it — it will not be installed ' + + 'alongside the CLI and the persona will fail to compile on a fresh install' + ); + } +}); + +test('authoring kits are published in lockstep with the CLI', () => { + const publishWorkflow = readFileSync('.github/workflows/publish.yml', 'utf8'); + const targets = publishWorkflow.match(/echo "packages=([^"]+)"/); + // Without this the reformatted-workflow case throws an unrelated TypeError + // and reads as a broken test rather than a broken workflow. + assert.ok(targets, 'publish workflow must declare its package targets'); + const published = new Set( + targets[1].trim().split(/\s+/).map((dir) => packageJson(dir).name) + ); + + for (const kit of personaAuthoringKits()) { + assert.ok(published.has(kit), `${kit} must publish with the CLI to stay version-matched`); + } +});