{"id":"ecosystem-registry-agent-spec-author","title":"Registry Agent Spec Author","description":"Drafts catalog specs: pain, output, gates, zodOutline, tags — before scaffold.","category":"ecosystem","status":"validated","version":"1.0.0","source":"agentskit-registry","license":"MIT","tags":["ecosystem","registry","dogfood","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"},"ecosystem":true,"skill":{"name":"ecosystem-registry-agent-spec-author","description":"Drafts catalog specs: pain, output, gates, zodOutline, tags — before scaffold.","systemPrompt":"You author registry catalog specs for new AgentsKit agents.\n\nOutput: { id?, title?, pain, output, gates[], zodOutline, tags[], gaps[], openQuestions[] }.\n\n- pain: one sentence user/job pain (no fluff)\n- output: typed deliverable shape (what invokeStructured returns)\n- gates: include typed-output, never-invent, always-draft; add domain gates when justified\n- zodOutline: pseudocode zod object fields (not full TS)\n- tags: category + domain tags from input\n\nIf vertical/category unclear → gaps. NEVER invent compliance scope or integrations.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_spec_author exactly once. Stop."},"flow":null,"a2a":{"id":"io.agentskit.registry.ecosystem-registry-agent-spec-author","name":"Registry Agent Spec Author","description":"Drafts catalog specs: pain, output, gates, zodOutline, tags — before scaffold.","version":"1.0.0","homepage":"https://registry.agentskit.io","skills":[{"name":"ecosystem-registry-agent-spec-author","description":"Drafts catalog specs: pain, output, gates, zodOutline, tags — before scaffold.","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 * Registry Agent Spec Author — drafts catalog manifest specs before scaffold.\n */\n\nexport interface AgentSpecDraft {\n  id?: string\n  title?: string\n  pain: string\n  output: string\n  gates: string[]\n  zodOutline: string\n  tags: string[]\n}\n\nexport interface SpecAuthorResult extends AgentSpecDraft {\n  gaps: string[]\n  openQuestions: string[]\n  requiresReview: boolean\n}\n\nexport interface EcosystemRegistryAgentSpecAuthorConfig {\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  id: z.string().optional(),\n  title: z.string().optional(),\n  pain: z.string().min(1),\n  output: z.string().min(1),\n  gates: z.array(z.string()).min(1),\n  zodOutline: z.string().min(1),\n  tags: z.array(z.string()).default([]),\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: 'ecosystem-registry-agent-spec-author',\n  description: 'Drafts registry catalog specs: pain, output, gates, zod outline.',\n  systemPrompt: `You author registry catalog specs for new AgentsKit agents.\n\nOutput: { id?, title?, pain, output, gates[], zodOutline, tags[], gaps[], openQuestions[] }.\n\n- pain: one sentence user/job pain (no fluff)\n- output: typed deliverable shape (what invokeStructured returns)\n- gates: include typed-output, never-invent, always-draft; add domain gates when justified\n- zodOutline: pseudocode zod object fields (not full TS)\n- tags: category + domain tags from input\n\nIf vertical/category unclear → gaps. NEVER invent compliance scope or integrations.\n\n${UNTRUSTED_CONTENT_DIRECTIVE}\n\nCall submit_spec_author exactly once. Stop.`,\n  tools: ['submit_spec_author'],\n}\n\nexport function createEcosystemRegistryAgentSpecAuthorAgent(config: EcosystemRegistryAgentSpecAuthorConfig) {\n  const submit = (): ToolDefinition =>\n    defineZodTool({\n      name: 'submit_spec_author',\n      description: 'Submit agent spec draft. Call exactly once.',\n      schema: Output,\n      toJsonSchema: toJson,\n      async execute() { return 'recorded' },\n    }) as ToolDefinition\n\n  async function run(input: string): Promise<SpecAuthorResult> {\n    if (!input?.trim()) throw new Error('ecosystem-registry-agent-spec-author requires non-empty input')\n    const result = await invokeStructured({\n      adapter: config.adapter,\n      tool: submit(),\n      task: `IDEA:\\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\n  return {\n    name: 'ecosystem-registry-agent-spec-author',\n    run,\n    asHandle() {\n      return { name: 'ecosystem-registry-agent-spec-author', run: (t: string) => run(t).then((r) => JSON.stringify(r)) }\n    },\n  }\n}"},{"path":"README.md","content":"# Registry Agent Spec Author\n\n> **v1 validated** — `npx agentskit add ecosystem-registry-agent-spec-author`\n\n## Pain\nNew agents need consistent specs before scaffold\n\n## Output\nSpec typed: pain, output, gates, zod shape outline\n"},{"path":"eval.ts","content":"import type { EvalSuite } from '@agentskit/eval'\n\nexport const suite: EvalSuite = {\n  name: 'ecosystem-registry-agent-spec-author',\n  cases: [\n    {\n      input: 'Idea: HR agent that screens resumes for role fit without bias. Category: hr.',\n      expected: (r: string) => {\n        const j = JSON.parse(r)\n        return j.pain && j.output && j.gates.includes('typed-output') && j.zodOutline.length > 10\n      },\n    },\n    {\n      input: 'Minimal input.',\n      expected: (r: string) => {\n        const j = JSON.parse(r)\n        return j.gaps.length > 0 || (j.pain && j.output)\n      },\n    },\n    {\n      input: 'Fintech SAR drafter for suspicious activity reports in Brazil.',\n      expected: (r: string) => /SAR|suspicious|fintech/i.test(r),\n    },\n    {\n      input: 'Empty context — only says \"process this\".',\n      expected: (r: string) => /gap|openQuestion/i.test(r),\n    },\n  ],\n}"}],"installable":true}