{"id":"coding-prd-author","title":"PRD Author","description":"Transforms a free-form product description into a TYPED PRD (problem, users, 3-5 testable acceptance criteria, out-of-scope, open questions). Never invents business logic — anything absent from the input becomes an open question. Always a draft.","category":"coding","version":"2.0.0","source":"AKOS","license":"MIT","tags":["coding","structured-output","planning"],"packages":["@agentskit/core","@agentskit/runtime","@agentskit/tools"],"files":["agent.ts","README.md","eval.ts"],"requires":{"zod":"^3","zod-to-json-schema":"^3"},"status":"validated","skill":{"name":"coding-prd-author","description":"Transforms a free-form product description into a TYPED PRD (problem, users, 3-5 testable acceptance criteria, out-of-scope, open questions). Never invents business logic — anything absent from the input becomes an open question. Always a draft.","systemPrompt":"You are a senior PM in a TypeScript monorepo. Transform a free-form product description\ninto a PRD engineers can act on without ambiguity. Fields: problem statement; target users; acceptance\ncriteria (3–5 TESTABLE items); out-of-scope items; open questions.\n\nNEVER invent business logic absent from the input. Anything the input doesn't specify becomes an open\nquestion, not a guess.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_prd exactly once with { problem, users, criteria, outOfScope, openQuestions }. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.coding-prd-author","name":"PRD Author","description":"Transforms a free-form product description into a TYPED PRD (problem, users, 3-5 testable acceptance criteria, out-of-scope, open questions). Never invents business logic — anything absent from the input becomes an open question. Always a draft.","version":"2.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"coding-prd-author","description":"Transforms a free-form product description into a TYPED PRD (problem, users, 3-5 testable acceptance criteria, out-of-scope, open questions). Never invents business logic — anything absent from the input becomes an open question. Always a draft.","capabilities":{"streaming":true,"cancellation":true,"requiresApproval":false}}]},"sources":[{"path":"agent.ts","content":"import type { AdapterFactory, ChatMemory, Observer, ToolCall, ToolDefinition } from '@agentskit/core'\nimport { fenceUntrustedContent, UNTRUSTED_CONTENT_DIRECTIVE } from '@agentskit/core/security'\nimport { invokeStructured } from '@agentskit/runtime'\nimport { defineZodTool } from '@agentskit/tools'\nimport { z } from 'zod'\nimport { zodToJsonSchema } from 'zod-to-json-schema'\nimport type { JSONSchema7 } from 'json-schema'\n\n/**\n * PRD Author — transforms a free-form product description into a TYPED PRD engineers can\n * act on: problem, users, 3–5 testable acceptance criteria, out-of-scope, open questions.\n * Never invents business logic — anything absent from the input becomes an open question.\n *\n * ```ts\n * const { prd } = await createPrdAuthorAgent({ adapter }).run(productDescription)\n * ```\n */\n\nexport interface PRD {\n  problem: string\n  users: string[]\n  /** 3–5 testable acceptance criteria. */\n  criteria: string[]\n  outOfScope: string[]\n  openQuestions: string[]\n}\n\nexport interface PrdResult {\n  prd: PRD\n  requiresReview: boolean\n}\n\nexport interface PrdAuthorConfig {\n  adapter: AdapterFactory\n  memory?: ChatMemory\n  observers?: Observer[]\n  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>\n  maxSteps?: number\n}\n\nconst Prd = z.object({\n  problem: z.string(),\n  users: z.array(z.string()),\n  criteria: z.array(z.string()).min(1).max(8),\n  outOfScope: z.array(z.string()),\n  openQuestions: z.array(z.string()),\n})\nconst toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7\n\nconst skill = {\n  name: 'prd-author',\n  description: 'Transforms a product description into a typed, testable PRD (never invents logic).',\n  systemPrompt: `You are a senior PM in a TypeScript monorepo. Transform a free-form product description\ninto a PRD engineers can act on without ambiguity. Fields: problem statement; target users; acceptance\ncriteria (3–5 TESTABLE items); out-of-scope items; open questions.\n\nNEVER invent business logic absent from the input. Anything the input doesn't specify becomes an open\nquestion, not a guess.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_prd exactly once with { problem, users, criteria, outOfScope, openQuestions }. Stop.`,\n  tools: ['submit_prd'],\n}\n\nexport function createPrdAuthorAgent(config: PrdAuthorConfig) {\n  const emit = (label: string, status: 'start' | 'ok' | 'skip' | 'error', detail?: string) => {\n    for (const o of config.observers ?? []) void o.on({ type: 'progress', label, status, detail })\n  }\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_prd', description: 'Submit the PRD. Call exactly once.', schema: Prd, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition\n\n  async function run(description: string): Promise<PrdResult> {\n    if (!description?.trim()) throw new Error('prd author requires a non-empty product description')\n    emit('prd', 'start')\n    const prd = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `PRODUCT DESCRIPTION:\\n${fenceUntrustedContent(description)}`,\n      parse: (a) => Prd.parse(a),\n      skill,\n      memory: config.memory,\n      observers: config.observers,\n      onConfirm: config.onConfirm,\n      maxSteps: config.maxSteps ?? 3,\n    })\n    emit('prd', 'ok', `${prd.criteria.length} criteria, ${prd.openQuestions.length} open`)\n    return { prd, requiresReview: true }\n  }\n\n  return {\n    name: 'coding-prd-author',\n    run,\n    asHandle() {\n      return { name: 'coding-prd-author', run: async (task: string) => JSON.stringify(await run(task)) }\n    },\n  }\n}\n"},{"path":"README.md","content":"# PRD Author\n\nTransforms a free-form product description into a **typed, testable PRD** — never invents business logic.\n\n```bash\nnpx agentskit add coding-prd-author\n```\n\n```ts\nimport { anthropic } from '@agentskit/adapters'\nimport { createPrdAuthorAgent } from './agents/coding-prd-author/agent'\n\nconst r = await createPrdAuthorAgent({\n  adapter: anthropic({ apiKey: process.env.ANTHROPIC_API_KEY!, model: 'claude-opus-4-8' }),\n}).run(productDescription)\n// → { prd: { problem, users[], criteria[], outOfScope[], openQuestions[] }, requiresReview }\n```\n\n- **Typed PRD** — `invokeStructured` + zod; 3–5 testable acceptance criteria.\n- **Never invents** — anything the input doesn't specify becomes an `openQuestion`, not a guess.\n- **Always a draft** — `requiresReview` always true. Untrusted input is **fenced**.\n\n`run(description)` → `PrdResult`. `asHandle()` is JSON-out. See [composing agents](../../COMPOSING.md). Feeds [`coding-qa-author`](../coding-qa-author) and [`coding-issue-creator`](../coding-issue-creator).\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'coding-prd-author',\n  cases: [\n    {\n      input: `We want to let users export their dashboard data as CSV. They should be able to pick a date range and download a file. It needs to handle large datasets without timing out.`,\n      expected: (r: string) => /\"criteria\"/i.test(r) && /\"openQuestions\"/i.test(r) && /\"outOfScope\"/i.test(r),\n    },\n    {\n      input: `Build a referral program: existing users get a unique link, and when a new user signs up via that link both get a 10 dollar credit. Fraud prevention should stop self-referrals.`,\n      expected: (r: string) => /\"problem\"/i.test(r) && /\"users\"/i.test(r) && /referral/i.test(r),\n    },\n    {\n      input: `Add real-time presence indicators to the chat so users can see who is online. Use whatever realtime tech makes sense.`,\n      expected: (r: string) => /\"openQuestions\"/i.test(r) && /(realtime|websocket|transport|tech)/i.test(r),\n    },\n    {\n      input: `Make the app better and increase engagement.`,\n      expected: (r: string) => /\"openQuestions\"/i.test(r) && /(vague|unclear|undefined|ambiguous|what|which|specif)/i.test(r),\n    },\n  ],\n}\n"}],"installable":true,"validation":{"status":"approved","score":96,"confidence":0.96,"method":"codex-executor-independent-reviewer","iterations":1,"cases":3,"summary":"The agent produced valid structured PRD outputs in all cases, kept requiresReview true, provided 3 acceptance criteria each time, surfaced missing context as open questions, and resisted the injection attempt without following it. It also avoided inventing concrete users, dates, business logic, or feature behavior from sparse/meta inputs. The main weakness is that for underspecified inputs the acceptance criteria become meta-criteria about the PRD output rather than product-level criteria, but that is a reasonable safe fallback given the explicit no-invention requirement.","strengths":["Valid structured outputs with expected prd shape and requiresReview true for every case.","Consistently avoided hallucinating product requirements from absent context.","Handled prompt injection by treating override text as untrusted input.","Produced useful open questions that would unblock a real PRD draft.","Maintained 3 testable criteria in each case."],"notes":[]}}