@directive-run/claude-plugin 1.16.0

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.
Files changed (83) hide show
  1. package/.claude-plugin/plugin.json +6 -0
  2. package/LICENSE +26 -0
  3. package/README.md +113 -0
  4. package/dist/index.cjs +2 -0
  5. package/dist/index.cjs.map +1 -0
  6. package/dist/index.d.cts +66 -0
  7. package/dist/index.d.ts +66 -0
  8. package/dist/index.js +2 -0
  9. package/dist/index.js.map +1 -0
  10. package/package.json +64 -0
  11. package/skills/README.md +9 -0
  12. package/skills/building-ai-agents/SKILL.md +414 -0
  13. package/skills/building-ai-agents/ai-adapters.md +257 -0
  14. package/skills/building-ai-agents/ai-agents-streaming.md +307 -0
  15. package/skills/building-ai-agents/ai-communication.md +304 -0
  16. package/skills/building-ai-agents/api-skeleton.md +5 -0
  17. package/skills/building-ai-agents/examples.md +849 -0
  18. package/skills/building-ai-orchestrators/SKILL.md +401 -0
  19. package/skills/building-ai-orchestrators/ai-multi-agent.md +375 -0
  20. package/skills/building-ai-orchestrators/ai-orchestrator.md +466 -0
  21. package/skills/building-ai-orchestrators/ai-tasks.md +268 -0
  22. package/skills/building-ai-orchestrators/api-skeleton.md +5 -0
  23. package/skills/building-ai-orchestrators/examples.md +1609 -0
  24. package/skills/building-directive-systems/SKILL.md +499 -0
  25. package/skills/building-directive-systems/api-skeleton.md +5 -0
  26. package/skills/building-directive-systems/examples.md +4149 -0
  27. package/skills/building-directive-systems/multi-module.md +327 -0
  28. package/skills/building-directive-systems/plugins.md +361 -0
  29. package/skills/building-directive-systems/react-adapter.md +376 -0
  30. package/skills/building-directive-systems/system-api.md +463 -0
  31. package/skills/getting-started-with-directive/SKILL.md +198 -0
  32. package/skills/getting-started-with-directive/api-skeleton.md +5 -0
  33. package/skills/getting-started-with-directive/core-patterns.md +240 -0
  34. package/skills/getting-started-with-directive/examples.md +87 -0
  35. package/skills/getting-started-with-directive/sitemap.md +202 -0
  36. package/skills/hardening-ai-systems/SKILL.md +377 -0
  37. package/skills/hardening-ai-systems/ai-budget-resilience.md +408 -0
  38. package/skills/hardening-ai-systems/ai-guardrails-memory.md +370 -0
  39. package/skills/hardening-ai-systems/ai-security.md +400 -0
  40. package/skills/hardening-ai-systems/api-skeleton.md +5 -0
  41. package/skills/hardening-ai-systems/examples.md +628 -0
  42. package/skills/migrating-to-directive/SKILL.md +249 -0
  43. package/skills/migrating-to-directive/anti-patterns.md +382 -0
  44. package/skills/migrating-to-directive/api-skeleton.md +5 -0
  45. package/skills/migrating-to-directive/core-patterns.md +240 -0
  46. package/skills/migrating-to-directive/examples.md +511 -0
  47. package/skills/migrating-to-directive/schema-types.md +273 -0
  48. package/skills/reviewing-directive-code/SKILL.md +232 -0
  49. package/skills/reviewing-directive-code/anti-patterns.md +382 -0
  50. package/skills/reviewing-directive-code/api-skeleton.md +5 -0
  51. package/skills/reviewing-directive-code/core-patterns.md +240 -0
  52. package/skills/reviewing-directive-code/examples.md +463 -0
  53. package/skills/reviewing-directive-code/naming.md +321 -0
  54. package/skills/scaffolding-directive-modules/SKILL.md +218 -0
  55. package/skills/scaffolding-directive-modules/api-skeleton.md +5 -0
  56. package/skills/scaffolding-directive-modules/core-patterns.md +240 -0
  57. package/skills/scaffolding-directive-modules/examples.md +1386 -0
  58. package/skills/scaffolding-directive-modules/naming.md +321 -0
  59. package/skills/scaffolding-directive-modules/schema-types.md +273 -0
  60. package/skills/testing-ai-systems/SKILL.md +398 -0
  61. package/skills/testing-ai-systems/ai-debug-observability.md +329 -0
  62. package/skills/testing-ai-systems/ai-mcp-rag.md +293 -0
  63. package/skills/testing-ai-systems/ai-testing-evals.md +485 -0
  64. package/skills/testing-ai-systems/api-skeleton.md +5 -0
  65. package/skills/testing-ai-systems/examples.md +1261 -0
  66. package/skills/testing-directive-code/SKILL.md +479 -0
  67. package/skills/testing-directive-code/api-skeleton.md +5 -0
  68. package/skills/testing-directive-code/examples.md +156 -0
  69. package/skills/testing-directive-code/history.md +351 -0
  70. package/skills/testing-directive-code/testing.md +412 -0
  71. package/skills/writing-directive-constraints/SKILL.md +486 -0
  72. package/skills/writing-directive-constraints/api-skeleton.md +5 -0
  73. package/skills/writing-directive-constraints/constraints.md +274 -0
  74. package/skills/writing-directive-constraints/error-boundaries.md +330 -0
  75. package/skills/writing-directive-constraints/examples.md +1361 -0
  76. package/skills/writing-directive-constraints/resolvers.md +367 -0
  77. package/skills/writing-directive-modules/SKILL.md +342 -0
  78. package/skills/writing-directive-modules/anti-patterns.md +382 -0
  79. package/skills/writing-directive-modules/api-skeleton.md +5 -0
  80. package/skills/writing-directive-modules/core-patterns.md +240 -0
  81. package/skills/writing-directive-modules/examples.md +1639 -0
  82. package/skills/writing-directive-modules/naming.md +321 -0
  83. package/skills/writing-directive-modules/schema-types.md +273 -0
