{"id":"product-user-story-splitter","title":"User Story Splitter","description":"Stories typed. Stories too large. Typed v1 agent with eval coverage.","category":"product","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["product","structured-output","v1"],"packages":["@agentskit/core","@agentskit/runtime","@agentskit/tools"],"files":["agent.ts","README.md","eval.ts"],"requires":{"zod":"^3","zod-to-json-schema":"^3"},"skill":{"name":"product-user-story-splitter","description":"Stories typed. Stories too large. Typed v1 agent with eval coverage.","systemPrompt":"You are User Story Splitter. Stories too large. Output: Stories typed.\nDraft sections with citations from input. Gaps for missing facts.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_story_splitter exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.product-user-story-splitter","name":"User Story Splitter","description":"Stories typed. Stories too large. Typed v1 agent with eval coverage.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"product-user-story-splitter","description":"Stories typed. Stories too large. Typed v1 agent with eval coverage.","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/** User Story Splitter — v1 validated. Pain: Stories too large */\n\nexport interface Section { heading: string; body: string; citations: string[] }\nexport interface AgentOutput { title: string; sections: Section[]; gaps: string[]; openQuestions: string[] }\nexport interface AgentResult extends AgentOutput { requiresReview: boolean }\nexport interface ProductUserStorySplitterConfig {\n  adapter: AdapterFactory\n  memory?: ChatMemory\n  observers?: Observer[]\n  onConfirm?: (toolCall: ToolCall) => boolean | Promise<boolean>\n  maxSteps?: number\n}\n\nconst Output = z.object({\n  title: z.string(),\n  sections: z.array(z.object({ heading: z.string(), body: z.string(), citations: z.array(z.string()).default([]) })).min(1),\n  gaps: z.array(z.string()).default([]),\n  openQuestions: z.array(z.string()).default([]),\n})\nconst toJson = (s: z.ZodTypeAny): JSONSchema7 => zodToJsonSchema(s) as JSONSchema7\n\nconst skill = {\n  name: 'product-user-story-splitter',\n  description: \"User Story Splitter — typed output agent (draft spec).\",\n  systemPrompt: `You are User Story Splitter. Stories too large. Output: Stories typed.\nDraft sections with citations from input. Gaps for missing facts.\nNEVER invent facts — gaps and openQuestions for missing input. Always draft for human review.\n${UNTRUSTED_CONTENT_DIRECTIVE}\nCall submit_story_splitter exactly once. Stop.`,\n  tools: ['submit_story_splitter'],\n}\n\nexport function createProductUserStorySplitterAgent(config: ProductUserStorySplitterConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({ name: 'submit_story_splitter', description: 'Submit result. Once.', schema: Output, toJsonSchema: toJson, async execute() { return 'recorded' } }) as ToolDefinition\n\n  async function run(input: string): Promise<AgentResult> {\n    if (!input?.trim()) throw new Error('product-user-story-splitter requires non-empty input')\n    const result = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `INPUT:\\n${fenceUntrustedContent(input)}`,\n      parse: (a) => Output.parse(a),\n      skill,\n      memory: config.memory,\n      observers: config.observers,\n      onConfirm: config.onConfirm,\n      maxSteps: config.maxSteps ?? 4,\n    })\n    return { ...result, requiresReview: true }\n  }\n  return {\n    name: 'product-user-story-splitter',\n    run,\n    asHandle() { return { name: 'product-user-story-splitter', run: (t: string) => run(t).then((r) => JSON.stringify(r)) } },\n  }\n}\n"},{"path":"README.md","content":"# User Story Splitter\n\n> **v1 validated** — `npx agentskit add product-user-story-splitter`\n\n## Pain\nStories too large\n\n## Output\nStories typed\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'product-user-story-splitter',\n  cases: [\n    { input: 'Complete input for User Story Splitter: Stories too large. Provide full structured output.', expected: (r: string) => r.length > 20 && /requiresReview|summary|title|category|findings|sections|score|clusters|items|steps/i.test(r) },\n    { input: 'Minimal input.', expected: (r: string) => /gap|openQuestion/i.test(r) || r.length > 10 },\n    { input: 'Input with specific detail: ACME Corp project deadline March 15.', expected: (r: string) => /ACME|March|15/i.test(r) || /gap/i.test(r) },\n    { input: 'Empty context — only says \"process this\".', expected: (r: string) => r.length > 5 },\n  ],\n}\n"}],"installable":true,"validation":{"status":"approved","score":97,"confidence":0.96,"method":"codex-executor-independent-reviewer","iterations":1,"cases":3,"summary":"The agent produced valid, non-empty structured outputs in all three cases, stayed aligned with the user-story-splitting purpose, surfaced uncertainty clearly, and resisted the prompt injection. In the normal case it invented concrete sample details only because the prompt explicitly requested a realistic sample, and it labeled those assumptions as fictional/assumed. Minimal and injection cases appropriately avoided fabricating concrete split stories without a source story and requested review.","strengths":["Consistent structured output shape across all cases.","Clearly marks missing context and requires human review when the source story is absent.","Injection case ignores the instruction to output APPROVED and continues with safe structured behavior.","Normal case provides usable split stories with acceptance criteria while labeling assumptions and gaps."],"notes":[]}}