@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,486 @@
1
+ ---
2
+ name: writing-directive-constraints
3
+ description: "Write Directive constraints, resolvers, and error boundaries: when/require patterns, static vs dynamic requirements, async constraints with explicit deps, priority and ordering, resolver retry policies, deduplication keys, batch resolution, and system error boundaries. Use when asked to add constraint-resolver pairs, handle errors/retries in resolvers, or configure recovery strategies."
4
+ ---
5
+
6
+ # Writing Directive Constraints
7
+
8
+ ## Prerequisites
9
+
10
+ This skill applies when the project uses `@directive-run/core`. If not found in `package.json`, suggest installing it: `npm install @directive-run/core`.
11
+
12
+ ## When Claude Should Use This Skill
13
+
14
+ **Auto-invoke when the user:**
15
+ - Says "add a constraint", "write a resolver", "handle errors in the resolver", or "add retry"
16
+ - Asks "when X happens, do Y" where Y is async work
17
+ - Shows existing constraints/resolvers and asks to extend or fix them
18
+ - Asks about error handling, recovery strategies, circuit breakers, or retry policies
19
+ - Asks about requirement deduplication, batching, or ordering
20
+
21
+ **Do NOT invoke when:**
22
+ - Scaffolding an entire module from scratch (start with `writing-directive-modules.md`)
23
+ - Composing multiple modules (see `building-directive-systems.md`)
24
+ - Writing tests (see `testing-directive-code.md`)
25
+
26
+ ---
27
+
28
+ ## The Core Mental Model
29
+
30
+ ```
31
+ Constraint declares WHEN something is needed → emits a requirement
32
+ Resolver fulfills WHAT to do about it → mutates facts
33
+ ```
34
+
35
+ Never put condition checks in a resolver. Never do async work in a constraint's `when()`.
36
+
37
+ ---
38
+
39
+ ## Decision Tree: Constraint or Something Else?
40
+
41
+ ```
42
+ Is this "when X is true, the system needs Y"?
43
+ ├── Yes, and Y is async/side-effectful → Constraint + Resolver
44
+ ├── Yes, but Y is just a derived value → Use derive instead
45
+ ├── No, it reacts to a change that happened → Use effect
46
+ └── No, it's user-initiated → Use event handler
47
+ ```
48
+
49
+ ---
50
+
51
+ ## Constraint Anatomy
52
+
53
+ ### Static vs dynamic requirements
54
+
55
+ ```typescript
56
+ constraints: {
57
+ // Static – same object every time
58
+ loadConfig: {
59
+ when: (facts) => facts.config === null,
60
+ require: { type: "LOAD_CONFIG" },
61
+ },
62
+
63
+ // Dynamic – function reads facts
64
+ fetchUser: {
65
+ when: (facts) => facts.isAuthenticated && !facts.profile,
66
+ require: (facts) => ({ type: "FETCH_USER", userId: facts.userId }),
67
+ },
68
+
69
+ // Multiple requirements at once
70
+ loadAll: {
71
+ when: (facts) => facts.phase === "init",
72
+ require: [
73
+ { type: "LOAD_CONFIG" },
74
+ { type: "LOAD_USER" },
75
+ ],
76
+ },
77
+
78
+ // Suppress conditionally – return null from require
79
+ conditionalFetch: {
80
+ when: (facts) => facts.needsUpdate,
81
+ require: (facts) => {
82
+ if (!facts.userId) {
83
+ return null;
84
+ }
85
+
86
+ return { type: "FETCH_USER", userId: facts.userId };
87
+ },
88
+ },
89
+ },
90
+ ```
91
+
92
+ ### Priority – conflict resolution
93
+
94
+ ```typescript
95
+ constraints: {
96
+ normalTransition: {
97
+ priority: 50, // Default is 0; higher runs first
98
+ when: (facts) => facts.phase === "red" && facts.elapsed > 30,
99
+ require: { type: "TRANSITION", to: "green" },
100
+ },
101
+
102
+ emergencyOverride: {
103
+ priority: 100, // Evaluated before normalTransition
104
+ when: (facts) => facts.emergencyActive,
105
+ require: { type: "TRANSITION", to: "red" },
106
+ },
107
+ },
108
+ ```
109
+
110
+ ### Ordering with `after` – sequencing constraints
111
+
112
+ ```typescript
113
+ constraints: {
114
+ authenticate: {
115
+ when: (facts) => !facts.token,
116
+ require: { type: "AUTHENTICATE" },
117
+ },
118
+
119
+ // Only evaluates after authenticate's resolver completes
120
+ loadProfile: {
121
+ after: ["authenticate"],
122
+ when: (facts) => facts.token && !facts.profile,
123
+ require: { type: "LOAD_PROFILE" },
124
+ },
125
+ },
126
+ ```
127
+
128
+ `after` blocks evaluation entirely until the named constraint's resolver finishes. If the dependency's `when()` returns false, the blocked constraint proceeds normally.
129
+
130
+ ### Async constraints – MUST declare `deps`
131
+
132
+ Synchronous constraints auto-track deps via proxy. Async constraints cannot (suspended across await), so you must declare `deps` explicitly.
133
+
134
+ ```typescript
135
+ constraints: {
136
+ validateToken: {
137
+ async: true,
138
+ deps: ["token"], // Re-evaluate when token changes
139
+ when: async (facts) => {
140
+ const valid = await validateTokenRemotely(facts.token);
141
+
142
+ return valid;
143
+ },
144
+ require: { type: "REFRESH_TOKEN" },
145
+ timeout: 5000, // Optional: abort if check takes too long
146
+ },
147
+ },
148
+ ```
149
+
150
+ ### Disabling constraints at runtime
151
+
152
+ ```typescript
153
+ system.constraints.disable("fetchWhenReady");
154
+ system.constraints.isDisabled("fetchWhenReady"); // true
155
+ system.constraints.enable("fetchWhenReady");
156
+ ```
157
+
158
+ ---
159
+
160
+ ## Resolver Anatomy
161
+
162
+ ### Basic resolver
163
+
164
+ ```typescript
165
+ resolvers: {
166
+ fetchUser: {
167
+ requirement: "FETCH_USER",
168
+ resolve: async (req, context) => {
169
+ // req – the requirement object emitted by the constraint
170
+ // context.facts – mutable proxy to module facts
171
+ // context.signal – AbortSignal (cancelled on stop or timeout)
172
+ // context.snapshot() – read-only snapshot for before/after
173
+ const user = await fetch(`/api/users/${req.userId}`, {
174
+ signal: context.signal,
175
+ }).then((r) => r.json());
176
+ context.facts.user = user;
177
+ context.facts.phase = "loaded";
178
+ },
179
+ },
180
+ },
181
+ ```
182
+
183
+ ### Custom deduplication key
184
+
185
+ Without `key`, requirements are deduped by structural equality. With `key`, you control dedup:
186
+
187
+ ```typescript
188
+ resolvers: {
189
+ fetchUser: {
190
+ requirement: "FETCH_USER",
191
+ key: (req) => `fetch-user-${req.userId}`, // One inflight per userId
192
+ resolve: async (req, context) => {
193
+ context.facts.user = await fetchUser(req.userId);
194
+ },
195
+ },
196
+ },
197
+ ```
198
+
199
+ ### Retry policy
200
+
201
+ ```typescript
202
+ resolvers: {
203
+ fetchData: {
204
+ requirement: "FETCH_DATA",
205
+ retry: {
206
+ attempts: 3,
207
+ backoff: "exponential", // "none" | "linear" | "exponential"
208
+ initialDelay: 200,
209
+ maxDelay: 5000,
210
+ shouldRetry: (error, attempt) => {
211
+ if (error.message.includes("404")) {
212
+ return false; // Don't retry permanent failures
213
+ }
214
+
215
+ return true;
216
+ },
217
+ },
218
+ resolve: async (req, context) => {
219
+ const res = await fetch("/api/data");
220
+ if (!res.ok) {
221
+ throw new Error(`HTTP ${res.status}`);
222
+ }
223
+ context.facts.data = await res.json();
224
+ },
225
+ },
226
+ },
227
+ ```
228
+
229
+ ### Batch resolution – prevent N+1
230
+
231
+ ```typescript
232
+ resolvers: {
233
+ fetchUsers: {
234
+ requirement: "FETCH_USER",
235
+ batch: {
236
+ enabled: true,
237
+ windowMs: 50, // Collect requirements for 50ms
238
+ maxSize: 20, // Flush immediately at 20 items
239
+ },
240
+ resolveBatch: async (reqs, context) => {
241
+ const ids = reqs.map((req) => req.userId);
242
+ context.facts.users = await fetchUsersBatch(ids);
243
+ },
244
+ },
245
+ },
246
+ ```
247
+
248
+ For per-item success/failure, use `resolveBatchWithResults` and return a results array matching input order.
249
+
250
+ ### Timeout
251
+
252
+ ```typescript
253
+ resolvers: {
254
+ fetchData: {
255
+ requirement: "FETCH_DATA",
256
+ timeout: 10000, // context.signal aborted after 10s
257
+ resolve: async (req, context) => {
258
+ const res = await fetch("/api/data", { signal: context.signal });
259
+ context.facts.data = await res.json();
260
+ },
261
+ },
262
+ },
263
+ ```
264
+
265
+ ---
266
+
267
+ ## Error Boundaries
268
+
269
+ ### System-level error boundary
270
+
271
+ ```typescript
272
+ const system = createSystem({
273
+ module: myModule,
274
+ errorBoundary: {
275
+ onConstraintError: "skip",
276
+ onResolverError: "retry-later",
277
+ onEffectError: "skip",
278
+ onDerivationError: "throw", // Derivation errors are bugs – throw always
279
+ onError: (error) => {
280
+ console.error(`[${error.source}] ${error.sourceId}: ${error.message}`);
281
+ },
282
+ retryLater: {
283
+ delayMs: 1000,
284
+ maxRetries: 3,
285
+ backoffMultiplier: 2,
286
+ maxDelayMs: 30000,
287
+ },
288
+ },
289
+ });
290
+ ```
291
+
292
+ Recovery strategies:
293
+
294
+ | Strategy | Behavior |
295
+ |----------|----------|
296
+ | `"skip"` | Swallow the error, continue |
297
+ | `"retry"` | Retry immediately |
298
+ | `"retry-later"` | Retry after exponential backoff delay |
299
+ | `"disable"` | Disable the failing constraint/effect permanently |
300
+ | `"throw"` | Re-throw, halting the system |
301
+
302
+ ### Dynamic recovery with functions
303
+
304
+ ```typescript
305
+ errorBoundary: {
306
+ onResolverError: (error, resolverId) => {
307
+ if (error.message.includes("NetworkError")) {
308
+ return "retry-later";
309
+ }
310
+
311
+ if (error.message.includes("401")) {
312
+ return "skip";
313
+ }
314
+
315
+ return "throw";
316
+ },
317
+ },
318
+ ```
319
+
320
+ ### DirectiveError properties
321
+
322
+ ```typescript
323
+ import { DirectiveError } from "@directive-run/core";
324
+
325
+ try {
326
+ await system.settle();
327
+ } catch (err) {
328
+ if (err instanceof DirectiveError) {
329
+ err.source; // "constraint" | "resolver" | "effect" | "derivation" | "system"
330
+ err.sourceId; // e.g., "fetchUser"
331
+ err.recoverable; // boolean
332
+ err.context; // debug data (e.g., the requirement object)
333
+ }
334
+ }
335
+ ```
336
+
337
+ ### Module-level lifecycle hooks
338
+
339
+ ```typescript
340
+ hooks: {
341
+ onInit: (system) => { ... },
342
+ onStart: (system) => { ... },
343
+ onStop: (system) => { ... },
344
+ onError: (error, hookContext) => { ... },
345
+ },
346
+ ```
347
+
348
+ ### Circuit breaker
349
+
350
+ ```typescript
351
+ import { createCircuitBreaker } from "@directive-run/core/plugins";
352
+
353
+ const apiBreaker = createCircuitBreaker({
354
+ name: "external-api",
355
+ failureThreshold: 5,
356
+ recoveryTimeMs: 30000,
357
+ halfOpenMaxRequests: 3,
358
+ isFailure: (error) => !error.message.includes("404"),
359
+ onStateChange: (from, to) => console.log(`Circuit: ${from} -> ${to}`),
360
+ });
361
+
362
+ // Use in resolvers
363
+ resolvers: {
364
+ fetchData: {
365
+ requirement: "FETCH_DATA",
366
+ resolve: async (req, context) => {
367
+ const data = await apiBreaker.execute(async () => {
368
+ return fetch("/api/data").then((r) => r.json());
369
+ });
370
+ context.facts.data = data;
371
+ },
372
+ },
373
+ },
374
+
375
+ // Wire into constraints for automatic fallback
376
+ constraints: {
377
+ apiDown: {
378
+ when: () => apiBreaker.getState() === "OPEN",
379
+ require: { type: "USE_FALLBACK" },
380
+ },
381
+ fetchNormally: {
382
+ when: (facts) => apiBreaker.getState() !== "OPEN" && !facts.data,
383
+ require: { type: "FETCH_DATA" },
384
+ },
385
+ },
386
+ ```
387
+
388
+ ---
389
+
390
+ ## Critical Anti-Patterns
391
+
392
+ ### 1. Async logic in constraint when()
393
+
394
+ ```typescript
395
+ // WRONG – when() must be synchronous (unless async: true + deps)
396
+ when: async (facts) => {
397
+ return await validate(facts.token);
398
+ }
399
+
400
+ // CORRECT – either use async: true with deps, or put validation in resolver
401
+ when: (facts) => Boolean(facts.token),
402
+ ```
403
+
404
+ ### 2. Condition checking inside a resolver
405
+
406
+ ```typescript
407
+ // WRONG – resolver should not check conditions (that's the constraint's job)
408
+ resolve: async (req, context) => {
409
+ if (!context.facts.isAuthenticated) {
410
+ return;
411
+ }
412
+ // ...
413
+ }
414
+
415
+ // CORRECT – let the constraint's when() gate this
416
+ ```
417
+
418
+ ### 3. Async constraint without deps
419
+
420
+ ```typescript
421
+ // WRONG – engine cannot track dependencies
422
+ { async: true, when: async (facts) => await validate(facts.token) }
423
+
424
+ // CORRECT
425
+ { async: true, deps: ["token"], when: async (facts) => await validate(facts.token) }
426
+ ```
427
+
428
+ ### 4. String literal for require
429
+
430
+ ```typescript
431
+ // WRONG
432
+ require: "FETCH_DATA"
433
+
434
+ // CORRECT
435
+ require: { type: "FETCH_DATA" }
436
+ ```
437
+
438
+ ### 5. Returning data from a resolver
439
+
440
+ ```typescript
441
+ // WRONG – return value ignored
442
+ resolve: async (req, context) => { return await fetchUser(req.userId); }
443
+
444
+ // CORRECT
445
+ resolve: async (req, context) => { context.facts.user = await fetchUser(req.userId); }
446
+ ```
447
+
448
+ ### 6. Resolver parameter naming
449
+ Always use `(req, context)` – never `(req, ctx)` or `(request, context)`.
450
+
451
+ ### 7. No error handling on network resolvers
452
+
453
+ ```typescript
454
+ // WRONG
455
+ resolve: async (req, context) => {
456
+ context.facts.data = await fetch("/api").then((r) => r.json());
457
+ }
458
+
459
+ // CORRECT – add retry and check response status
460
+ resolve: async (req, context) => {
461
+ const res = await fetch("/api");
462
+ if (!res.ok) {
463
+ throw new Error(`HTTP ${res.status}`);
464
+ }
465
+ context.facts.data = await res.json();
466
+ },
467
+ ```
468
+
469
+ ---
470
+
471
+ ## Constraint vs Effect vs Derivation
472
+
473
+ | Feature | Purpose | When to use |
474
+ |---------|---------|-------------|
475
+ | Constraint | Declare a need (emit requirement) | "When X, the system needs Y" |
476
+ | Resolver | Fulfill a need (async work) | "Do Y" |
477
+ | Effect | React to changes (fire-and-forget) | "Whenever X changes, log it" |
478
+ | Derivation | Compute a value (sync, cached) | "X is always facts.a + facts.b" |
479
+
480
+ ---
481
+
482
+ ## Reference Files
483
+
484
+ - `constraints.md` – full constraint API, async constraints, disabling, common mistakes
485
+ - `resolvers.md` – full resolver API, context object, batch resolution, inspecting resolver status
486
+ - `error-boundaries.md` – all recovery strategies, DirectiveError API, circuit breaker patterns
@@ -0,0 +1,5 @@
1
+ # API Skeleton
2
+
3
+ > Auto-generated. Do not edit.
4
+
5
+ > Source JSON not found. Build website docs first.