import type { Router } from "./router.js"; import type { ProviderName } from "../eval/llm.js"; interface DetectedLayout { layout: 1 | 2 | 3 | 4; label: string; pathTemplate: string; existingPlugins: string[]; } interface ProjectLayoutResponse { root: string; detectedLayouts: DetectedLayout[]; suggestedLayout: 1 | 2 | 3; existingSkills: Array<{ plugin: string; skill: string; }>; } /** * Authoring engine for the skill (0734). Three first-class peer choices: * - "vskill" (default): VSkill skill-builder, cross-universal across 8 agents. * - "anthropic-skill-creator": Anthropic's built-in plugin, richer Claude-native * schema, Claude-only. Requires the `skill-creator` plugin installed locally. * - "none": skip engine assistance, emit the request body verbatim. The * `metadata.engine` frontmatter key is omitted entirely. * * Anthropic and VSkill are peer engines, NOT a fallback chain — the trade-off * is "richer Claude-native expressiveness" vs. "portable cross-tool output". */ export type CreateSkillEngine = "vskill" | "anthropic-skill-creator" | "none"; export interface PluginSuggestion { plugin: string; layout: 1 | 2; confidence: "high" | "medium" | "low"; reason: string; } /** * 0772 US-005 — Probe whether the project has a `.git` directory and a * GitHub `origin` remote. Used by `GET /api/project/github-status` to drive * the publish-readiness hint card on the Skill Overview tab. * * Returns a deterministic three-state result: * - `no-git` → no `.git` found by walking up from `root` (12 levels max) * - `non-github` → `.git` exists but `origin` is missing or not on github.com * - `github` → `.git` exists and `origin` resolves to a github.com URL * * Uses `spawnSync` with a 250ms timeout — same pattern as the onboarding * detection. Any failure short-circuits to `no-git` (safe default; the UI * shows the bootstrap hint instead of crashing). */ export interface ProjectGitHubStatus { hasGit: boolean; githubOrigin: string | null; status: "no-git" | "non-github" | "github"; } export declare function detectProjectGitHubStatus(root: string): ProjectGitHubStatus; /** Detect project layout — mirrors scanSkills() logic from skill-scanner.ts */ export declare function detectProjectLayout(root: string): ProjectLayoutResponse; export interface BuildSkillMdInput { name: string; plugin: string; layout: 1 | 2 | 3; description: string; /** Frontmatter `version:`. Defaults to "1.0.0" when omitted or empty. */ version?: string; /** 0734: authoring engine emitted under metadata.engine. */ engine?: CreateSkillEngine; model?: string; allowedTools?: string; body: string; tags?: string[]; targetAgents?: string[]; } /** * Public alias of the private `buildSkillMd` emitter for tests and lint scripts. * The underlying function is the source of truth; this export exists so * golden-file tests and the CI validator can exercise the emitter without * touching the HTTP route. * * @internal — tests must import from `__tests__/helpers/skill-md-test-helpers.ts`, * not from this module. Production code MUST NOT call this. */ export declare function buildSkillMdForTest(data: BuildSkillMdInput): string; /** * Minimal YAML frontmatter parser for test assertions — supports the shape * emitted by `buildSkillMd`: * - top-level scalars (quoted or unquoted) * - `metadata:` block with `tags:` and `target-agents:` list children * * This is intentionally narrow. For general YAML, callers should use a real * parser. Exported solely to avoid adding a YAML dep to the test target. * * @internal — production reads MUST go through `parseSkillFrontmatter` * in api-routes.ts. Tests must import this via * `__tests__/helpers/skill-md-test-helpers.ts`. */ export declare function parseFrontmatterForTest(content: string): Record & { metadata: Record; }; export interface SpawnResultLike { status: number | null; stdout: string; stderr: string; error: (NodeJS.ErrnoException | Error) | undefined; } export interface ValidatorOptions { strict: boolean; } export interface ValidatorOutcome { ok: boolean; exitCode: 0 | 1; kind: "success" | "warning" | "error" | "missing-binary"; messages: string[]; skillPath: string; } /** * Interpret a `spawnSync("skills-ref", ["validate", path])` result into a * structured outcome. Pure function — no side effects, no I/O. */ export declare function interpretValidatorResult(skillPath: string, res: SpawnResultLike, opts: ValidatorOptions): ValidatorOutcome; /** * Render a `ValidatorOutcome` to a human-readable string for CLI / server * log surfaces. Empty string on success (silent). Pure function. * * Color handling is deliberately omitted here — callers that want ANSI * colors wrap the returned string with their own formatter. That keeps the * helper trivially testable. */ export declare function formatValidatorReport(outcome: ValidatorOutcome): string; /** Compute target directory for a new skill based on layout */ export declare function resolveSafe(base: string, relPath: string): string; /** * Build a `.env.example` body from a list of secret env-var names. Each line * is `NAME=` with no value — the file ships placeholders only, never real * secrets. Caller is responsible for including a leading comment if desired. */ export declare function buildEnvExample(secrets: string[]): string; /** * Check if a resolved path is strictly within a root directory. * Uses trailing separator to prevent prefix collision (e.g., /project vs /project-evil). */ export declare function isDraftWithinRoot(draftPath: string, root: string): boolean; /** * Match a newly generated skill against existing plugins by tag/keyword overlap. * Returns the best-matching plugin if score exceeds threshold, or null. */ export declare function matchExistingPlugin(skillName: string, skillDescription: string, skillTags: string[], root: string): PluginSuggestion | null; export declare const BODY_SYSTEM_PROMPT = "You are an expert AI skill engineer in Skill Studio. Given a user's description of what a skill should do, generate the SKILL.md body and metadata (NOT evals).\n\n## Skill Studio Best Practices\n\n### SKILL.md Anatomy\nEvery skill has YAML frontmatter (description required, name/model/allowed-tools optional) and a markdown body with instructions.\n\n### Description Quality (Frontmatter) \u2014 CRITICAL\nThe description is the PRIMARY triggering mechanism. It must be \"pushy\" to combat undertriggering:\n- Use third-person format: \"This skill should be used when the user asks to...\"\n- Include specific trigger phrases users would say (e.g., \"create X\", \"configure Y\", \"fix Z\")\n- Include explicit activation phrases: \"Make sure to use this skill whenever the user mentions...\"\n- Be concrete and specific, not vague or generic\n- BAD: \"Provides guidance for working with X\" (vague, no triggers)\n- GOOD: \"This skill should be used when the user asks to \\\"create X\\\", \\\"configure Y\\\", or \\\"troubleshoot Z\\\". Activate whenever the user mentions X-related tasks.\"\n\n### Writing Style\n- Use imperative/infinitive form (verb-first instructions), NOT second person\n- Use objective, instructional language: \"To accomplish X, do Y\" not \"You should do X\"\n- BAD: \"You should start by reading the file\" / \"You need to validate\"\n- GOOD: \"Start by reading the file\" / \"Validate the input before processing\"\n- Explain the WHY behind rules \u2014 LLMs respond better to reasoning than rigid MUST/NEVER\n\n### Progressive Disclosure\n- Keep SKILL.md lean: 500-2000 words ideal for body, under 500 lines total\n- Core concepts and essential procedures in the body\n- Structure with ## sections: Workflow, Rules, Output Format, Examples\n\n### Content Quality\n- Focus on procedural knowledge non-obvious to an AI assistant\n- Include information that helps another AI instance execute tasks effectively\n- Include concrete examples where helpful\n- Include a clear Workflow section with numbered steps\n\n### Common Mistakes to Avoid\n- Weak trigger descriptions (vague, no specific phrases)\n- Too much content without structure\n- Second-person writing style\n- Missing workflow section\n- Overly generic instructions that don't add value\n\n### Action-First Skills (Bash/tool-execution)\nWhen a skill includes allowed-tools: Bash (or Read, Write, Edit), the skill body MUST:\n- Open with an explicit execution directive: \"Execute each step immediately. Run Bash commands directly \u2014 do not ask for permission or describe plans.\"\n- Use \"Step N \u2014 [action verb]. Run this immediately:\" headers, not \"Step N: [noun] Discovery\"\n- Every step must end with a concrete, complete, copy-pasteable code block \u2014 not prose describing what the code would do\n- Include any variable values (paths, URLs, flags) directly in the code block, not as placeholders\n\n## Output Format\nReturn a JSON object with these fields:\n{\n \"name\": \"kebab-case-name\",\n \"description\": \"Third-person trigger description with specific activation phrases\",\n \"model\": \"\",\n \"allowedTools\": \"\",\n \"body\": \"# /skill-name\\n\\nFull system prompt with ## sections\"\n}\n\nField rules:\n- name: kebab-case, concise, descriptive (e.g., \"sql-formatter\", \"api-docs-generator\")\n- description: 1-3 sentences with trigger phrases. Must pass the \"would Claude trigger on this?\" test\n- model: \"\" (any) unless task clearly requires opus-level reasoning or is trivially haiku-suitable\n- allowedTools: comma-separated list (e.g., \"Read, Write, Edit, Bash\") or \"\" for unrestricted. Only restrict when the skill genuinely shouldn't use certain tools\n- body: Complete markdown starting with # /skill-name, structured with ## sections, 500-2000 words\n\nReturn ONLY the JSON object \u2014 no code fences, no preamble.\n\nAfter the JSON, on a new line, write \"---REASONING---\" followed by a brief explanation of your design choices (why this name, why these trigger phrases, what Skill Studio rules you applied)."; export declare const EVAL_SYSTEM_PROMPT = "You are an expert AI skill evaluator. Given a skill's name, description, and purpose, generate eval test cases that verify the skill works correctly.\n\n### Eval Assertions for Action-Oriented Skills (CRITICAL)\nThe eval evaluates the LLM text response \u2014 it cannot run Bash or call tools. Assertions must check for code/commands present IN the response, not whether they were executed.\n- BAD: \"Runs a bash command to discover profiles\" (implies execution \u2014 will always fail)\n- GOOD: \"Response includes a bash code block that lists Chrome profile directories\"\n- BAD: \"Opens https://studio.youtube.com as the target URL\"\n- GOOD: \"Response includes studio.youtube.com as the target URL in a code block or command\"\n- BAD: \"Checks that the file exists before uploading\"\n- GOOD: \"Response includes a bash command checking whether the file exists (e.g., using test -f or ls)\"\n\n### Assertion Quality: Functional Over Formatting (CRITICAL)\nAssert on FUNCTIONAL correctness, not formatting or presentation details. Each assertion should test exactly ONE observable behavior (unit-test style).\n- NEVER assert on: blank lines, paragraph count, whitespace, exact heading levels, bullet formatting, sentence count, or line breaks\n- GOOD: \"The response includes a greeting that contains the name 'Anton'\" (checks functional behavior)\n- BAD: \"The greeting is a single short sentence (not multiple paragraphs)\" (tests formatting, not function)\n- GOOD: \"The response lists at least 3 benefits of TypeScript\" (checks content)\n- BAD: \"The response uses exactly 3 bullet points\" (tests formatting)\nFormatting is stylistic \u2014 it varies between LLM runs and does not indicate skill quality.\n\n## Output Format\nReturn a JSON object with these fields:\n{\n \"evals\": [\n {\n \"id\": 1,\n \"name\": \"test case name\",\n \"prompt\": \"realistic user prompt\",\n \"expected_output\": \"description of correct behavior\",\n \"assertions\": [\n { \"id\": \"a1\", \"text\": \"objectively verifiable assertion\", \"type\": \"boolean\" }\n ]\n }\n ]\n}\n\nField rules:\n- evals: 2-3 realistic test cases with objectively verifiable assertions\n- Prompts should be what real users would say, not abstract test inputs\n- Each assertion must be independently verifiable by a judge LLM reading the response text\n- Each assertion should check exactly ONE functional behavior (unit-test granularity)\n\nReturn ONLY the JSON object \u2014 no code fences, no preamble."; /** * Build an agent-aware system prompt by conditionally appending a * "## Target Agent Constraints" section when non-Claude agents are targeted. * * When targetAgents is absent, empty, or only contains "claude-code", * the base prompt is returned unchanged (backward compatible). */ export declare function buildAgentAwareSystemPrompt(basePrompt: string, targetAgents: string[] | undefined): string; export interface GenerateSkillRequest { prompt: string; provider?: ProviderName; model?: string; targetAgents?: string[]; } export interface GenerateSkillResult { name: string; description: string; model: string; allowedTools: string; body: string; evals: Array<{ id: number; name: string; prompt: string; expected_output: string; assertions: Array<{ id: string; text: string; type: string; }>; }>; reasoning: string; warning?: string; } export interface BodyResult { name: string; description: string; model: string; allowedTools: string; body: string; reasoning: string; } type EvalItem = GenerateSkillResult["evals"][number]; export interface EvalsResult { evals: EvalItem[]; } export declare function parseBodyResponse(raw: string): BodyResult; export declare function parseEvalsResponse(raw: string): EvalsResult; export declare function mergeGenerateResults(bodySettled: PromiseSettledResult, evalsSettled: PromiseSettledResult): GenerateSkillResult; export declare function registerSkillCreateRoutes(router: Router, rootArg: string | (() => string)): void; export {};