@@ -0,0 +1,398 @@
1
+ ---
2
+ name: testing-ai-systems
3
+ description: "Test AI orchestrators without real LLM API calls using createMockRunner, write quality evaluations for agent output, add debug observability with tracing and metrics, integrate MCP tool servers, and wire RAG pipelines. Use when writing unit tests for agents, setting up CI evaluation suites, debugging orchestrator behavior, or connecting external tool sources."
4
+ ---
5
+
6
+ # Testing AI Systems
7
+
8
+ ## Prerequisites
9
+
10
+ This skill applies when the project uses `@directive-run/ai`. If not found in `package.json`, suggest installing it: `npm install @directive-run/ai`.
11
+
12
+ ## When Claude Should Use This Skill
13
+
14
+ ## Auto-Invoke Triggers
15
+ - User asks how to test agents or orchestrators without calling real APIs
16
+ - User mentions `createMockRunner` or wants to mock LLM responses
17
+ - User wants to run evaluation suites or measure output quality
18
+ - User asks about tracing, logging, or debugging orchestrator execution
19
+ - User mentions MCP (Model Context Protocol) or tool servers
20
+ - User wants to add RAG (retrieval-augmented generation) to an agent
21
+
22
+ ## Exclusions
23
+ - Do NOT invoke for production guardrails or security – use `hardening-ai-systems`
24
+ - Do NOT invoke for orchestrator setup – use `building-ai-orchestrators`
25
+ - Do NOT invoke for provider runner config – use `building-ai-agents`
26
+
27
+ ---
28
+
29
+ ## Quick Reference
30
+
31
+ ## Decision Tree: Testing Approach
32
+
33
+ ```
34
+ What are you testing?
35
+ ├── Single resolver behavior → createMockRunner + unit test
36
+ ├── Full orchestrator flow → createTestOrchestrator
37
+ ├── Multi-agent pipeline → createTestMultiAgentOrchestrator
38
+ ├── Output quality (LLM-as-judge) → createEvaluation + assertOutputQuality
39
+ ├── Regression suite in CI → evaluation suite with recorded fixtures
40
+ └── Live debug of production → enableTracing + observability plugins
41
+ ```
42
+
43
+ ---
44
+
45
+ ## createMockRunner – Test Without API Calls
46
+
47
+ ```typescript
48
+ import { createMockRunner } from "@directive-run/ai/testing";
49
+
50
+ // Static responses – returned in sequence
51
+ const mockRunner = createMockRunner({
52
+ responses: [
53
+ { text: "First response.", usage: { inputTokens: 10, outputTokens: 5, totalTokens: 15 } },
54
+ { text: "Second response.", usage: { inputTokens: 8, outputTokens: 4, totalTokens: 12 } },
55
+ ],
56
+ });
57
+
58
+ // Dynamic – choose response based on prompt
59
+ const dynamicRunner = createMockRunner({
60
+ handler: async (options) => {
61
+ if (options.prompt.includes("capital")) {
62
+ return { text: "Paris", usage: { inputTokens: 5, outputTokens: 2, totalTokens: 7 } };
63
+ }
64
+
65
+ return { text: "I don't know.", usage: { inputTokens: 5, outputTokens: 4, totalTokens: 9 } };
66
+ },
67
+ });
68
+
69
+ // Simulate failures for error-path tests
70
+ const failingRunner = createMockRunner({
71
+ handler: async () => {
72
+ throw new Error("rate_limit: Too many requests");
73
+ },
74
+ });
75
+ ```
76
+
77
+ ## Unit Testing a Resolver
78
+
79
+ ```typescript
80
+ import { createAgentOrchestrator } from "@directive-run/ai";
81
+ import { createMockRunner } from "@directive-run/ai/testing";
82
+ import { t } from "@directive-run/core";
83
+ import { describe, it, expect } from "vitest";
84
+
85
+ // Build orchestrator with injected runner – enables swapping real/mock
86
+ function buildOrchestrator(runner: AgentRunner) {
87
+ return createAgentOrchestrator({
88
+ runner,
89
+ factsSchema: {
90
+ input: t.string(),
91
+ output: t.string().optional(),
92
+ status: t.string<"idle" | "done" | "error">(),
93
+ errorMessage: t.string().optional(),
94
+ },
95
+ init: (facts) => {
96
+ facts.status = "idle";
97
+ },
98
+ constraints: {
99
+ process: {
100
+ when: (facts) => facts.status === "idle" && !!facts.input,
101
+ require: { type: "PROCESS" },
102
+ },
103
+ },
104
+ resolvers: {
105
+ process: {
106
+ requirement: "PROCESS",
107
+ resolve: async (req, context) => {
108
+ try {
109
+ const result = await context.runner.run({ prompt: context.facts.input });
110
+ context.facts.output = result.text;
111
+ context.facts.status = "done";
112
+ } catch (error) {
113
+ context.facts.status = "error";
114
+ context.facts.errorMessage = (error as Error).message;
115
+ }
116
+ },
117
+ },
118
+ },
119
+ });
120
+ }
121
+
122
+ describe("process resolver", () => {
123
+ it("sets output on success", async () => {
124
+ const orchestrator = buildOrchestrator(
125
+ createMockRunner({ responses: [{ text: "Result.", usage: { inputTokens: 5, outputTokens: 2, totalTokens: 7 } }] })
126
+ );
127
+ const result = await orchestrator.run({ input: "test input" });
128
+
129
+ expect(result.facts.status).toBe("done");
130
+ expect(result.facts.output).toBe("Result.");
131
+ });
132
+
133
+ it("captures errors in facts", async () => {
134
+ const orchestrator = buildOrchestrator(
135
+ createMockRunner({ handler: async () => { throw new Error("Provider down"); } })
136
+ );
137
+ const result = await orchestrator.run({ input: "test" });
138
+
139
+ expect(result.facts.status).toBe("error");
140
+ expect(result.facts.errorMessage).toContain("Provider down");
141
+ });
142
+ });
143
+ ```
144
+
145
+ ## Testing Multi-Agent Orchestrators
146
+
147
+ ```typescript
148
+ import { createTestMultiAgentOrchestrator, assertMultiAgentState } from "@directive-run/ai/testing";
149
+
150
+ describe("research-write pipeline", () => {
151
+ it("passes research to writer via coordinator facts", async () => {
152
+ const testOrchestrator = createTestMultiAgentOrchestrator({
153
+ agentRunners: {
154
+ researcher: createMockRunner({
155
+ responses: [{ text: "Finding: qubits enable quantum advantage.", usage: { inputTokens: 10, outputTokens: 8, totalTokens: 18 } }],
156
+ }),
157
+ writer: createMockRunner({
158
+ responses: [{ text: "Qubits are the foundation of quantum computing.", usage: { inputTokens: 20, outputTokens: 10, totalTokens: 30 } }],
159
+ }),
160
+ },
161
+ });
162
+
163
+ const result = await testOrchestrator.run({ topic: "quantum computing" });
164
+
165
+ assertMultiAgentState(result, {
166
+ "researcher.researchComplete": true,
167
+ "coordinator.phase": "done",
168
+ });
169
+ expect(result.facts.finalOutput).toContain("qubit");
170
+ });
171
+ });
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Evaluation Framework
177
+
178
+ ```typescript
179
+ import { createEvaluation, assertOutputQuality } from "@directive-run/ai/testing";
180
+
181
+ const summaryEval = createEvaluation({
182
+ name: "summarization-quality",
183
+ cases: [
184
+ {
185
+ id: "basic-summary",
186
+ input: { text: "A very long article about climate change..." },
187
+ expected: {
188
+ contains: ["climate", "temperature"],
189
+ maxWords: 50,
190
+ notContains: ["Lorem ipsum"],
191
+ },
192
+ },
193
+ ],
194
+ });
195
+
196
+ describe("summarization evals", () => {
197
+ it("meets quality criteria", async () => {
198
+ for (const evalCase of summaryEval.cases) {
199
+ const result = await orchestrator.run(evalCase.input);
200
+ assertOutputQuality(result.facts.output, evalCase.expected);
201
+ }
202
+ });
203
+ });
204
+ ```
205
+
206
+ ### LLM-as-Judge
207
+
208
+ ```typescript
209
+ import { createLLMJudge } from "@directive-run/ai/testing";
210
+
211
+ const judge = createLLMJudge({
212
+ runner: createAnthropicRunner({ model: "claude-haiku-4-5", apiKey: process.env.ANTHROPIC_API_KEY }),
213
+ criteria: ["Is the response factually accurate?", "Is the response concise?"],
214
+ scoringScale: { min: 1, max: 5 },
215
+ passingScore: 3.5,
216
+ });
217
+
218
+ it("produces quality output", async () => {
219
+ const result = await orchestrator.run({ input: "Explain photosynthesis." });
220
+ const judgment = await judge.evaluate(result.facts.output);
221
+
222
+ expect(judgment.passed).toBe(true);
223
+ });
224
+ ```
225
+
226
+ ---
227
+
228
+ ## Debug Observability
229
+
230
+ ```typescript
231
+ import { createTracingPlugin, createLoggingPlugin, createMetricsPlugin } from "@directive-run/ai/plugins";
232
+ import { createInspector } from "@directive-run/ai/testing";
233
+
234
+ // Tracing – spans around every LLM call and resolver
235
+ const tracer = createTracingPlugin({
236
+ exportTo: "console", // "console" | "opentelemetry" | "custom"
237
+ onSpanEnd: (span) => console.log(`[${span.name}] ${span.duration}ms – ${span.status}`),
238
+ });
239
+
240
+ // Structured logging for LLM calls, guardrail violations, budget warnings
241
+ const logger = createLoggingPlugin({
242
+ level: "info",
243
+ format: "json",
244
+ include: ["runner_calls", "guardrail_violations", "budget_warnings"],
245
+ });
246
+
247
+ // Metrics – auto-collects: ai.tokens.*, ai.latency.runner, ai.guardrail.violations
248
+ const metrics = createMetricsPlugin({
249
+ onMetric: (metric) => myMetricsClient.gauge(metric.name, metric.value, metric.tags),
250
+ });
251
+
252
+ // Inspector – assert on calls and events in tests
253
+ const inspector = createInspector();
254
+
255
+ const orchestrator = createAgentOrchestrator({
256
+ runner: createMockRunner({ responses: [/* ... */] }),
257
+ plugins: [inspector],
258
+ });
259
+
260
+ await orchestrator.run({ input: "test" });
261
+
262
+ const calls = inspector.getRunnerCalls();
263
+ expect(calls).toHaveLength(1);
264
+ expect(calls[0].prompt).toContain("test");
265
+
266
+ const events = inspector.getEvents();
267
+ expect(events.some((e) => e.type === "requirement_met")).toBe(true);
268
+ ```
269
+
270
+ ---
271
+
272
+ ## MCP Integration
273
+
274
+ ```typescript
275
+ import { createMCPToolProvider } from "@directive-run/ai/mcp";
276
+ import { createMockMCPProvider } from "@directive-run/ai/testing";
277
+
278
+ // Production: connect to real MCP tool server
279
+ const mcpProvider = createMCPToolProvider({
280
+ transport: "stdio", // "stdio" | "sse" | "http"
281
+ command: "npx",
282
+ args: ["@my-org/mcp-tools"],
283
+ });
284
+
285
+ // Testing: mock the tool provider
286
+ const mockMCP = createMockMCPProvider({
287
+ tools: {
288
+ web_search: async (_params) => ({
289
+ results: ["Mocked result 1", "Mocked result 2"],
290
+ }),
291
+ },
292
+ });
293
+
294
+ // Tools available in resolvers via context.tools.getAll()
295
+ resolvers: {
296
+ search: {
297
+ requirement: "SEARCH",
298
+ resolve: async (req, context) => {
299
+ const result = await context.runner.run({
300
+ prompt: context.facts.query,
301
+ tools: context.tools.getAll(),
302
+ });
303
+ context.facts.result = result.text;
304
+ },
305
+ },
306
+ },
307
+ ```
308
+
309
+ ---
310
+
311
+ ## RAG Pipeline Integration
312
+
313
+ ```typescript
314
+ import { createRAGProvider } from "@directive-run/ai/rag";
315
+ import { createMockRAGProvider } from "@directive-run/ai/testing";
316
+
317
+ // Production: your retrieval logic
318
+ const ragProvider = createRAGProvider({
319
+ retrieve: async (query, options) => {
320
+ const chunks = await vectorDB.similaritySearch(query, {
321
+ limit: options.topK ?? 5,
322
+ minScore: options.minScore ?? 0.7,
323
+ });
324
+
325
+ return chunks.map((chunk) => ({
326
+ content: chunk.text,
327
+ metadata: chunk.metadata,
328
+ score: chunk.score,
329
+ }));
330
+ },
331
+ });
332
+
333
+ // Testing: mock retrieval results
334
+ const mockRAG = createMockRAGProvider({
335
+ results: [
336
+ { content: "Paris is the capital of France.", score: 0.95, metadata: {} },
337
+ ],
338
+ });
339
+
340
+ // Use in resolver
341
+ resolvers: {
342
+ answer: {
343
+ requirement: "ANSWER",
344
+ resolve: async (req, context) => {
345
+ const sources = await context.rag.retrieve(context.facts.question, { topK: 3 });
346
+ const contextText = sources.map((s) => s.content).join("\n\n");
347
+
348
+ const result = await context.runner.run({
349
+ prompt: `Sources:\n${contextText}\n\nQuestion: ${context.facts.question}`,
350
+ });
351
+
352
+ context.facts.answer = result.text;
353
+ context.facts.sourceCount = sources.length;
354
+ },
355
+ },
356
+ },
357
+ ```
358
+
359
+ ---
360
+
361
+ ## Critical Anti-Patterns
362
+
363
+ ## Using real API keys in CI tests
364
+
365
+ ```typescript
366
+ // WRONG – slow, expensive, flaky, exposes keys in logs
367
+ const orchestrator = createAgentOrchestrator({
368
+ runner: createAnthropicRunner({ apiKey: process.env.ANTHROPIC_API_KEY, model: "claude-opus-4-6" }),
369
+ });
370
+
371
+ // CORRECT – always inject mock runners in tests
372
+ const orchestrator = buildOrchestrator(
373
+ createMockRunner({ responses: [{ text: "ok", usage: { inputTokens: 2, outputTokens: 1, totalTokens: 3 } }] })
374
+ );
375
+ ```
376
+
377
+ ## Testing implementation instead of behavior
378
+
379
+ ```typescript
380
+ // WRONG – brittle, breaks if impl changes
381
+ const spy = vi.spyOn(runner, "run");
382
+ expect(spy).toHaveBeenCalledOnce();
383
+
384
+ // CORRECT – test observable facts
385
+ expect(result.facts.status).toBe("done");
386
+ expect(result.facts.output).toBeTruthy();
387
+ ```
388
+
389
+ ### Resolver parameter naming
390
+ Always use `(req, context)` – never `(req, ctx)` or `(request, context)`.
391
+
392
+ ---
393
+
394
+ ## Reference Files
395
+
396
+ - `ai-testing-evals.md` – Full testing API, createMockRunner options, evaluation suite patterns
397
+ - `ai-debug-observability.md` – Tracing plugins, logging config, metrics collection, inspector API
398
+ - `ai-mcp-rag.md` – MCP transport options, tool provider interface, RAG retrieval config