@danieljvdm/dev-kit 0.6.0 → 0.7.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 (49) hide show
  1. package/README.md +123 -56
  2. package/dev-kit.example.jsonc +7 -3
  3. package/package.json +19 -16
  4. package/schema/dev-kit.schema.json +38 -0
  5. package/skill-sources.jsonc +8 -12
  6. package/skill-sources.lock.json +3 -9
  7. package/skills/dev-kit/SKILL.md +52 -17
  8. package/skills/effect-ts/agents/openai.yaml +0 -1
  9. package/skills/effect-ts/references/audit-services.md +11 -11
  10. package/skills/effect-ts/references/guide-effect.md +56 -69
  11. package/skills/effect-ts/references/guide-error-handling.md +64 -73
  12. package/skills/effect-ts/references/guide-layers.md +187 -215
  13. package/skills/effect-ts/references/guide-observability.md +91 -116
  14. package/skills/effect-ts/references/guide-retries.md +32 -44
  15. package/skills/effect-ts/references/guide-schedule.md +26 -40
  16. package/skills/effect-ts/references/guide-schema.md +50 -57
  17. package/skills/effect-ts/references/guide-sql.md +47 -50
  18. package/skills/effect-ts/references/guide-testing.md +96 -98
  19. package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +7 -7
  20. package/skills/effect-ts/references/version-and-source.md +0 -1
  21. package/src/bin/dev-kit.ts +61 -28
  22. package/src/catalog-manager.ts +86 -34
  23. package/src/catalog.ts +71 -33
  24. package/src/cli-ui.ts +20 -16
  25. package/src/effect-source.ts +49 -19
  26. package/src/effect-tsgo.ts +66 -35
  27. package/src/gitignore.ts +19 -6
  28. package/src/index.ts +6 -0
  29. package/src/manifest.ts +38 -3
  30. package/src/node-symbolic-link.ts +3 -0
  31. package/src/oxlint-plugin-effect.js +3 -0
  32. package/src/oxlint-plugin-style.d.ts +8 -0
  33. package/src/oxlint-plugin-style.js +8 -0
  34. package/src/oxlint.js +14 -0
  35. package/src/oxlint.ts +14 -0
  36. package/src/package-skill-source.ts +189 -52
  37. package/src/path-digest.ts +31 -11
  38. package/src/project-package.ts +44 -19
  39. package/src/project-process-lock.ts +19 -12
  40. package/src/project-state.ts +11 -0
  41. package/src/skill-manager.ts +134 -55
  42. package/src/skill-selector.ts +8 -2
  43. package/src/source-manifest.ts +2 -6
  44. package/src/sync.ts +371 -103
  45. package/src/vendor.ts +112 -42
  46. package/src/vite-plus-hooks.ts +174 -0
  47. package/src/vite-plus-quality.ts +49 -0
  48. package/templates/vite-plus/github-actions-check.yml +44 -0
  49. package/templates/vite-plus/vite.config.ts +22 -0
@@ -45,22 +45,22 @@ Use `Effect.fn` as the default constructor for business-logic functions that ret
45
45
  Prefer this:
46
46
 
47
47
  ```ts
48
- import { Effect } from "effect"
48
+ import { Effect } from "effect";
49
49
 
50
- const loadUser = Effect.fn("loadUser")(function*(userId: string) {
51
- return { id: userId, name: "Ada" }
52
- })
50
+ const loadUser = Effect.fn("loadUser")(function* (userId: string) {
51
+ return { id: userId, name: "Ada" };
52
+ });
53
53
  ```
54
54
 
55
55
  Over this:
56
56
 
57
57
  ```ts
58
- import { Effect } from "effect"
58
+ import { Effect } from "effect";
59
59
 
60
60
  const loadUser = (userId: string) =>
61
- Effect.gen(function*() {
62
- return { id: userId, name: "Ada" }
63
- })
61
+ Effect.gen(function* () {
62
+ return { id: userId, name: "Ada" };
63
+ });
64
64
  ```
65
65
 
66
66
  The second version works, but it throws away useful observability structure that `Effect.fn` gives you automatically.
