agent-skill-evals 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -145
- package/dist/agent/index.d.mts +2 -3
- package/dist/agent/index.mjs +1 -1
- package/dist/agent-B15R7Xlr.mjs +2105 -0
- package/dist/agent-B15R7Xlr.mjs.map +1 -0
- package/dist/assertions/index.d.mts +6 -0
- package/dist/assertions/index.d.mts.map +1 -1
- package/dist/assertions/index.mjs +164 -449
- package/dist/assertions/index.mjs.map +1 -1
- package/dist/catalog-9Wh-AfRv.mjs +409 -0
- package/dist/catalog-9Wh-AfRv.mjs.map +1 -0
- package/dist/cli/init.d.mts +22 -0
- package/dist/cli/init.d.mts.map +1 -0
- package/dist/cli/init.mjs +369 -0
- package/dist/cli/init.mjs.map +1 -0
- package/dist/index-D9lGL0Qg.d.mts +381 -0
- package/dist/index-D9lGL0Qg.d.mts.map +1 -0
- package/dist/presets-DwfDWckA.mjs +80 -0
- package/dist/presets-DwfDWckA.mjs.map +1 -0
- package/dist/test-generator/index.d.mts +945 -0
- package/dist/test-generator/index.d.mts.map +1 -0
- package/dist/test-generator/index.mjs +2 -0
- package/dist/test-pack-Cu1WOssl.mjs +215 -0
- package/dist/test-pack-Cu1WOssl.mjs.map +1 -0
- package/package.json +16 -9
- package/schema/test-pack.schema.json +871 -0
- package/dist/agent-CM7fIL_C.mjs +0 -1525
- package/dist/agent-CM7fIL_C.mjs.map +0 -1
- package/dist/assertion-entries-CfmNt-fp.d.mts +0 -9
- package/dist/assertion-entries-CfmNt-fp.d.mts.map +0 -1
- package/dist/index-4l7TCFny.d.mts +0 -90
- package/dist/index-4l7TCFny.d.mts.map +0 -1
- package/dist/internal-services-5-mRgNls.mjs +0 -226
- package/dist/internal-services-5-mRgNls.mjs.map +0 -1
- package/dist/internal-services-DbsekQ_K.d.mts +0 -76
- package/dist/internal-services-DbsekQ_K.d.mts.map +0 -1
- package/dist/skill-checks/index.d.mts +0 -113
- package/dist/skill-checks/index.d.mts.map +0 -1
- package/dist/skill-checks/index.mjs +0 -408
- package/dist/skill-checks/index.mjs.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"catalog-9Wh-AfRv.mjs","names":[],"sources":["../src/runtime-checks/schemas.ts","../src/runtime-checks/verifier-checks.ts","../src/runtime-checks/file-code-checks.ts","../src/runtime-checks/skill-loaded.ts","../src/runtime-checks/_match.ts","../src/runtime-checks/tool-turn-checks.ts","../src/runtime-checks/output-checks.ts","../src/runtime-checks/catalog.ts"],"sourcesContent":["import { z } from \"zod\";\nimport type {\n AgentSkillEvalsAssertionResult,\n AssertionMode,\n} from \"../internal-types.js\";\n\nexport function result(\n pass: boolean,\n reason: string,\n evidence?: unknown,\n): AgentSkillEvalsAssertionResult {\n return { pass, score: pass ? 1 : 0, reason, evidence };\n}\n\nexport function validationFailure(reason: string): AgentSkillEvalsAssertionResult {\n return result(false, reason);\n}\n\n/**\n * Map a \"matched\" boolean to a pass result based on mode. Used by checks\n * that don't self-encode polarity (file.exists, tool.called, ...).\n *\n * - should: pass = matched\n * - should_not: pass = !matched\n * - precondition: pass = matched (precondition asserts a current state)\n */\nexport function applyMode(\n matched: boolean,\n mode: AssertionMode,\n reasonMatched: string,\n reasonUnmatched: string,\n): AgentSkillEvalsAssertionResult {\n switch (mode) {\n case \"should\":\n case \"precondition\":\n return result(matched, matched ? reasonMatched : reasonUnmatched);\n case \"should_not\":\n return result(!matched, matched ? reasonMatched : reasonUnmatched);\n }\n}\n\nconst NonEmptyString = z.string().trim().min(1);\nconst OptionalString = z.string().optional();\nconst OptionalNonEmptyString = NonEmptyString.optional();\n\nexport const PathArgsSchema = z.object({\n path: NonEmptyString,\n});\n\nexport const FileContainsArgsSchema = z.object({\n path: NonEmptyString,\n text: z.string(),\n});\n\nexport const FileChangesWithinArgsSchema = z.object({\n paths: z.array(NonEmptyString).min(1),\n});\n\nexport const VerifierArgsSchema = z.object({\n run: NonEmptyString,\n args: z.array(z.string()).optional(),\n timeoutMs: z.number().optional(),\n});\n\nexport const ToolCalledArgsSchema = z.object({\n tool: NonEmptyString,\n provider: OptionalString,\n server: OptionalString,\n args_match: z.unknown().optional(),\n});\n\nconst JsonValueSchema = z.union([\n z.null(),\n z.boolean(),\n z.number(),\n z.string(),\n z.array(z.unknown()),\n z.record(z.string(), z.unknown()),\n]);\n\nconst ToolNotCalledSelectorSchema = z.object({\n tool: OptionalNonEmptyString,\n provider: OptionalNonEmptyString,\n server: OptionalNonEmptyString,\n args_match: JsonValueSchema.optional(),\n});\n\nexport const ToolNotCalledArgsSchema = z.intersection(\n ToolNotCalledSelectorSchema,\n z.union([\n z.object({ tool: NonEmptyString }),\n z.object({ provider: NonEmptyString }),\n z.object({ server: NonEmptyString }),\n z.object({ args_match: JsonValueSchema }),\n ]),\n);\n\nexport const SkillSelectionArgsSchema = z.object({\n skills: z.array(NonEmptyString).min(1),\n delivery: z.enum([\"native\", \"mcp\"]).optional(),\n provider: OptionalString,\n server: OptionalString,\n source: OptionalString,\n});\n\nexport const ToolCountArgsSchema = z.object({\n tool: OptionalString,\n provider: OptionalString,\n server: OptionalString,\n args_match: z.unknown().optional(),\n turn: z.number().optional(),\n before_turn: z.number().optional(),\n after_turn: z.number().optional(),\n min: z.number().optional(),\n max: z.number().optional(),\n});\n\nexport const TurnCountArgsSchema = z.object({\n min: z.number().optional(),\n max: z.number().optional(),\n});\n\nexport const ToolSequenceArgsSchema = z.object({\n order: z.array(NonEmptyString).min(2),\n});\n\nexport const OutputContainsArgsSchema = z.object({\n text: z.string(),\n});\n\nexport const OutputMatchesArgsSchema = z.object({\n pattern: NonEmptyString,\n flags: z.string().optional(),\n});\n\nexport type ToolNotCalledArgs = z.infer<typeof ToolNotCalledArgsSchema>;\n\n/**\n * Decode assertion args with a zod schema. Returns the parsed value, or a\n * failed assertion result carrying `invalidReason` when parsing fails.\n */\nexport function decodeCheckArgs<T>(\n schema: z.ZodType<T>,\n assertion: unknown,\n invalidReason: string,\n): T | AgentSkillEvalsAssertionResult {\n const decoded = schema.safeParse(assertion ?? {});\n return decoded.success ? decoded.data : validationFailure(invalidReason);\n}\n\nexport function isValidationFailure(value: unknown): value is AgentSkillEvalsAssertionResult {\n return (\n !!value &&\n typeof value === \"object\" &&\n \"pass\" in value &&\n (value as AgentSkillEvalsAssertionResult).pass === false &&\n typeof (value as AgentSkillEvalsAssertionResult).reason === \"string\"\n );\n}\n\nexport function decodeToolNotCalledArgs(\n assertion: unknown,\n): ToolNotCalledArgs | AgentSkillEvalsAssertionResult {\n return decodeCheckArgs(\n ToolNotCalledArgsSchema,\n assertion,\n \"tool.not_called: assertion must include at least one selector\",\n );\n}\n","import type { VerifierPlugin } from \"../internal-types.js\";\nimport {\n applyMode,\n decodeCheckArgs,\n isValidationFailure,\n VerifierArgsSchema,\n} from \"./schemas.js\";\n\nexport const verifierSucceeds: VerifierPlugin = {\n type: \"verifier.succeeds\",\n async verify(ctx) {\n const a = decodeCheckArgs(\n VerifierArgsSchema,\n ctx.assertion,\n \"verifier.succeeds: assertion.run must be a non-empty string\",\n );\n if (isValidationFailure(a)) return a;\n const r = await ctx.world.exec(a.run, a.args ?? [], {\n timeoutMs: a.timeoutMs ?? 60_000,\n });\n const matched = r.exitCode === 0;\n return applyMode(\n matched,\n ctx.mode,\n `verifier.succeeds: ${a.run} exited 0`,\n `verifier.succeeds: ${a.run} exited ${r.exitCode}: ${r.stderr.slice(0, 200)}`,\n );\n },\n};\n\nexport const verifierFails: VerifierPlugin = {\n type: \"verifier.fails\",\n async verify(ctx) {\n const a = decodeCheckArgs(\n VerifierArgsSchema,\n ctx.assertion,\n \"verifier.fails: assertion.run must be a non-empty string\",\n );\n if (isValidationFailure(a)) return a;\n const r = await ctx.world.exec(a.run, a.args ?? [], {\n timeoutMs: a.timeoutMs ?? 60_000,\n });\n const matched = r.exitCode !== 0;\n return applyMode(\n matched,\n ctx.mode,\n `verifier.fails: ${a.run} exited ${r.exitCode} (failed as expected)`,\n `verifier.fails: ${a.run} unexpectedly exited 0`,\n );\n },\n};\n","import type { VerifierPlugin } from \"../internal-types.js\";\nimport {\n applyMode,\n decodeCheckArgs,\n FileChangesWithinArgsSchema,\n FileContainsArgsSchema,\n isValidationFailure,\n PathArgsSchema,\n} from \"./schemas.js\";\n\nexport const fileExists: VerifierPlugin = {\n type: \"file.exists\",\n async verify(ctx) {\n const args = decodeCheckArgs(PathArgsSchema, ctx.assertion, \"file.exists: path must be a non-empty string\");\n if (isValidationFailure(args)) return args;\n const exists = (await ctx.world.readFile(args.path)) !== null;\n return applyMode(exists, ctx.mode, `file.exists: ${args.path} present`, `file.exists: ${args.path} not found`);\n },\n};\n\nexport const fileCreated: VerifierPlugin = {\n type: \"file.created\",\n verify(ctx) {\n const args = decodeCheckArgs(PathArgsSchema, ctx.assertion, \"file.created: path must be a non-empty string\");\n if (isValidationFailure(args)) return args;\n const created = ctx.evidence.filesWritten().some((event) => event.path === args.path && event.op === \"create\");\n return applyMode(created, ctx.mode, `file.created: ${args.path} created`, `file.created: ${args.path} was not created`);\n },\n};\n\nexport const fileUnchanged: VerifierPlugin = {\n type: \"file.unchanged\",\n verify(ctx) {\n const args = decodeCheckArgs(PathArgsSchema, ctx.assertion, \"file.unchanged: path must be a non-empty string\");\n if (isValidationFailure(args)) return args;\n const unchanged = !ctx.evidence.filesWritten().some((event) => event.path === args.path);\n return applyMode(unchanged, ctx.mode, `file.unchanged: ${args.path} unchanged`, `file.unchanged: ${args.path} changed`);\n },\n};\n\nexport const fileContains: VerifierPlugin = {\n type: \"file.contains\",\n async verify(ctx) {\n const args = decodeCheckArgs(FileContainsArgsSchema, ctx.assertion, \"file.contains: path and text are required\");\n if (isValidationFailure(args)) return args;\n const content = await ctx.world.readFile(args.path);\n const matched = content !== null && content.includes(args.text);\n return applyMode(\n matched,\n ctx.mode,\n `file.contains: ${args.path} contains ${JSON.stringify(args.text.slice(0, 40))}`,\n content === null ? `file.contains: ${args.path} not found` : `file.contains: ${args.path} missing ${JSON.stringify(args.text.slice(0, 40))}`,\n );\n },\n};\n\n/** A path declaration is either one exact file or a directory ending in /. */\nfunction pathAllowed(path: string, allowed: string): boolean {\n return path === allowed || (allowed.endsWith(\"/\") && path.startsWith(allowed));\n}\n\nexport const fileChangesWithin: VerifierPlugin = {\n type: \"file.changes_within\",\n verify(ctx) {\n const args = decodeCheckArgs(FileChangesWithinArgsSchema, ctx.assertion, \"file.changes_within: paths must contain at least one path\");\n if (isValidationFailure(args)) return args;\n const outside = ctx.evidence.filesWritten().filter((event) =>\n !args.paths.some((allowed) => pathAllowed(event.path, allowed))\n );\n const matched = outside.length === 0;\n return applyMode(\n matched,\n ctx.mode,\n `file.changes_within: all changes stayed within ${args.paths.join(\", \")}`,\n `file.changes_within: changed outside allowed paths: ${outside.slice(0, 3).map((event) => event.path).join(\", \")}`,\n );\n },\n};\n","import type { SkillLoadEvent, VerifierPlugin } from \"../internal-types.js\";\nimport { applyMode, decodeCheckArgs, isValidationFailure, SkillSelectionArgsSchema } from \"./schemas.js\";\n\nfunction matchesLoad(\n event: SkillLoadEvent,\n args: { delivery?: \"native\" | \"mcp\"; provider?: string; server?: string; source?: string },\n): boolean {\n return (\n (!args.delivery || event.delivery === args.delivery) &&\n (!args.provider || event.provider === args.provider) &&\n (!args.server || event.server === args.server) &&\n (!args.source || event.source === args.source)\n );\n}\n\nfunction loadedSkills(\n events: readonly SkillLoadEvent[],\n args: { delivery?: \"native\" | \"mcp\"; provider?: string; server?: string; source?: string },\n): Set<string> {\n return new Set(events.filter((event) => matchesLoad(event, args)).map((event) => event.skill));\n}\n\nexport const skillLoaded: VerifierPlugin = {\n type: \"skill.loaded\",\n verify({ assertion, evidence, mode }) {\n const args = decodeCheckArgs(SkillSelectionArgsSchema, assertion, \"skill.loaded: skills must contain at least one name\");\n if (isValidationFailure(args)) return args;\n const loaded = loadedSkills(evidence.skillsLoaded(), args);\n const missing = args.skills.filter((skill) => !loaded.has(skill));\n return applyMode(\n missing.length === 0,\n mode,\n `skill.loaded: loaded ${args.skills.join(\", \")}`,\n `skill.loaded: missing ${missing.join(\", \")}`,\n );\n },\n};\n\nexport const skillNotLoaded: VerifierPlugin = {\n type: \"skill.not_loaded\",\n verify({ assertion, evidence, mode }) {\n const args = decodeCheckArgs(SkillSelectionArgsSchema, assertion, \"skill.not_loaded: skills must contain at least one name\");\n if (isValidationFailure(args)) return args;\n const loaded = loadedSkills(evidence.skillsLoaded(), args);\n const forbidden = args.skills.filter((skill) => loaded.has(skill));\n return applyMode(\n forbidden.length === 0,\n mode,\n `skill.not_loaded: absent ${args.skills.join(\", \")}`,\n `skill.not_loaded: unexpectedly loaded ${forbidden.join(\", \")}`,\n );\n },\n};\n","export function matchesSubset(actual: unknown, expected: unknown): boolean {\n if (expected === actual) return true;\n if (Array.isArray(expected)) {\n return (\n Array.isArray(actual) &&\n expected.length === actual.length &&\n expected.every((item, i) => matchesSubset(actual[i], item))\n );\n }\n if (expected && typeof expected === \"object\") {\n if (!actual || typeof actual !== \"object\" || Array.isArray(actual)) return false;\n const actualRecord = actual as Record<string, unknown>;\n return Object.entries(expected as Record<string, unknown>).every(([key, value]) =>\n matchesSubset(actualRecord[key], value),\n );\n }\n return false;\n}\n","import type { VerifierPlugin } from \"../internal-types.js\";\nimport { matchesSubset } from \"./_match.js\";\nimport {\n applyMode,\n decodeCheckArgs,\n decodeToolNotCalledArgs,\n isValidationFailure,\n ToolCalledArgsSchema,\n ToolCountArgsSchema,\n ToolSequenceArgsSchema,\n TurnCountArgsSchema,\n validationFailure,\n} from \"./schemas.js\";\n\ninterface CallMatchArgs {\n tool?: string;\n provider?: string;\n server?: string;\n args_match?: unknown;\n}\n\ninterface RecordedCall {\n tool: string;\n provider?: string;\n server?: string;\n args?: unknown;\n}\n\nfunction matchesRecordedCall(call: RecordedCall, args: CallMatchArgs): boolean {\n return (\n (!args.tool || call.tool === args.tool) &&\n (!args.provider || call.provider === args.provider) &&\n (!args.server || call.server === args.server) &&\n (args.args_match === undefined || matchesSubset(call.args, args.args_match))\n );\n}\n\nexport const toolCalled: VerifierPlugin = {\n type: \"tool.called\",\n verify({ assertion, evidence, mode }) {\n const a = decodeCheckArgs(\n ToolCalledArgsSchema,\n assertion,\n \"tool.called: assertion.tool must be a non-empty string\",\n );\n if (isValidationFailure(a)) return a;\n const calls = evidence.toolCalls();\n if (!calls.length) {\n return { pass: false, score: 0, reason: \"tool.called: no built-in tool evidence found\" };\n }\n const found = calls.some((c) => matchesRecordedCall(c, a));\n return applyMode(\n found,\n mode,\n \"tool.called: matched built-in tool call\",\n \"tool.called: matching call not found\",\n );\n },\n};\n\nexport const toolNotCalled: VerifierPlugin = {\n type: \"tool.not_called\",\n verify({ assertion, evidence }) {\n const a = decodeToolNotCalledArgs(assertion);\n if (isValidationFailure(a)) return a;\n const calls = evidence.toolCalls();\n const found = calls.some((c) => matchesRecordedCall(c, a));\n return found\n ? { pass: false, score: 0, reason: \"tool.not_called: forbidden built-in tool call observed\" }\n : { pass: true, score: 1, reason: \"tool.not_called: no matching built-in tool calls observed\" };\n },\n};\n\n/**\n * Count matching tool calls, optionally scoped to conversation turns.\n * Single-turn runs record no turn tags; their calls count as turn 1.\n */\nexport const toolCount: VerifierPlugin = {\n type: \"tool.count\",\n verify({ assertion, evidence, mode }) {\n const a = decodeCheckArgs(\n ToolCountArgsSchema,\n assertion,\n \"tool.count: invalid args (tool/provider/server/args_match/turn/before_turn/after_turn/min/max)\",\n );\n if (isValidationFailure(a)) return a;\n if (a.min === undefined && a.max === undefined) {\n return validationFailure(\"tool.count: set min and/or max\");\n }\n const count = evidence.toolCalls().filter((call) => {\n const turn = call.turn ?? 1;\n if (a.turn !== undefined && turn !== a.turn) return false;\n if (a.before_turn !== undefined && turn >= a.before_turn) return false;\n if (a.after_turn !== undefined && turn <= a.after_turn) return false;\n return matchesRecordedCall(call, a);\n }).length;\n const within =\n (a.min === undefined || count >= a.min) &&\n (a.max === undefined || count <= a.max);\n const bounds = [\n a.min !== undefined ? `min ${a.min}` : undefined,\n a.max !== undefined ? `max ${a.max}` : undefined,\n ].filter(Boolean).join(\", \");\n return applyMode(\n within,\n mode,\n `tool.count: ${count} matching call(s) within bounds (${bounds})`,\n `tool.count: ${count} matching call(s) outside bounds (${bounds})`,\n );\n },\n};\n\n/**\n * Assert an ordered subsequence of tool calls: every listed tool name must\n * appear in recorded order, with any number of other calls in between.\n */\nexport const toolSequence: VerifierPlugin = {\n type: \"tool.sequence\",\n verify({ assertion, evidence, mode }) {\n const a = decodeCheckArgs(\n ToolSequenceArgsSchema,\n assertion,\n \"tool.sequence: assertion.order must list at least two tool names\",\n );\n if (isValidationFailure(a)) return a;\n const names = evidence.toolCalls().map((call) => call.tool);\n let next = 0;\n for (const name of names) {\n if (name === a.order[next]) next += 1;\n if (next === a.order.length) break;\n }\n const matched = next === a.order.length;\n const missing = matched ? \"\" : ` (stuck at \"${a.order[next]}\")`;\n return applyMode(\n matched,\n mode,\n `tool.sequence: observed ${a.order.join(\" -> \")} in order`,\n `tool.sequence: did not observe ${a.order.join(\" -> \")} in order${missing}`,\n );\n },\n};\n\n/**\n * Bound the number of agent turns. A single-turn run counts as one turn.\n */\nexport const turnCount: VerifierPlugin = {\n type: \"turn.count\",\n verify({ assertion, evidence, mode }) {\n const a = decodeCheckArgs(\n TurnCountArgsSchema,\n assertion,\n \"turn.count: invalid args (min/max must be numbers)\",\n );\n if (isValidationFailure(a)) return a;\n if (a.min === undefined && a.max === undefined) {\n return validationFailure(\"turn.count: set min and/or max\");\n }\n const turns = evidence.turns?.() ?? [];\n const agentTurns = turns.filter((turn) => turn.role === \"agent\").length;\n const count = agentTurns > 0 ? agentTurns : 1;\n const within =\n (a.min === undefined || count >= a.min) &&\n (a.max === undefined || count <= a.max);\n const bounds = [\n a.min !== undefined ? `min ${a.min}` : undefined,\n a.max !== undefined ? `max ${a.max}` : undefined,\n ].filter(Boolean).join(\", \");\n return applyMode(\n within,\n mode,\n `turn.count: ${count} agent turn(s) within bounds (${bounds})`,\n `turn.count: ${count} agent turn(s) outside bounds (${bounds})`,\n );\n },\n};\n","import type { VerifierPlugin } from \"../internal-types.js\";\nimport { applyMode, decodeCheckArgs, isValidationFailure, OutputContainsArgsSchema, OutputMatchesArgsSchema } from \"./schemas.js\";\n\nexport const outputContains: VerifierPlugin = {\n type: \"output.contains\",\n verify(ctx) {\n const args = decodeCheckArgs(OutputContainsArgsSchema, ctx.assertion, \"output.contains: text is required\");\n if (isValidationFailure(args)) return args;\n const matched = ctx.evidence.output().includes(args.text);\n return applyMode(matched, ctx.mode, `output.contains: found ${JSON.stringify(args.text)}`, `output.contains: missing ${JSON.stringify(args.text)}`);\n },\n};\n\nexport const outputMatches: VerifierPlugin = {\n type: \"output.matches\",\n verify(ctx) {\n const args = decodeCheckArgs(OutputMatchesArgsSchema, ctx.assertion, \"output.matches: pattern is required\");\n if (isValidationFailure(args)) return args;\n let regex: RegExp;\n try {\n regex = new RegExp(args.pattern, args.flags);\n } catch (error) {\n return { pass: false, score: 0, reason: `output.matches: invalid regex: ${error instanceof Error ? error.message : String(error)}` };\n }\n const matched = regex.test(ctx.evidence.output());\n return applyMode(matched, ctx.mode, `output.matches: /${args.pattern}/${args.flags ?? \"\"} matched`, `output.matches: /${args.pattern}/${args.flags ?? \"\"} did not match`);\n },\n};\n","import type { VerifierPlugin } from \"../internal-types.js\";\nimport {\n FileChangesWithinArgsSchema,\n FileContainsArgsSchema,\n OutputContainsArgsSchema,\n OutputMatchesArgsSchema,\n PathArgsSchema,\n SkillSelectionArgsSchema,\n ToolCalledArgsSchema,\n ToolCountArgsSchema,\n ToolNotCalledArgsSchema,\n ToolSequenceArgsSchema,\n TurnCountArgsSchema,\n VerifierArgsSchema,\n} from \"./schemas.js\";\nimport { verifierFails, verifierSucceeds } from \"./verifier-checks.js\";\nimport { fileChangesWithin, fileContains, fileCreated, fileExists, fileUnchanged } from \"./file-code-checks.js\";\nimport { skillLoaded, skillNotLoaded } from \"./skill-loaded.js\";\nimport { toolCalled, toolCount, toolNotCalled, toolSequence, turnCount } from \"./tool-turn-checks.js\";\nimport { outputContains, outputMatches } from \"./output-checks.js\";\n\nexport const RUNTIME_CHECKS = [\n { type: \"verifier.succeeds\", plugin: verifierSucceeds, argsSchema: VerifierArgsSchema },\n { type: \"verifier.fails\", plugin: verifierFails, argsSchema: VerifierArgsSchema },\n { type: \"file.exists\", plugin: fileExists, argsSchema: PathArgsSchema },\n { type: \"file.created\", plugin: fileCreated, argsSchema: PathArgsSchema },\n { type: \"file.contains\", plugin: fileContains, argsSchema: FileContainsArgsSchema },\n { type: \"file.unchanged\", plugin: fileUnchanged, argsSchema: PathArgsSchema },\n { type: \"file.changes_within\", plugin: fileChangesWithin, argsSchema: FileChangesWithinArgsSchema },\n { type: \"tool.called\", plugin: toolCalled, argsSchema: ToolCalledArgsSchema },\n { type: \"tool.not_called\", plugin: toolNotCalled, argsSchema: ToolNotCalledArgsSchema },\n { type: \"tool.count\", plugin: toolCount, argsSchema: ToolCountArgsSchema },\n { type: \"tool.sequence\", plugin: toolSequence, argsSchema: ToolSequenceArgsSchema },\n { type: \"turn.count\", plugin: turnCount, argsSchema: TurnCountArgsSchema },\n { type: \"skill.loaded\", plugin: skillLoaded, argsSchema: SkillSelectionArgsSchema },\n { type: \"skill.not_loaded\", plugin: skillNotLoaded, argsSchema: SkillSelectionArgsSchema },\n { type: \"output.contains\", plugin: outputContains, argsSchema: OutputContainsArgsSchema },\n { type: \"output.matches\", plugin: outputMatches, argsSchema: OutputMatchesArgsSchema },\n] as const;\n\nexport const RUNTIME_CHECK_TYPES = RUNTIME_CHECKS.map((check) => check.type);\nexport const RUNTIME_CHECKS_BY_TYPE: ReadonlyMap<string, VerifierPlugin> = new Map(\n RUNTIME_CHECKS.map(({ type, plugin }) => [type, plugin]),\n);\n"],"mappings":"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"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
//#region src/cli/init.d.ts
|
|
2
|
+
interface InitOptions {
|
|
3
|
+
dir?: string;
|
|
4
|
+
adapter?: string;
|
|
5
|
+
skill?: string;
|
|
6
|
+
force?: boolean;
|
|
7
|
+
}
|
|
8
|
+
interface InitResult {
|
|
9
|
+
created: string[];
|
|
10
|
+
skipped: string[];
|
|
11
|
+
errors: string[];
|
|
12
|
+
}
|
|
13
|
+
interface CliIo {
|
|
14
|
+
cwd: string;
|
|
15
|
+
stdout: (text: string) => void;
|
|
16
|
+
stderr: (text: string) => void;
|
|
17
|
+
}
|
|
18
|
+
declare function scaffold(options?: InitOptions): InitResult;
|
|
19
|
+
declare function main(argv?: string[], io?: CliIo): Promise<number>;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { CliIo, InitOptions, InitResult, main, scaffold };
|
|
22
|
+
//# sourceMappingURL=init.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.d.mts","names":[],"sources":["../../src/cli/init.ts"],"mappings":";UAOiB,WAAA;EACf,GAAA;EACA,OAAA;EACA,KAAA;EACA,KAAA;AAAA;AAAA,UAGe,UAAA;EACf,OAAA;EACA,OAAA;EACA,MAAA;AAAA;AAAA,UAGe,KAAA;EACf,GAAA;EACA,MAAA,GAAS,IAAA;EACT,MAAA,GAAS,IAAA;AAAA;AAAA,iBAiDK,QAAA,CAAS,OAAA,GAAS,WAAA,GAAmB,UAAA;AAAA,iBAsD/B,IAAA,CACpB,IAAA,aACA,EAAA,GAAI,KAAA,GAKH,OAAA"}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { r as RUNTIME_CHECK_TYPES } from "../catalog-9Wh-AfRv.mjs";
|
|
3
|
+
import { n as PRESET_IDS, t as AGENT_PRESETS } from "../presets-DwfDWckA.mjs";
|
|
4
|
+
import { a as loadTestPack, l as parseYaml, u as pathExists } from "../test-pack-Cu1WOssl.mjs";
|
|
5
|
+
import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
6
|
+
import { access, readFile, stat } from "node:fs/promises";
|
|
7
|
+
import { constants, existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
8
|
+
//#region src/skill-checks/skill.ts
|
|
9
|
+
const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|
|
10
|
+
async function parseSkillMd(skillMdPath) {
|
|
11
|
+
const skillDir = dirname(skillMdPath);
|
|
12
|
+
const raw = await readFile(skillMdPath, "utf8");
|
|
13
|
+
const totalLines = raw.split(/\r?\n/).length;
|
|
14
|
+
let frontmatter = {};
|
|
15
|
+
let body = raw;
|
|
16
|
+
const fmMatch = raw.match(FRONTMATTER_RE);
|
|
17
|
+
if (fmMatch) {
|
|
18
|
+
const parsed = parseYaml(fmMatch[1]);
|
|
19
|
+
if (parsed && typeof parsed === "object") frontmatter = parsed;
|
|
20
|
+
body = raw.slice(fmMatch[0].length);
|
|
21
|
+
}
|
|
22
|
+
const references = extractReferences(body);
|
|
23
|
+
const missingReferences = [];
|
|
24
|
+
for (const ref of references) if (!await pathExists(isAbsolute(ref) ? ref : resolve(skillDir, ref))) missingReferences.push(ref);
|
|
25
|
+
return {
|
|
26
|
+
skillMdPath,
|
|
27
|
+
skillDir,
|
|
28
|
+
frontmatter,
|
|
29
|
+
body,
|
|
30
|
+
totalLines,
|
|
31
|
+
references,
|
|
32
|
+
missingReferences
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract relative paths from a SKILL.md body. Captures:
|
|
37
|
+
* - Markdown links: [text](path)
|
|
38
|
+
* - Bare relative paths in code/inline-code: ./foo, ../foo, foo/bar.sh
|
|
39
|
+
* Skips http(s):// URLs.
|
|
40
|
+
*/
|
|
41
|
+
function extractReferences(body) {
|
|
42
|
+
const refs = /* @__PURE__ */ new Set();
|
|
43
|
+
const linkRe = /\[[^\]]*\]\(([^)\s]+)\)/g;
|
|
44
|
+
let m;
|
|
45
|
+
while ((m = linkRe.exec(body)) !== null) {
|
|
46
|
+
const target = m[1];
|
|
47
|
+
if (target.startsWith("http://") || target.startsWith("https://")) continue;
|
|
48
|
+
if (target.startsWith("#")) continue;
|
|
49
|
+
refs.add(target);
|
|
50
|
+
}
|
|
51
|
+
const codeRe = /`((?:\.{1,2}\/)?[a-zA-Z0-9_./-]+\.[a-zA-Z0-9]{1,5})`/g;
|
|
52
|
+
while ((m = codeRe.exec(body)) !== null) {
|
|
53
|
+
const target = m[1];
|
|
54
|
+
if (target.includes("://")) continue;
|
|
55
|
+
if (!target.includes("/")) continue;
|
|
56
|
+
refs.add(target);
|
|
57
|
+
}
|
|
58
|
+
return [...refs];
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region src/skill-checks/check.ts
|
|
62
|
+
const WHEN_TO_USE_RE = /\b(use when|use this|when (?:the )?(?:user|you))\b/i;
|
|
63
|
+
const WHEN_NOT_RE = /\bdo not use|do not invoke|don'?t use|not for|avoid using\b/i;
|
|
64
|
+
const KNOWN_CHECKS = new Set(RUNTIME_CHECK_TYPES);
|
|
65
|
+
async function isFile(path) {
|
|
66
|
+
try {
|
|
67
|
+
return (await stat(path)).isFile();
|
|
68
|
+
} catch {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function isDirectory(path) {
|
|
73
|
+
try {
|
|
74
|
+
return (await stat(path)).isDirectory();
|
|
75
|
+
} catch {
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
async function isExecutable(path) {
|
|
80
|
+
try {
|
|
81
|
+
await access(path, constants.X_OK);
|
|
82
|
+
return true;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function resolveSkillMd(path) {
|
|
88
|
+
if (await isFile(path)) return path;
|
|
89
|
+
return join(path, "SKILL.md");
|
|
90
|
+
}
|
|
91
|
+
function diagnostic(level, code, message, path, suggestion) {
|
|
92
|
+
return {
|
|
93
|
+
level,
|
|
94
|
+
code,
|
|
95
|
+
message,
|
|
96
|
+
...path ? { path } : {},
|
|
97
|
+
...suggestion ? { suggestion } : {}
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function checkSkillQuality(skill) {
|
|
101
|
+
const out = [];
|
|
102
|
+
const name = typeof skill.frontmatter.name === "string" ? skill.frontmatter.name.trim() : "";
|
|
103
|
+
const description = typeof skill.frontmatter.description === "string" ? skill.frontmatter.description.trim() : "";
|
|
104
|
+
if (!name) out.push(diagnostic("error", "skill.name.missing", "SKILL.md frontmatter requires a non-empty name.", skill.skillMdPath));
|
|
105
|
+
if (!description) out.push(diagnostic("error", "skill.description.missing", "SKILL.md frontmatter requires a non-empty description.", skill.skillMdPath));
|
|
106
|
+
else if (skill.frontmatter["disable-model-invocation"] !== true) {
|
|
107
|
+
if (!WHEN_TO_USE_RE.test(description)) out.push(diagnostic("warning", "skill.routing.when", "The description does not clearly say when the skill should be used.", skill.skillMdPath, "Add a concrete `Use when ...` trigger."));
|
|
108
|
+
if (!WHEN_NOT_RE.test(description)) out.push(diagnostic("warning", "skill.routing.boundary", "The description does not clearly say when the skill should not be used.", skill.skillMdPath, "Add a concrete `Do not use for ...` boundary."));
|
|
109
|
+
}
|
|
110
|
+
if (skill.totalLines > 200) out.push(diagnostic("warning", "skill.size", `SKILL.md has ${skill.totalLines} lines; the default quality target is 200.`, skill.skillMdPath, "Move reference material into linked files."));
|
|
111
|
+
for (const reference of skill.missingReferences) out.push(diagnostic("error", "skill.reference.missing", `Referenced file does not exist: ${reference}`, resolve(skill.skillDir, reference)));
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
function checkName(entry) {
|
|
115
|
+
if (typeof entry === "string") return entry;
|
|
116
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return void 0;
|
|
117
|
+
const record = entry;
|
|
118
|
+
if (typeof record.type === "string") return record.type;
|
|
119
|
+
return Object.keys(record)[0];
|
|
120
|
+
}
|
|
121
|
+
function checkArgs(entry) {
|
|
122
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return {};
|
|
123
|
+
const record = entry;
|
|
124
|
+
if (typeof record.type === "string") return record;
|
|
125
|
+
const key = Object.keys(record)[0];
|
|
126
|
+
const value = key ? record[key] : void 0;
|
|
127
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
128
|
+
}
|
|
129
|
+
async function checkVerifier(entry, fixtureRoot) {
|
|
130
|
+
const name = checkName(entry);
|
|
131
|
+
if (name !== "verifier.succeeds" && name !== "verifier.fails") return [];
|
|
132
|
+
const run = checkArgs(entry).run;
|
|
133
|
+
if (typeof run !== "string" || run.length === 0) return [];
|
|
134
|
+
const executable = isAbsolute(run) ? run : resolve(fixtureRoot, run);
|
|
135
|
+
if (!await isFile(executable)) return [diagnostic("error", "verifier.missing", `Verifier does not exist: ${run}`, executable, "Create the verifier or correct its run path.")];
|
|
136
|
+
if (!await isExecutable(executable)) return [diagnostic("error", "verifier.not_executable", `Verifier is not executable: ${run}`, executable, `Run chmod +x ${run}`)];
|
|
137
|
+
return [];
|
|
138
|
+
}
|
|
139
|
+
async function checkMock(mock, packDir) {
|
|
140
|
+
const localFiles = mock.kind === "command" ? [mock.executable] : [...mock.command && (mock.command.startsWith("./") || mock.command.startsWith("../") || isAbsolute(mock.command)) ? [mock.command] : [], ...mock.args.filter((value) => value.startsWith("./") || value.startsWith("../"))];
|
|
141
|
+
for (const value of localFiles) {
|
|
142
|
+
if (!(value.startsWith("./") || value.startsWith("../") || isAbsolute(value))) continue;
|
|
143
|
+
const path = isAbsolute(value) ? value : resolve(packDir, value);
|
|
144
|
+
if (!await isFile(path)) return [diagnostic("error", "mock.file.missing", `Mock Service file does not exist: ${value}`, path)];
|
|
145
|
+
}
|
|
146
|
+
if (mock.kind === "command") {
|
|
147
|
+
const path = isAbsolute(mock.executable) ? mock.executable : resolve(packDir, mock.executable);
|
|
148
|
+
if (!await isFile(path)) return [diagnostic("error", "mock.executable.missing", `Mock command does not exist: ${mock.executable}`, path)];
|
|
149
|
+
if (!await isExecutable(path)) return [diagnostic("error", "mock.executable.not_executable", `Mock command is not executable: ${mock.executable}`, path)];
|
|
150
|
+
}
|
|
151
|
+
if (mock.kind === "mcp" && mock.transport === "stdio" && !mock.command) return [diagnostic("error", "mock.mcp.command", `MCP stdio mock "${mock.name}" requires a command.`)];
|
|
152
|
+
if (mock.kind === "mcp" && mock.transport === "http" && !mock.url) return [diagnostic("error", "mock.mcp.url", `MCP HTTP mock "${mock.name}" requires a URL.`)];
|
|
153
|
+
return [];
|
|
154
|
+
}
|
|
155
|
+
async function checkCase(test, pack, packDir) {
|
|
156
|
+
const out = [];
|
|
157
|
+
const fixturePath = test.fixture ? isAbsolute(test.fixture) ? test.fixture : resolve(packDir, test.fixture) : void 0;
|
|
158
|
+
if (fixturePath && !await isDirectory(fixturePath)) out.push(diagnostic("error", "fixture.missing", `Fixture directory does not exist: ${test.fixture}`, fixturePath));
|
|
159
|
+
const verifierRoot = fixturePath ?? packDir;
|
|
160
|
+
for (const entry of [...test.preconditions, ...test.expect]) {
|
|
161
|
+
const name = checkName(entry);
|
|
162
|
+
if (!name || !KNOWN_CHECKS.has(name)) {
|
|
163
|
+
out.push(diagnostic("error", "check.unknown", `Unknown expectation: ${name ?? "invalid entry"}`));
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
out.push(...await checkVerifier(entry, verifierRoot));
|
|
167
|
+
}
|
|
168
|
+
for (const mock of [...pack.environment?.mocks ?? [], ...test.environment?.mocks ?? []]) out.push(...await checkMock(mock, packDir));
|
|
169
|
+
const mocks = [...pack.environment?.mocks ?? [], ...test.environment?.mocks ?? []];
|
|
170
|
+
const expectedChecks = test.expect.map(checkName);
|
|
171
|
+
if (test.mode === "routing" && !expectedChecks.includes("skill.loaded")) out.push(diagnostic("error", "routing.skill_loaded.required", "Routing Tests must prove that the target skill was loaded."));
|
|
172
|
+
const hasDistractors = pack.builtin_distractor || pack.distractor_skills.length > 0 || (test.distractor_skills?.length ?? 0) > 0;
|
|
173
|
+
if (test.mode === "routing" && hasDistractors && !expectedChecks.includes("skill.not_loaded")) out.push(diagnostic("error", "routing.skill_not_loaded.required", "Routing Tests with distractors must prove that unrelated skills were not loaded."));
|
|
174
|
+
const hasSkillEvidence = mocks.some((mock) => mock.kind === "mcp" && mock.provides_skill_evidence);
|
|
175
|
+
if (test.mode === "routing" && expectedChecks.includes("skill.loaded") && !hasSkillEvidence) out.push(diagnostic("error", "routing.observation.unsupported", "Native skill discovery does not currently expose trustworthy load evidence for routing assertions.", void 0, "Add an MCP boundary that emits observable skill-load telemetry and set provides_skill_evidence: true."));
|
|
176
|
+
if (!test.budget) out.push(diagnostic("warning", "budget.missing", `Test "${test.description ?? test.prompt}" has no token budget.`, void 0, "Add a budget after observing a representative passing run."));
|
|
177
|
+
return out;
|
|
178
|
+
}
|
|
179
|
+
async function checkSkillProject(options) {
|
|
180
|
+
const cwd = resolve(options.cwd ?? process.cwd());
|
|
181
|
+
const skillMd = await resolveSkillMd(isAbsolute(options.skillPath) ? options.skillPath : resolve(cwd, options.skillPath));
|
|
182
|
+
const skillName = basename(dirname(skillMd));
|
|
183
|
+
const testPack = options.testPackPath ? isAbsolute(options.testPackPath) ? options.testPackPath : resolve(cwd, options.testPackPath) : resolve(cwd, "tests", `${skillName}.yaml`);
|
|
184
|
+
const diagnostics = [];
|
|
185
|
+
let skill;
|
|
186
|
+
try {
|
|
187
|
+
skill = await parseSkillMd(skillMd);
|
|
188
|
+
diagnostics.push(...checkSkillQuality(skill));
|
|
189
|
+
} catch (error) {
|
|
190
|
+
diagnostics.push(diagnostic("error", "skill.read", `Could not read SKILL.md: ${error instanceof Error ? error.message : String(error)}`, skillMd));
|
|
191
|
+
}
|
|
192
|
+
let pack;
|
|
193
|
+
try {
|
|
194
|
+
pack = await loadTestPack(testPack);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
diagnostics.push(diagnostic("error", "test_pack.read", error instanceof Error ? error.message : String(error), testPack));
|
|
197
|
+
}
|
|
198
|
+
if (pack) {
|
|
199
|
+
const declaredSkill = resolve(dirname(testPack), pack.skill);
|
|
200
|
+
if (declaredSkill !== dirname(skillMd) && declaredSkill !== skillMd) diagnostics.push(diagnostic("error", "test_pack.skill_mismatch", `Test Pack declares ${pack.skill}, which does not match the checked skill.`, declaredSkill, `Set skill: ${options.skillPath}`));
|
|
201
|
+
for (const test of pack.tests) diagnostics.push(...await checkCase(test, pack, dirname(testPack)));
|
|
202
|
+
}
|
|
203
|
+
const hasErrors = diagnostics.some((item) => item.level === "error");
|
|
204
|
+
const hasWarnings = diagnostics.some((item) => item.level === "warning");
|
|
205
|
+
return {
|
|
206
|
+
schemaVersion: 1,
|
|
207
|
+
ok: !hasErrors && !(options.strict && hasWarnings),
|
|
208
|
+
skill: skillMd,
|
|
209
|
+
testPack,
|
|
210
|
+
diagnostics
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
//#endregion
|
|
214
|
+
//#region src/cli/init.ts
|
|
215
|
+
/** Agent Skill Evals owns setup and static checks; Promptfoo owns runtime evals. */
|
|
216
|
+
const SHIMS = {
|
|
217
|
+
"agent-skill-evals/agent.js": "export { default } from \"agent-skill-evals/agent\";\n",
|
|
218
|
+
"agent-skill-evals/assertions.js": "import(\"agent-skill-evals/assertions\").then(({ default: grade }) => grade(output, context))\n",
|
|
219
|
+
"agent-skill-evals/test-generator.js": "export { default } from \"agent-skill-evals/test-generator\";\n"
|
|
220
|
+
};
|
|
221
|
+
function promptfooConfig(adapter, testsPath) {
|
|
222
|
+
return `description: Agent skill tests (${adapter})
|
|
223
|
+
|
|
224
|
+
prompts:
|
|
225
|
+
- "{{prompt}}"
|
|
226
|
+
|
|
227
|
+
providers:
|
|
228
|
+
- id: file://./agent-skill-evals/agent.js
|
|
229
|
+
label: ${adapter}-agent
|
|
230
|
+
config:
|
|
231
|
+
preset: ${adapter}
|
|
232
|
+
timeoutMs: 300000
|
|
233
|
+
|
|
234
|
+
defaultTest:
|
|
235
|
+
options:
|
|
236
|
+
runSerially: true
|
|
237
|
+
|
|
238
|
+
tests:
|
|
239
|
+
path: file://./agent-skill-evals/test-generator.js
|
|
240
|
+
config:
|
|
241
|
+
path: ./${testsPath}
|
|
242
|
+
assertionPath: file://./agent-skill-evals/assertions.js
|
|
243
|
+
`;
|
|
244
|
+
}
|
|
245
|
+
function starterTestPack(skillPathFromPack, skillName) {
|
|
246
|
+
return `# yaml-language-server: $schema=../node_modules/agent-skill-evals/schema/test-pack.schema.json
|
|
247
|
+
skill: ${skillPathFromPack}
|
|
248
|
+
|
|
249
|
+
tests:
|
|
250
|
+
- description: ${skillName} produces the expected result
|
|
251
|
+
prompt: "TODO: replace with a realistic request this skill should handle."
|
|
252
|
+
expect:
|
|
253
|
+
- output.contains:
|
|
254
|
+
text: "TODO_EXPECTED_RESULT"
|
|
255
|
+
`;
|
|
256
|
+
}
|
|
257
|
+
function scaffold(options = {}) {
|
|
258
|
+
const root = resolve(options.dir ?? ".");
|
|
259
|
+
const adapter = options.adapter ?? "claude-code";
|
|
260
|
+
const result = {
|
|
261
|
+
created: [],
|
|
262
|
+
skipped: [],
|
|
263
|
+
errors: []
|
|
264
|
+
};
|
|
265
|
+
if (!AGENT_PRESETS[adapter]) {
|
|
266
|
+
result.errors.push(`unknown adapter "${adapter}". Supported adapters: ${PRESET_IDS.join(", ")}`);
|
|
267
|
+
return result;
|
|
268
|
+
}
|
|
269
|
+
const skillPath = options.skill ?? "./skills/my-skill";
|
|
270
|
+
const skillName = basename(skillPath.replace(/\/+$/, "")) || "my-skill";
|
|
271
|
+
const testsPath = `tests/${skillName}.yaml`;
|
|
272
|
+
const skillPathFromPack = relative(dirname(join(root, testsPath)), resolve(root, skillPath)).replaceAll("\\", "/");
|
|
273
|
+
const files = {
|
|
274
|
+
...SHIMS,
|
|
275
|
+
"promptfooconfig.yaml": promptfooConfig(adapter, testsPath),
|
|
276
|
+
[testsPath]: starterTestPack(skillPathFromPack, skillName)
|
|
277
|
+
};
|
|
278
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
279
|
+
const target = join(root, relativePath);
|
|
280
|
+
if (existsSync(target) && !options.force) {
|
|
281
|
+
result.skipped.push(relativePath);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
286
|
+
writeFileSync(target, content);
|
|
287
|
+
result.created.push(relativePath);
|
|
288
|
+
} catch (error) {
|
|
289
|
+
result.errors.push(`${relativePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return result;
|
|
293
|
+
}
|
|
294
|
+
function optionValue(argv, name) {
|
|
295
|
+
const index = argv.indexOf(name);
|
|
296
|
+
return index >= 0 ? argv[index + 1] : void 0;
|
|
297
|
+
}
|
|
298
|
+
const USAGE = `Usage:
|
|
299
|
+
agent-skill-evals init --skill <path> --adapter <${PRESET_IDS.join("|")}> [--dir <path>] [--force]
|
|
300
|
+
agent-skill-evals check <skill-path> [--tests <pack-path>] [--strict] [--json]
|
|
301
|
+
|
|
302
|
+
Static checks are local and agent-free. Runtime evaluations continue to use promptfoo eval.
|
|
303
|
+
`;
|
|
304
|
+
async function main(argv = process.argv.slice(2), io = {
|
|
305
|
+
cwd: process.cwd(),
|
|
306
|
+
stdout: (text) => process.stdout.write(text),
|
|
307
|
+
stderr: (text) => process.stderr.write(text)
|
|
308
|
+
}) {
|
|
309
|
+
const command = argv[0];
|
|
310
|
+
if (command === "check") {
|
|
311
|
+
const skillPath = argv[1];
|
|
312
|
+
if (!skillPath || skillPath.startsWith("-")) {
|
|
313
|
+
io.stderr("error: check requires a skill path\n");
|
|
314
|
+
return 1;
|
|
315
|
+
}
|
|
316
|
+
const result = await checkSkillProject({
|
|
317
|
+
cwd: io.cwd,
|
|
318
|
+
skillPath,
|
|
319
|
+
...optionValue(argv, "--tests") ? { testPackPath: optionValue(argv, "--tests") } : {},
|
|
320
|
+
strict: argv.includes("--strict")
|
|
321
|
+
});
|
|
322
|
+
if (argv.includes("--json")) io.stdout(`${JSON.stringify(result, null, 2)}\n`);
|
|
323
|
+
else {
|
|
324
|
+
for (const item of result.diagnostics) {
|
|
325
|
+
const target = item.path ? ` (${item.path})` : "";
|
|
326
|
+
io.stdout(`${item.level.toUpperCase()} ${item.code}: ${item.message}${target}\n`);
|
|
327
|
+
if (item.suggestion) io.stdout(` Fix: ${item.suggestion}\n`);
|
|
328
|
+
}
|
|
329
|
+
io.stdout(result.ok ? "Skill checks passed.\n" : "Skill checks failed.\n");
|
|
330
|
+
}
|
|
331
|
+
return result.ok ? 0 : 1;
|
|
332
|
+
}
|
|
333
|
+
if (command !== "init") {
|
|
334
|
+
io.stdout(USAGE);
|
|
335
|
+
return command ? 1 : 0;
|
|
336
|
+
}
|
|
337
|
+
const result = scaffold({
|
|
338
|
+
dir: optionValue(argv, "--dir") ?? io.cwd,
|
|
339
|
+
adapter: optionValue(argv, "--adapter"),
|
|
340
|
+
skill: optionValue(argv, "--skill"),
|
|
341
|
+
force: argv.includes("--force")
|
|
342
|
+
});
|
|
343
|
+
for (const file of result.created) io.stdout(`created ${file}\n`);
|
|
344
|
+
for (const file of result.skipped) io.stdout(`skipped ${file} (exists; use --force to overwrite)\n`);
|
|
345
|
+
for (const error of result.errors) io.stderr(`error: ${error}\n`);
|
|
346
|
+
if (result.created.length > 0) {
|
|
347
|
+
const skill = optionValue(argv, "--skill") ?? "./skills/my-skill";
|
|
348
|
+
io.stdout(`
|
|
349
|
+
Next steps:
|
|
350
|
+
1. pnpm exec agent-skill-evals check ${skill}\n 2. Replace the TODO expectation with a realistic evidence check.
|
|
351
|
+
3. pnpm exec promptfoo eval
|
|
352
|
+
`);
|
|
353
|
+
}
|
|
354
|
+
return result.errors.length > 0 ? 1 : 0;
|
|
355
|
+
}
|
|
356
|
+
if ((() => {
|
|
357
|
+
if (typeof process.argv[1] !== "string") return false;
|
|
358
|
+
try {
|
|
359
|
+
return import.meta.url === new URL(`file://${process.argv[1]}`).href || import.meta.url === new URL(`file://${resolve(process.argv[1])}`).href;
|
|
360
|
+
} catch {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
})()) main().then((exitCode) => {
|
|
364
|
+
process.exitCode = exitCode;
|
|
365
|
+
});
|
|
366
|
+
//#endregion
|
|
367
|
+
export { main, scaffold };
|
|
368
|
+
|
|
369
|
+
//# sourceMappingURL=init.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"init.mjs","names":["resolvePath"],"sources":["../../src/skill-checks/skill.ts","../../src/skill-checks/check.ts","../../src/cli/init.ts"],"sourcesContent":["import { join, dirname, isAbsolute, resolve as resolvePath } from \"node:path\";\nimport { readFile } from \"node:fs/promises\";\nimport { parseYaml, pathExists } from \"../internal-services.js\";\n\nexport interface SkillFrontmatter {\n name?: string;\n description?: string;\n [key: string]: unknown;\n}\n\nexport interface ParsedSkill {\n /** Absolute path to the SKILL.md file. */\n skillMdPath: string;\n /** Absolute path to the skill folder (parent of SKILL.md). */\n skillDir: string;\n frontmatter: SkillFrontmatter;\n /** Raw markdown body (after frontmatter). */\n body: string;\n /** Total lines in SKILL.md (including frontmatter). */\n totalLines: number;\n /** Relative paths referenced from SKILL.md (markdown links + script paths). */\n references: string[];\n /** Subset of references that don't exist on disk. */\n missingReferences: string[];\n}\n\nconst FRONTMATTER_RE = /^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?/;\n\nexport async function parseSkillMd(skillMdPath: string): Promise<ParsedSkill> {\n const skillDir = dirname(skillMdPath);\n const raw = await readFile(skillMdPath, \"utf8\");\n const totalLines = raw.split(/\\r?\\n/).length;\n\n let frontmatter: SkillFrontmatter = {};\n let body = raw;\n const fmMatch = raw.match(FRONTMATTER_RE);\n if (fmMatch) {\n const parsed = parseYaml(fmMatch[1]!);\n if (parsed && typeof parsed === \"object\") {\n frontmatter = parsed as SkillFrontmatter;\n }\n body = raw.slice(fmMatch[0].length);\n }\n\n const references = extractReferences(body);\n const missingReferences: string[] = [];\n for (const ref of references) {\n const abs = isAbsolute(ref) ? ref : resolvePath(skillDir, ref);\n if (!(await pathExists(abs))) missingReferences.push(ref);\n }\n\n return {\n skillMdPath,\n skillDir,\n frontmatter,\n body,\n totalLines,\n references,\n missingReferences,\n };\n}\n\n/**\n * Extract relative paths from a SKILL.md body. Captures:\n * - Markdown links: [text](path)\n * - Bare relative paths in code/inline-code: ./foo, ../foo, foo/bar.sh\n * Skips http(s):// URLs.\n */\nfunction extractReferences(body: string): string[] {\n const refs = new Set<string>();\n const linkRe = /\\[[^\\]]*\\]\\(([^)\\s]+)\\)/g;\n let m: RegExpExecArray | null;\n while ((m = linkRe.exec(body)) !== null) {\n const target = m[1]!;\n if (target.startsWith(\"http://\") || target.startsWith(\"https://\")) continue;\n if (target.startsWith(\"#\")) continue;\n refs.add(target);\n }\n // Inline-code relative paths like `./script.sh` or `path/to/file.ext`.\n const codeRe = /`((?:\\.{1,2}\\/)?[a-zA-Z0-9_./-]+\\.[a-zA-Z0-9]{1,5})`/g;\n while ((m = codeRe.exec(body)) !== null) {\n const target = m[1]!;\n if (target.includes(\"://\")) continue;\n // Require a path separator: a bare dotted identifier in inline code\n // (a metric name like `skill.test`, a version, etc.) is not a file ref.\n if (!target.includes(\"/\")) continue;\n refs.add(target);\n }\n return [...refs];\n}\n\nexport function joinSkill(skillDir: string, rel: string): string {\n return isAbsolute(rel) ? rel : join(skillDir, rel);\n}\n","import { constants } from \"node:fs\";\nimport { access, stat } from \"node:fs/promises\";\nimport { basename, dirname, isAbsolute, join, resolve } from \"node:path\";\nimport { loadTestPack, type CleanTestCase, type CleanTestPack, type MockService } from \"../test-pack.js\";\nimport { parseSkillMd, type ParsedSkill } from \"./skill.js\";\nimport { RUNTIME_CHECK_TYPES } from \"../runtime-checks/catalog.js\";\n\nexport type DiagnosticLevel = \"error\" | \"warning\";\n\nexport interface CheckDiagnostic {\n level: DiagnosticLevel;\n code: string;\n message: string;\n path?: string;\n suggestion?: string;\n}\n\nexport interface CheckSkillProjectOptions {\n cwd?: string;\n skillPath: string;\n testPackPath?: string;\n strict?: boolean;\n}\n\nexport interface CheckSkillProjectResult {\n schemaVersion: 1;\n ok: boolean;\n skill: string;\n testPack: string;\n diagnostics: CheckDiagnostic[];\n}\n\nconst WHEN_TO_USE_RE = /\\b(use when|use this|when (?:the )?(?:user|you))\\b/i;\nconst WHEN_NOT_RE = /\\bdo not use|do not invoke|don'?t use|not for|avoid using\\b/i;\nconst KNOWN_CHECKS = new Set<string>(RUNTIME_CHECK_TYPES);\n\nasync function isFile(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isFile();\n } catch {\n return false;\n }\n}\n\nasync function isDirectory(path: string): Promise<boolean> {\n try {\n return (await stat(path)).isDirectory();\n } catch {\n return false;\n }\n}\n\nasync function isExecutable(path: string): Promise<boolean> {\n try {\n await access(path, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\nasync function resolveSkillMd(path: string): Promise<string> {\n if (await isFile(path)) return path;\n return join(path, \"SKILL.md\");\n}\n\nfunction diagnostic(\n level: DiagnosticLevel,\n code: string,\n message: string,\n path?: string,\n suggestion?: string,\n): CheckDiagnostic {\n return {\n level,\n code,\n message,\n ...(path ? { path } : {}),\n ...(suggestion ? { suggestion } : {}),\n };\n}\n\nfunction checkSkillQuality(skill: ParsedSkill): CheckDiagnostic[] {\n const out: CheckDiagnostic[] = [];\n const name = typeof skill.frontmatter.name === \"string\" ? skill.frontmatter.name.trim() : \"\";\n const description = typeof skill.frontmatter.description === \"string\"\n ? skill.frontmatter.description.trim()\n : \"\";\n if (!name) {\n out.push(diagnostic(\"error\", \"skill.name.missing\", \"SKILL.md frontmatter requires a non-empty name.\", skill.skillMdPath));\n }\n if (!description) {\n out.push(diagnostic(\"error\", \"skill.description.missing\", \"SKILL.md frontmatter requires a non-empty description.\", skill.skillMdPath));\n } else if (skill.frontmatter[\"disable-model-invocation\"] !== true) {\n if (!WHEN_TO_USE_RE.test(description)) {\n out.push(diagnostic(\"warning\", \"skill.routing.when\", \"The description does not clearly say when the skill should be used.\", skill.skillMdPath, \"Add a concrete `Use when ...` trigger.\"));\n }\n if (!WHEN_NOT_RE.test(description)) {\n out.push(diagnostic(\"warning\", \"skill.routing.boundary\", \"The description does not clearly say when the skill should not be used.\", skill.skillMdPath, \"Add a concrete `Do not use for ...` boundary.\"));\n }\n }\n if (skill.totalLines > 200) {\n out.push(diagnostic(\"warning\", \"skill.size\", `SKILL.md has ${skill.totalLines} lines; the default quality target is 200.`, skill.skillMdPath, \"Move reference material into linked files.\"));\n }\n for (const reference of skill.missingReferences) {\n out.push(diagnostic(\"error\", \"skill.reference.missing\", `Referenced file does not exist: ${reference}`, resolve(skill.skillDir, reference)));\n }\n return out;\n}\n\nfunction checkName(entry: unknown): string | undefined {\n if (typeof entry === \"string\") return entry;\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return undefined;\n const record = entry as Record<string, unknown>;\n if (typeof record.type === \"string\") return record.type;\n return Object.keys(record)[0];\n}\n\nfunction checkArgs(entry: unknown): Record<string, unknown> {\n if (!entry || typeof entry !== \"object\" || Array.isArray(entry)) return {};\n const record = entry as Record<string, unknown>;\n if (typeof record.type === \"string\") return record;\n const key = Object.keys(record)[0];\n const value = key ? record[key] : undefined;\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? value as Record<string, unknown>\n : {};\n}\n\nasync function checkVerifier(\n entry: unknown,\n fixtureRoot: string,\n): Promise<CheckDiagnostic[]> {\n const name = checkName(entry);\n if (name !== \"verifier.succeeds\" && name !== \"verifier.fails\") return [];\n const run = checkArgs(entry).run;\n if (typeof run !== \"string\" || run.length === 0) return [];\n const executable = isAbsolute(run) ? run : resolve(fixtureRoot, run);\n if (!(await isFile(executable))) {\n return [diagnostic(\"error\", \"verifier.missing\", `Verifier does not exist: ${run}`, executable, \"Create the verifier or correct its run path.\")];\n }\n if (!(await isExecutable(executable))) {\n return [diagnostic(\"error\", \"verifier.not_executable\", `Verifier is not executable: ${run}`, executable, `Run chmod +x ${run}`)];\n }\n return [];\n}\n\nasync function checkMock(mock: MockService, packDir: string): Promise<CheckDiagnostic[]> {\n const localFiles = mock.kind === \"command\"\n ? [mock.executable]\n : [\n ...(mock.command && (mock.command.startsWith(\"./\") || mock.command.startsWith(\"../\") || isAbsolute(mock.command))\n ? [mock.command]\n : []),\n ...mock.args.filter((value) => value.startsWith(\"./\") || value.startsWith(\"../\")),\n ];\n for (const value of localFiles) {\n if (!(value.startsWith(\"./\") || value.startsWith(\"../\") || isAbsolute(value))) continue;\n const path = isAbsolute(value) ? value : resolve(packDir, value);\n if (!(await isFile(path))) return [diagnostic(\"error\", \"mock.file.missing\", `Mock Service file does not exist: ${value}`, path)];\n }\n if (mock.kind === \"command\") {\n const path = isAbsolute(mock.executable) ? mock.executable : resolve(packDir, mock.executable);\n if (!(await isFile(path))) return [diagnostic(\"error\", \"mock.executable.missing\", `Mock command does not exist: ${mock.executable}`, path)];\n if (!(await isExecutable(path))) return [diagnostic(\"error\", \"mock.executable.not_executable\", `Mock command is not executable: ${mock.executable}`, path)];\n }\n if (mock.kind === \"mcp\" && mock.transport === \"stdio\" && !mock.command) {\n return [diagnostic(\"error\", \"mock.mcp.command\", `MCP stdio mock \"${mock.name}\" requires a command.`)];\n }\n if (mock.kind === \"mcp\" && mock.transport === \"http\" && !mock.url) {\n return [diagnostic(\"error\", \"mock.mcp.url\", `MCP HTTP mock \"${mock.name}\" requires a URL.`)];\n }\n return [];\n}\n\nasync function checkCase(\n test: CleanTestCase,\n pack: CleanTestPack,\n packDir: string,\n): Promise<CheckDiagnostic[]> {\n const out: CheckDiagnostic[] = [];\n const fixturePath = test.fixture\n ? isAbsolute(test.fixture) ? test.fixture : resolve(packDir, test.fixture)\n : undefined;\n if (fixturePath && !(await isDirectory(fixturePath))) {\n out.push(diagnostic(\"error\", \"fixture.missing\", `Fixture directory does not exist: ${test.fixture}`, fixturePath));\n }\n const verifierRoot = fixturePath ?? packDir;\n for (const entry of [...test.preconditions, ...test.expect]) {\n const name = checkName(entry);\n if (!name || !KNOWN_CHECKS.has(name)) {\n out.push(diagnostic(\"error\", \"check.unknown\", `Unknown expectation: ${name ?? \"invalid entry\"}`));\n continue;\n }\n out.push(...await checkVerifier(entry, verifierRoot));\n }\n for (const mock of [...(pack.environment?.mocks ?? []), ...(test.environment?.mocks ?? [])]) {\n out.push(...await checkMock(mock, packDir));\n }\n const mocks = [...(pack.environment?.mocks ?? []), ...(test.environment?.mocks ?? [])];\n const expectedChecks = test.expect.map(checkName);\n if (test.mode === \"routing\" && !expectedChecks.includes(\"skill.loaded\")) {\n out.push(diagnostic(\"error\", \"routing.skill_loaded.required\", \"Routing Tests must prove that the target skill was loaded.\"));\n }\n const hasDistractors =\n pack.builtin_distractor ||\n pack.distractor_skills.length > 0 ||\n (test.distractor_skills?.length ?? 0) > 0;\n if (test.mode === \"routing\" && hasDistractors && !expectedChecks.includes(\"skill.not_loaded\")) {\n out.push(diagnostic(\"error\", \"routing.skill_not_loaded.required\", \"Routing Tests with distractors must prove that unrelated skills were not loaded.\"));\n }\n const hasSkillEvidence = mocks.some(\n (mock) => mock.kind === \"mcp\" && mock.provides_skill_evidence,\n );\n if (test.mode === \"routing\" && expectedChecks.includes(\"skill.loaded\") && !hasSkillEvidence) {\n out.push(diagnostic(\n \"error\",\n \"routing.observation.unsupported\",\n \"Native skill discovery does not currently expose trustworthy load evidence for routing assertions.\",\n undefined,\n \"Add an MCP boundary that emits observable skill-load telemetry and set provides_skill_evidence: true.\",\n ));\n }\n if (!test.budget) {\n out.push(diagnostic(\"warning\", \"budget.missing\", `Test \"${test.description ?? test.prompt}\" has no token budget.`, undefined, \"Add a budget after observing a representative passing run.\"));\n }\n return out;\n}\n\nexport async function checkSkillProject(\n options: CheckSkillProjectOptions,\n): Promise<CheckSkillProjectResult> {\n const cwd = resolve(options.cwd ?? process.cwd());\n const requestedSkill = isAbsolute(options.skillPath) ? options.skillPath : resolve(cwd, options.skillPath);\n const skillMd = await resolveSkillMd(requestedSkill);\n const skillName = basename(dirname(skillMd));\n const testPack = options.testPackPath\n ? isAbsolute(options.testPackPath) ? options.testPackPath : resolve(cwd, options.testPackPath)\n : resolve(cwd, \"tests\", `${skillName}.yaml`);\n const diagnostics: CheckDiagnostic[] = [];\n\n let skill: ParsedSkill | undefined;\n try {\n skill = await parseSkillMd(skillMd);\n diagnostics.push(...checkSkillQuality(skill));\n } catch (error) {\n diagnostics.push(diagnostic(\"error\", \"skill.read\", `Could not read SKILL.md: ${error instanceof Error ? error.message : String(error)}`, skillMd));\n }\n\n let pack: CleanTestPack | undefined;\n try {\n pack = await loadTestPack(testPack);\n } catch (error) {\n diagnostics.push(diagnostic(\"error\", \"test_pack.read\", error instanceof Error ? error.message : String(error), testPack));\n }\n\n if (pack) {\n const declaredSkill = resolve(dirname(testPack), pack.skill);\n const actualSkillDir = dirname(skillMd);\n if (declaredSkill !== actualSkillDir && declaredSkill !== skillMd) {\n diagnostics.push(diagnostic(\"error\", \"test_pack.skill_mismatch\", `Test Pack declares ${pack.skill}, which does not match the checked skill.`, declaredSkill, `Set skill: ${options.skillPath}`));\n }\n for (const test of pack.tests) diagnostics.push(...await checkCase(test, pack, dirname(testPack)));\n }\n\n const hasErrors = diagnostics.some((item) => item.level === \"error\");\n const hasWarnings = diagnostics.some((item) => item.level === \"warning\");\n return {\n schemaVersion: 1,\n ok: !hasErrors && !(options.strict && hasWarnings),\n skill: skillMd,\n testPack,\n diagnostics,\n };\n}\n","#!/usr/bin/env node\n/** Agent Skill Evals owns setup and static checks; Promptfoo owns runtime evals. */\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { basename, dirname, join, relative, resolve } from \"node:path\";\nimport { AGENT_PRESETS, PRESET_IDS } from \"../agent/presets.js\";\nimport { checkSkillProject } from \"../skill-checks/check.js\";\n\nexport interface InitOptions {\n dir?: string;\n adapter?: string;\n skill?: string;\n force?: boolean;\n}\n\nexport interface InitResult {\n created: string[];\n skipped: string[];\n errors: string[];\n}\n\nexport interface CliIo {\n cwd: string;\n stdout: (text: string) => void;\n stderr: (text: string) => void;\n}\n\nconst SHIMS: Record<string, string> = {\n \"agent-skill-evals/agent.js\": 'export { default } from \"agent-skill-evals/agent\";\\n',\n \"agent-skill-evals/assertions.js\":\n 'import(\"agent-skill-evals/assertions\").then(({ default: grade }) => grade(output, context))\\n',\n \"agent-skill-evals/test-generator.js\":\n 'export { default } from \"agent-skill-evals/test-generator\";\\n',\n};\n\nfunction promptfooConfig(adapter: string, testsPath: string): string {\n return `description: Agent skill tests (${adapter})\n\nprompts:\n - \"{{prompt}}\"\n\nproviders:\n - id: file://./agent-skill-evals/agent.js\n label: ${adapter}-agent\n config:\n preset: ${adapter}\n timeoutMs: 300000\n\ndefaultTest:\n options:\n runSerially: true\n\ntests:\n path: file://./agent-skill-evals/test-generator.js\n config:\n path: ./${testsPath}\n assertionPath: file://./agent-skill-evals/assertions.js\n`;\n}\n\nfunction starterTestPack(skillPathFromPack: string, skillName: string): string {\n return `# yaml-language-server: $schema=../node_modules/agent-skill-evals/schema/test-pack.schema.json\nskill: ${skillPathFromPack}\n\ntests:\n - description: ${skillName} produces the expected result\n prompt: \"TODO: replace with a realistic request this skill should handle.\"\n expect:\n - output.contains:\n text: \"TODO_EXPECTED_RESULT\"\n`;\n}\n\nexport function scaffold(options: InitOptions = {}): InitResult {\n const root = resolve(options.dir ?? \".\");\n const adapter = options.adapter ?? \"claude-code\";\n const result: InitResult = { created: [], skipped: [], errors: [] };\n if (!AGENT_PRESETS[adapter]) {\n result.errors.push(\n `unknown adapter \"${adapter}\". Supported adapters: ${PRESET_IDS.join(\", \")}`,\n );\n return result;\n }\n\n const skillPath = options.skill ?? \"./skills/my-skill\";\n const skillName = basename(skillPath.replace(/\\/+$/, \"\")) || \"my-skill\";\n const testsPath = `tests/${skillName}.yaml`;\n const packDir = dirname(join(root, testsPath));\n const absoluteSkill = resolve(root, skillPath);\n const skillPathFromPack = relative(packDir, absoluteSkill).replaceAll(\"\\\\\", \"/\");\n const files: Record<string, string> = {\n ...SHIMS,\n \"promptfooconfig.yaml\": promptfooConfig(adapter, testsPath),\n [testsPath]: starterTestPack(skillPathFromPack, skillName),\n };\n\n for (const [relativePath, content] of Object.entries(files)) {\n const target = join(root, relativePath);\n if (existsSync(target) && !options.force) {\n result.skipped.push(relativePath);\n continue;\n }\n try {\n mkdirSync(dirname(target), { recursive: true });\n writeFileSync(target, content);\n result.created.push(relativePath);\n } catch (error) {\n result.errors.push(\n `${relativePath}: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n return result;\n}\n\nfunction optionValue(argv: string[], name: string): string | undefined {\n const index = argv.indexOf(name);\n return index >= 0 ? argv[index + 1] : undefined;\n}\n\nconst USAGE = `Usage:\n agent-skill-evals init --skill <path> --adapter <${PRESET_IDS.join(\"|\")}> [--dir <path>] [--force]\n agent-skill-evals check <skill-path> [--tests <pack-path>] [--strict] [--json]\n\nStatic checks are local and agent-free. Runtime evaluations continue to use promptfoo eval.\n`;\n\nexport async function main(\n argv: string[] = process.argv.slice(2),\n io: CliIo = {\n cwd: process.cwd(),\n stdout: (text) => process.stdout.write(text),\n stderr: (text) => process.stderr.write(text),\n },\n): Promise<number> {\n const command = argv[0];\n if (command === \"check\") {\n const skillPath = argv[1];\n if (!skillPath || skillPath.startsWith(\"-\")) {\n io.stderr(\"error: check requires a skill path\\n\");\n return 1;\n }\n const result = await checkSkillProject({\n cwd: io.cwd,\n skillPath,\n ...(optionValue(argv, \"--tests\") ? { testPackPath: optionValue(argv, \"--tests\") } : {}),\n strict: argv.includes(\"--strict\"),\n });\n if (argv.includes(\"--json\")) {\n io.stdout(`${JSON.stringify(result, null, 2)}\\n`);\n } else {\n for (const item of result.diagnostics) {\n const target = item.path ? ` (${item.path})` : \"\";\n io.stdout(`${item.level.toUpperCase()} ${item.code}: ${item.message}${target}\\n`);\n if (item.suggestion) io.stdout(` Fix: ${item.suggestion}\\n`);\n }\n io.stdout(result.ok ? \"Skill checks passed.\\n\" : \"Skill checks failed.\\n\");\n }\n return result.ok ? 0 : 1;\n }\n\n if (command !== \"init\") {\n io.stdout(USAGE);\n return command ? 1 : 0;\n }\n\n const result = scaffold({\n dir: optionValue(argv, \"--dir\") ?? io.cwd,\n adapter: optionValue(argv, \"--adapter\"),\n skill: optionValue(argv, \"--skill\"),\n force: argv.includes(\"--force\"),\n });\n for (const file of result.created) io.stdout(`created ${file}\\n`);\n for (const file of result.skipped) io.stdout(`skipped ${file} (exists; use --force to overwrite)\\n`);\n for (const error of result.errors) io.stderr(`error: ${error}\\n`);\n if (result.created.length > 0) {\n const skill = optionValue(argv, \"--skill\") ?? \"./skills/my-skill\";\n io.stdout(\n \"\\nNext steps:\\n\" +\n ` 1. pnpm exec agent-skill-evals check ${skill}\\n` +\n \" 2. Replace the TODO expectation with a realistic evidence check.\\n\" +\n \" 3. pnpm exec promptfoo eval\\n\",\n );\n }\n return result.errors.length > 0 ? 1 : 0;\n}\n\nconst invokedDirectly = (() => {\n if (typeof process.argv[1] !== \"string\") return false;\n try {\n return import.meta.url === new URL(`file://${process.argv[1]}`).href ||\n import.meta.url === new URL(`file://${resolve(process.argv[1])}`).href;\n } catch {\n return false;\n }\n})();\n\nif (invokedDirectly) {\n void main().then((exitCode) => {\n process.exitCode = exitCode;\n });\n}\n"],"mappings":"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"}
|