@@ -72,17 +72,17 @@ For business-logic definitions, prefer `Effect.fn` over writing raw `Effect.gen`
72
72
  Prefer this:
73
73
 
74
74
  ```ts
75
- const refreshCache = Effect.fn("refreshCache")(function*() {
76
- yield* Effect.logInfo("refreshing cache")
77
- })
75
+ const refreshCache = Effect.fn("refreshCache")(function* () {
76
+ yield* Effect.logInfo("refreshing cache");
77
+ });
78
78
  ```
79
79
 
80
80
  Over this:
81
81
 
82
82
  ```ts
83
- const refreshCache = Effect.gen(function*() {
84
- yield* Effect.logInfo("refreshing cache")
85
- })
83
+ const refreshCache = Effect.gen(function* () {
84
+ yield* Effect.logInfo("refreshing cache");
85
+ });
86
86
  ```
87
87
 
88
88
  Why:
@@ -118,11 +118,11 @@ It is the preferred default because it adds:
118
118
  Example:
119
119
 
120
120
  ```ts
121
- import { Effect } from "effect"
121
+ import { Effect } from "effect";
122
122
 
123
- const createUser = Effect.fn("createUser")(function*(name: string) {
124
- return { id: "u_123", name }
125
- })
123
+ const createUser = Effect.fn("createUser")(function* (name: string) {
124
+ return { id: "u_123", name };
125
+ });
126
126
  ```
127
127
 
128
128
  Use this for:
@@ -145,17 +145,17 @@ If you do not want an explicit named span, prefer `Effect.fn` without a span nam
145
145
  Prefer this:
146
146
 
147
147
  ```ts
148
- const normalizeUser = Effect.fn(function*(input: string) {
149
- return input.trim().toLowerCase()
150
- })
148
+ const normalizeUser = Effect.fn(function* (input: string) {
149
+ return input.trim().toLowerCase();
150
+ });
151
151
  ```
152
152
 
153
153
  Over this:
154
154
 
155
155
  ```ts
156
- const normalizeUser = Effect.fnUntraced(function*(input: string) {
157
- return input.trim().toLowerCase()
158
- })
156
+ const normalizeUser = Effect.fnUntraced(function* (input: string) {
157
+ return input.trim().toLowerCase();
158
+ });
159
159
  ```
160
160
 
161
161
  Typical cases:
@@ -177,18 +177,18 @@ Preferred rule:
177
177
  Good:
178
178
 
179
179
  ```ts
180
- const parseCommand = Effect.fn("parseCommand")(function*(input: string) {
181
- return input.trim()
182
- })
183
-
184
- const loadUser = Effect.fn("loadUser")(function*(userId: string) {
185
- return { id: userId, name: "Ada" }
186
- })
187
-
188
- const sendWelcomeEmail = Effect.fn("sendWelcomeEmail")(function*(userId: string) {
189
- const user = yield* loadUser(userId)
190
- return user.email
191
- })
180
+ const parseCommand = Effect.fn("parseCommand")(function* (input: string) {
181
+ return input.trim();
182
+ });
183
+
184
+ const loadUser = Effect.fn("loadUser")(function* (userId: string) {
185
+ return { id: userId, name: "Ada" };
186
+ });
187
+
188
+ const sendWelcomeEmail = Effect.fn("sendWelcomeEmail")(function* (userId: string) {
189
+ const user = yield* loadUser(userId);
190
+ return user.email;
191
+ });
192
192
  ```
193
193
 
194
194
  Why this is good:
@@ -223,17 +223,13 @@ Use `withSpan` when you need an explicit span around an effect that is not alrea
223
223
  Example:
224
224
 
225
225
  ```ts
226
- import { Effect } from "effect"
226
+ import { Effect } from "effect";
227
227
 
228
- const syncUser = Effect.fn("syncUser")(function*(userId: string) {
229
- const profile = yield* fetchProfile(userId).pipe(
230
- Effect.withSpan("fetchProfile")
231
- )
228
+ const syncUser = Effect.fn("syncUser")(function* (userId: string) {
229
+ const profile = yield* fetchProfile(userId).pipe(Effect.withSpan("fetchProfile"));
232
230
 
233
- return yield* persistProfile(profile).pipe(
234
- Effect.withSpan("persistProfile")
235
- )
236
- })
231
+ return yield* persistProfile(profile).pipe(Effect.withSpan("persistProfile"));
232
+ });
237
233
  ```
238
234
 
239
235
  Use this when:
@@ -263,12 +259,12 @@ Use `annotateCurrentSpan` to attach important structured fields to the current s
263
259
  Example:
264
260
 
265
261
  ```ts
266
- import { Effect } from "effect"
262
+ import { Effect } from "effect";
267
263
 
268
- const loadUser = Effect.fn("loadUser")(function*(userId: string) {
269
- yield* Effect.annotateCurrentSpan({ userId })
270
- return { id: userId, name: "Ada" }
271
- })
264
+ const loadUser = Effect.fn("loadUser")(function* (userId: string) {
265
+ yield* Effect.annotateCurrentSpan({ userId });
266
+ return { id: userId, name: "Ada" };
267
+ });
272
268
  ```
273
269
 
274
270
  Good span annotations:
@@ -301,10 +297,10 @@ These integrate with the current Effect execution context.
301
297
  Example:
302
298
 
303
299
  ```ts
304
- const loadUser = Effect.fn("loadUser")(function*(userId: string) {
305
- yield* Effect.logDebug("loading user", { userId })
306
- return { id: userId, name: "Ada" }
307
- })
300
+ const loadUser = Effect.fn("loadUser")(function* (userId: string) {
301
+ yield* Effect.logDebug("loading user", { userId });
302
+ return { id: userId, name: "Ada" };
303
+ });
308
304
  ```
309
305
 
310
306
  ### `Effect.withLogSpan`
@@ -314,9 +310,7 @@ Use `withLogSpan` when you want log messages to carry a local logical span label
314
310
  Example:
315
311
 
316
312
  ```ts
317
- const program = Effect.logInfo("starting sync").pipe(
318
- Effect.withLogSpan("user-sync")
319
- )
313
+ const program = Effect.logInfo("starting sync").pipe(Effect.withLogSpan("user-sync"));
320
314
  ```
321
315
 
322
316
  This is useful for:
@@ -348,19 +342,19 @@ site.
348
342
 
349
343
  ```ts
350
344
  const runCheckout = (request: CheckoutRequest) =>
351
- Effect.gen(function*() {
352
- yield* Effect.logInfo("loading checkout state")
353
- yield* validateCart(request.cart)
354
- yield* Effect.logInfo("submitting payment")
355
- yield* submitPayment(request.payment)
345
+ Effect.gen(function* () {
346
+ yield* Effect.logInfo("loading checkout state");
347
+ yield* validateCart(request.cart);
348
+ yield* Effect.logInfo("submitting payment");
349
+ yield* submitPayment(request.payment);
356
350
  }).pipe(
357
351
  Effect.annotateLogs({
358
352
  operation: "checkout",
359
353
  requestId: request.id,
360
- cartId: request.cart.id
354
+ cartId: request.cart.id,
361
355
  }),
362
- Effect.withLogSpan("checkout")
363
- )
356
+ Effect.withLogSpan("checkout"),
357
+ );
364
358
  ```
365
359
 
366
360
  Add narrower annotations only when metadata belongs to a nested item, attempt,
@@ -388,18 +382,13 @@ Repo reference:
388
382
  Example:
389
383
 
390
384
  ```ts
391
- import { Effect, Metric } from "effect"
385
+ import { Effect, Metric } from "effect";
392
386
 
393
- const requests = Metric.counter("user_load_requests").pipe(
394
- Metric.withConstantInput(1)
395
- )
387
+ const requests = Metric.counter("user_load_requests").pipe(Metric.withConstantInput(1));
396
388
 
397
- const loadUser = Effect.fn("loadUser")(
398
- function*(userId: string) {
399
- return { id: userId, name: "Ada" }
400
- },
401
- Effect.track(requests)
402
- )
389
+ const loadUser = Effect.fn("loadUser")(function* (userId: string) {
390
+ return { id: userId, name: "Ada" };
391
+ }, Effect.track(requests));
403
392
  ```
404
393
 
405
394
  ### Prefer boundary metrics over micro-metrics
@@ -432,14 +421,10 @@ Repo references:
432
421
  Preferred composition style:
433
422
 
434
423
  ```ts
435
- const AppLayer = Layer.mergeAll(
436
- UserLayer,
437
- BillingLayer,
438
- HttpLayer
439
- ).pipe(
424
+ const AppLayer = Layer.mergeAll(UserLayer, BillingLayer, HttpLayer).pipe(
440
425
  Layer.provide(Telemetry),
441
- Layer.provide(NodeSdk)
442
- )
426
+ Layer.provide(NodeSdk),
427
+ );
443
428
  ```
444
429
 
445
430
  Why:
@@ -628,19 +613,14 @@ Example shape:
628
613
  const TelemetryLayer = NodeSdk.layer(() => ({
629
614
  resource: {
630
615
  serviceName: "todo-service",
631
- serviceVersion: "1.0.0"
616
+ serviceVersion: "1.0.0",
632
617
  },
633
618
  spanProcessor: mySpanProcessor,
634
619
  metricReader: myMetricReader,
635
- logRecordProcessor: myLogProcessor
636
- }))
637
-
638
- const AppLayer = Layer.mergeAll(
639
- DomainLayer,
640
- HttpLayer
641
- ).pipe(
642
- Layer.provide(TelemetryLayer)
643
- )
620
+ logRecordProcessor: myLogProcessor,
621
+ }));
622
+
623
+ const AppLayer = Layer.mergeAll(DomainLayer, HttpLayer).pipe(Layer.provide(TelemetryLayer));
644
624
  ```
645
625
 
646
626
  This keeps:
@@ -666,9 +646,9 @@ Bad:
666
646
 
667
647
  ```ts
668
648
  const loadUser = (userId: string) =>
669
- Effect.gen(function*() {
670
- return { id: userId, name: "Ada" }
671
- })
649
+ Effect.gen(function* () {
650
+ return { id: userId, name: "Ada" };
651
+ });
672
652
  ```
673
653
 
674
654
  Why this is bad:
@@ -680,9 +660,9 @@ Why this is bad:
680
660
  Preferred:
681
661
 
682
662
  ```ts
683
- const loadUser = Effect.fn("loadUser")(function*(userId: string) {
684
- return { id: userId, name: "Ada" }
685
- })
663
+ const loadUser = Effect.fn("loadUser")(function* (userId: string) {
664
+ return { id: userId, name: "Ada" };
665
+ });
686
666
  ```
687
667
 
688
668
  ### Anti-Pattern: using `Effect.fnUntraced` by default
@@ -729,36 +709,31 @@ Preferred order:
729
709
  ### Pattern: observable business operation
730
710
 
731
711
  ```ts
732
- import { Effect } from "effect"
712
+ import { Effect } from "effect";
733
713
 
734
- const fetchUser = Effect.fn("fetchUser")(function*(userId: string) {
735
- yield* Effect.annotateCurrentSpan({ userId })
736
- yield* Effect.logDebug("fetching user", { userId })
737
- return { id: userId, name: "Ada" }
738
- })
714
+ const fetchUser = Effect.fn("fetchUser")(function* (userId: string) {
715
+ yield* Effect.annotateCurrentSpan({ userId });
716
+ yield* Effect.logDebug("fetching user", { userId });
717
+ return { id: userId, name: "Ada" };
718
+ });
739
719
  ```
740
720
 
741
721
  ### Pattern: orchestration with nested spans
742
722
 
743
723
  ```ts
744
- const syncUser = Effect.fn("syncUser")(function*(userId: string) {
745
- const profile = yield* fetchRemoteProfile(userId).pipe(
746
- Effect.withSpan("fetchRemoteProfile")
747
- )
748
-
749
- return yield* persistProfile(profile).pipe(
750
- Effect.withSpan("persistProfile")
751
- )
752
- })
724
+ const syncUser = Effect.fn("syncUser")(function* (userId: string) {
725
+ const profile = yield* fetchRemoteProfile(userId).pipe(Effect.withSpan("fetchRemoteProfile"));
726
+
727
+ return yield* persistProfile(profile).pipe(Effect.withSpan("persistProfile"));
728
+ });
753
729
  ```
754
730
 
755
731
  ### Pattern: framework boundary with runtime
756
732
 
757
733
  ```ts
758
- const runtime = ManagedRuntime.make(AppLayer)
734
+ const runtime = ManagedRuntime.make(AppLayer);
759
735
 
760
- const handleRequest = (userId: string) =>
761
- runtime.runPromise(fetchUser(userId))
736
+ const handleRequest = (userId: string) => runtime.runPromise(fetchUser(userId));
762
737
  ```
763
738
 
764
739
  ## Good Repo Examples To Study
@@ -70,9 +70,7 @@ This is explicitly covered in the module tests.
70
70
  Use this for the simplest bounded retry case.
71
71
 
72
72
  ```ts
73
- const retried = effect.pipe(
74
- Effect.retry({ times: 3 })
75
- )
73
+ const retried = effect.pipe(Effect.retry({ times: 3 }));
76
74
  ```
77
75
 
78
76
  Use this when:
@@ -93,9 +91,7 @@ The module tests show:
93
91
  Example:
94
92
 
95
93
  ```ts
96
- const retried = effect.pipe(
97
- Effect.retry({ until: (error) => error._tag === "Done" })
98
- )
94
+ const retried = effect.pipe(Effect.retry({ until: (error) => error._tag === "Done" }));
99
95
  ```
100
96
 
101
97
  ### `{ while: predicate }`
@@ -107,9 +103,7 @@ The tests also show pure and effectful `while` variants.
107
103
  Example:
108
104
 
109
105
  ```ts
110
- const retried = effect.pipe(
111
- Effect.retry({ while: (error) => error._tag === "Retryable" })
112
- )
106
+ const retried = effect.pipe(Effect.retry({ while: (error) => error._tag === "Retryable" }));
113
107
  ```
114
108
 
115
109
  ## Retry With Schedule
@@ -117,9 +111,7 @@ const retried = effect.pipe(
117
111
  Use a `Schedule` whenever timing matters.
118
112
 
119
113
  ```ts
120
- const retried = effect.pipe(
121
- Effect.retry(Schedule.recurs(3))
122
- )
114
+ const retried = effect.pipe(Effect.retry(Schedule.recurs(3)));
123
115
  ```
124
116
 
125
117
  Or with the richer object form:
@@ -128,9 +120,9 @@ Or with the richer object form:
128
120
  const retried = effect.pipe(
129
121
  Effect.retry({
130
122
  schedule: Schedule.recurs(3),
131
- while: (error) => error._tag === "Retryable"
132
- })
133
- )
123
+ while: (error) => error._tag === "Retryable",
124
+ }),
125
+ );
134
126
  ```
135
127
 
136
128
  This is a very important repo pattern because it lets you combine:
@@ -178,13 +170,13 @@ The canonical source repeatedly uses these patterns:
178
170
  ### Fixed retry count
179
171
 
180
172
  ```ts
181
- Schedule.recurs(3)
173
+ Schedule.recurs(3);
182
174
  ```
183
175
 
184
176
  ### Exponential backoff
185
177
 
186
178
  ```ts
187
- Schedule.exponential("500 millis", 1.5)
179
+ Schedule.exponential("500 millis", 1.5);
188
180
  ```
189
181
 
190
182
  ### Exponential plus steady fallback spacing
@@ -193,7 +185,7 @@ Schedule.exponential("500 millis", 1.5)
193
185
  Schedule.exponential("500 millis", 1.5).pipe(
194
186
  Schedule.upTo({ times: 4 }),
195
187
  Schedule.andThen(Schedule.spaced("5 seconds")),
196
- )
188
+ );
197
189
  ```
198
190
 
199
191
  This appears in production modules such as RPC and workflow code.
@@ -203,10 +195,8 @@ This appears in production modules such as RPC and workflow code.
203
195
  ```ts
204
196
  Schedule.forever.pipe(
205
197
  Schedule.setInputType<RetryableError>(),
206
- Schedule.addDelay(({ input: error }) =>
207
- Effect.succeed(error.retryAfter ?? "1 second")
208
- ),
209
- )
198
+ Schedule.addDelay(({ input: error }) => Effect.succeed(error.retryAfter ?? "1 second")),
199
+ );
210
200
  ```
211
201
 
212
202
  The OTLP exporter uses this shape to derive delays from actual HTTP failure details such as rate limits.
@@ -227,11 +217,11 @@ effect.pipe(
227
217
  Effect.retry(policy),
228
218
  Effect.catch((cause) => {
229
219
  if (!Cause.hasInterrupts(cause)) {
230
- return Effect.failCause(cause)
220
+ return Effect.failCause(cause);
231
221
  }
232
- return Effect.die("interrupted and retries exhausted")
233
- })
234
- )
222
+ return Effect.die("interrupted and retries exhausted");
223
+ }),
224
+ );
235
225
  ```
236
226
 
237
227
  Use this when:
@@ -298,17 +288,17 @@ const Plan = ExecutionPlan.make(
298
288
  {
299
289
  provide: FastLayer,
300
290
  attempts: 2,
301
- schedule: Schedule.spaced("3 seconds")
291
+ schedule: Schedule.spaced("3 seconds"),
302
292
  },
303
293
  {
304
294
  provide: SafeLayer,
305
295
  attempts: 3,
306
- schedule: Schedule.spaced("1 second")
296
+ schedule: Schedule.spaced("1 second"),
307
297
  },
308
298
  {
309
- provide: FinalFallbackLayer
310
- }
311
- )
299
+ provide: FinalFallbackLayer,
300
+ },
301
+ );
312
302
  ```
313
303
 
314
304
  ### Step Semantics
@@ -358,7 +348,7 @@ one whose requirements are satisfied from the current context.
358
348
  Use this when the plan should be frozen with the current environment before being applied later.
359
349
 
360
350
  ```ts
361
- const capturedPlan = yield* Plan.captureRequirements
351
+ const capturedPlan = yield * Plan.captureRequirements;
362
352
  ```
363
353
 
364
354
  ## `ExecutionPlan.merge`
@@ -385,9 +375,7 @@ Use `ExecutionPlan` when:
385
375
  ### Pattern: simple bounded retry
386
376
 
387
377
  ```ts
388
- const retried = effect.pipe(
389
- Effect.retry({ times: 3 })
390
- )
378
+ const retried = effect.pipe(Effect.retry({ times: 3 }));
391
379
  ```
392
380
 
393
381
  ### Pattern: retryable-error backoff
@@ -396,14 +384,14 @@ const retried = effect.pipe(
396
384
  const retryPolicy = Schedule.exponential("500 millis", 1.5).pipe(
397
385
  Schedule.upTo({ times: 4 }),
398
386
  Schedule.andThen(Schedule.spaced("5 seconds")),
399
- )
387
+ );
400
388
 
401
389
  const retried = effect.pipe(
402
390
  Effect.retry({
403
391
  schedule: retryPolicy,
404
- while: (error) => error._tag === "Retryable"
405
- })
406
- )
392
+ while: (error) => error._tag === "Retryable",
393
+ }),
394
+ );
407
395
  ```
408
396
 
409
397
  ### Pattern: fallback across providers
@@ -413,17 +401,17 @@ const Plan = ExecutionPlan.make(
413
401
  {
414
402
  provide: PrimaryLayer,
415
403
  attempts: 2,
416
- schedule: Schedule.spaced("1 second")
404
+ schedule: Schedule.spaced("1 second"),
417
405
  },
418
406
  {
419
407
  provide: SecondaryLayer,
420
408
  attempts: 3,
421
- schedule: Schedule.exponential(500, 1.5)
409
+ schedule: Schedule.exponential(500, 1.5),
422
410
  },
423
411
  {
424
- provide: FinalFallbackLayer
425
- }
426
- )
412
+ provide: FinalFallbackLayer,
413
+ },
414
+ );
427
415
  ```
428
416
 
429
417
  ## Anti-Patterns