@danieljvdm/dev-kit 0.7.2 → 0.8.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.
package/README.md CHANGED
@@ -193,8 +193,9 @@ tool versions. A project-local process lock also prevents concurrent applies.
193
193
  ```
194
194
 
195
195
  - `dev-kit` installs guidance for operating the toolkit itself.
196
- - `effect` expands to `effect-ts` plus focused Effect Atom HTTP data-fetching
197
- guidance.
196
+ - `effect` expands to the `effect-ts` umbrella, whose lazy references include
197
+ Effect Atom HTTP data fetching and Effect DateTime guidance. The former
198
+ `effect-atom-data-fetching` selector remains an alias for compatibility.
198
199
  - Prefer individual external skills such as `workers-best-practices` and
199
200
  `wrangler`, selected after scanning the project for relevant technologies.
200
201
  - `serve-sim` selects the approved Evan Bacon simulator skill directly.
@@ -259,8 +260,8 @@ worktree converges its own copy during install while the project-owned
259
260
 
260
261
  ## Vite+ quality setup
261
262
 
262
- Supported Vite+/Effect repositories can explicitly opt into Dev Kit's canonical
263
- quality configuration and GitHub Actions workflow:
263
+ Vite+/Effect repositories opt into the canonical Vite config and GitHub Actions
264
+ workflow independently:
264
265
 
265
266
  ```jsonc
266
267
  {
@@ -269,27 +270,27 @@ quality configuration and GitHub Actions workflow:
269
270
  "effectTsgo": { "enabled": true },
270
271
  "vitePlus": {
271
272
  "hooks": { "enabled": true },
272
- "quality": { "enabled": true },
273
+ "quality": {
274
+ "config": { "enabled": true },
275
+ "workflow": { "enabled": true },
276
+ },
273
277
  },
274
278
  },
275
279
  }
276
280
  ```
277
281
 
278
- The quality task requires direct `@danieljvdm/dev-kit`, `vite-plus`, `effect`,
279
- `@effect/tsgo`, and native TypeScript dependencies, plus the enabled Effect
280
- TypeScript-Go patch. The installed Vite+ version must satisfy Dev Kit's
281
- `vite-plus` peer range; Dev Kit tests one exact development version inside that
282
- range, while CI lets `setup-vp` resolve the consumer's version from its own
283
- manifest and lockfile. It manages `vite.config.ts` and
284
- `.github/workflows/check.yml` as digest-owned files. Existing custom files or
285
- conflicting `check`/`typecheck` package scripts are rejected instead of merged
286
- or overwritten. Exact canonical files can be adopted; disabling the task
287
- removes only unchanged owned files.
282
+ `quality.config.enabled` owns `vite.config.ts`; `quality.workflow.enabled` owns
283
+ `.github/workflows/check.yml`. Selecting one does not impose the other resource's
284
+ config, scripts, or TypeScript topology. Both require direct
285
+ `@danieljvdm/dev-kit`, `vite-plus`, `effect`, `@effect/tsgo`, and native
286
+ TypeScript dependencies with `setup.effectTsgo.enabled`. The installed Vite+
287
+ must satisfy Dev Kit's peer range.
288
288
 
289
289
  The managed Vite config composes the shared Oxfmt and Oxlint presets, configures
290
290
  `vp staged`, and defines separate `vp run check` and pure `vp run typecheck`
291
- tasks. The default `single-project` strategy runs one `tsc --noEmit` process and
292
- requires a root `tsconfig.json` without project references:
291
+ tasks. It rejects conflicting root scripts. The default `single-project`
292
+ strategy requires a root `tsconfig.json` without project references. Workspaces
293
+ can instead select Vite Task orchestration explicitly:
293
294
 
294
295
  ```jsonc
295
296
  {
@@ -297,15 +298,28 @@ requires a root `tsconfig.json` without project references:
297
298
  "effectTsgo": { "enabled": true },
298
299
  "vitePlus": {
299
300
  "quality": {
300
- "enabled": true,
301
- "typecheck": { "strategy": "single-project" },
301
+ "config": {
302
+ "enabled": true,
303
+ "typecheck": {
304
+ "strategy": "workspace",
305
+ "concurrency": 4,
306
+ "packages": ["apps/web", "packages/core"],
307
+ },
308
+ },
302
309
  },
303
310
  },
304
311
  },
305
312
  }
306
313
  ```
307
314
 
308
- Workspaces can instead opt into Vite Task orchestration explicitly:
315
+ Workspace mode requires `package.json#workspaces` and an explicit list of
316
+ package directories with pure `typecheck` scripts. The generated task uses
317
+ dependency order, caching, `--fail-if-no-match`, and bounded concurrency.
318
+ Project-reference builds remain custom because their `.tsbuildinfo` inputs and
319
+ outputs are repository-specific.
320
+
321
+ Repositories with a custom Vite config can still adopt only the hardened CI
322
+ workflow and declare repository-specific preparation and typecheck commands:
309
323
 
310
324
  ```jsonc
311
325
  {
@@ -313,11 +327,18 @@ Workspaces can instead opt into Vite Task orchestration explicitly:
313
327
  "effectTsgo": { "enabled": true },
314
328
  "vitePlus": {
315
329
  "quality": {
316
- "enabled": true,
317
- "typecheck": {
318
- "strategy": "workspace",
319
- "concurrency": 4,
320
- "packages": ["apps/web", "packages/core"],
330
+ "workflow": {
331
+ "enabled": true,
332
+ "beforeChecks": [
333
+ {
334
+ "name": "Install media tools",
335
+ "run": ["sudo apt-get update", "sudo apt-get install --yes ffmpeg"],
336
+ },
337
+ ],
338
+ "typecheck": [
339
+ "vp run -F './apps/*' -F './packages/*' check",
340
+ "vp exec tsc --noEmit -p scripts/tsconfig.json",
341
+ ],
321
342
  },
322
343
  },
323
344
  },
@@ -325,31 +346,19 @@ Workspaces can instead opt into Vite Task orchestration explicitly:
325
346
  }
326
347
  ```
327
348
 
328
- Workspace mode requires `package.json#workspaces` and an explicit list of
329
- project-relative package directories. Each listed package must define a pure
330
- `typecheck` package script. Dev Kit generates Vite+ filters for exactly that
331
- scope with `--fail-if-no-match`; Vite Task uses ordinary workspace dependency
332
- order, forces script caching on, and bounds concurrency to the configured value
333
- (1–32). Dev Kit does not generate a project-reference mode: repositories that
334
- need `tsc -b` must keep the quality setup disabled and define a custom Vite task
335
- with deliberate `.tsbuildinfo` inputs and outputs.
336
-
337
- Generated CI performs exactly one `vp install` through
338
- `voidzero-dev/setup-vp`, using `--frozen-lockfile --ignore-scripts`, then runs
339
- `vp exec dev-kit apply --locked` before quality checks. This verifies the
340
- committed Dev Kit lock and explicitly converges the Effect TypeScript-Go patch
341
- without relying on a consumer lifecycle script or running an unlocked apply.
342
- Vite+ maps those install flags to the detected npm, pnpm, Yarn, or Bun project;
343
- the workflow does not pretend their native immutable/no-script flags are
344
- interchangeable.
345
- The template pins the exact commit for a supported `setup-vp` release because
346
- its `v1` tag is frozen; keep the annotated release commit current with Renovate
347
- or Dependabot. See the primary
349
+ Workflow-only setup does not validate or take ownership of the Vite config,
350
+ scripts, or TypeScript topology. It performs one frozen, script-suppressed
351
+ install, runs `dev-kit apply --locked`, and only then runs custom preparation,
352
+ formatting, linting, tests, and typechecking. Its default typecheck command is
353
+ `vp run typecheck`; `workflow.typecheck` replaces it. Vite+ maps install flags
354
+ to the detected package manager. The template pins an exact `setup-vp` release
355
+ commit because its `v1` tag is frozen; keep it current with Renovate or
356
+ Dependabot. Existing workflows remain user-owned until their rendered content
357
+ matches exactly—Dev Kit never merges YAML. See the primary
348
358
  [`setup-vp` versioning guidance](https://github.com/voidzero-dev/setup-vp#versioning),
349
359
  [Vite+ install guide](https://viteplus.dev/guide/install), and
350
- [Vite Task run guide](https://viteplus.dev/guide/run) when maintaining these
351
- templates. The check task and CI then run `vp fmt --check`, `vp lint`,
352
- `vp test`, and `vp run typecheck`.
360
+ [Vite Task run guide](https://viteplus.dev/guide/run) when maintaining the
361
+ templates.
353
362
 
354
363
  ## Effect source checkout
355
364
 
@@ -413,8 +422,7 @@ native TypeScript compiler. It does not download dependencies and skips an
413
422
  installation that is already patched. Use `dev-kit tsgo patch --dry-run` when
414
423
  troubleshooting the task directly.
415
424
 
416
- Dependency and `tsconfig.json` edits remain explicit. Enable the Vite+ quality
417
- task only when Dev Kit can own the canonical root config and GitHub workflow.
425
+ Dependency and `tsconfig.json` edits remain explicit.
418
426
 
419
427
  ## Installed package skills
420
428
 
@@ -547,9 +555,8 @@ Use `lint.extends` rather than a shallow object spread so Vite+ composes the
547
555
  nested plugin and rule configuration correctly. Oxfmt has no `extends`, so
548
556
  spread its configuration before project-local formatter options. The shared
549
557
  lint preset enables `typeAware` for semantic lint rules but leaves `typeCheck`
550
- disabled. The opt-in `setup.vitePlus.quality` task manages this composition for
551
- repositories that use the canonical config; custom configs keep the task
552
- disabled and compose the exports manually. Effect TypeScript-Go projects run:
558
+ disabled. Managed quality config uses this composition; custom Vite configs
559
+ compose the exports manually. Effect TypeScript-Go projects run:
553
560
 
554
561
  ```sh
555
562
  vp fmt --check
@@ -9,8 +9,11 @@
9
9
  "vitePlus": {
10
10
  "hooks": { "enabled": true },
11
11
  "quality": {
12
- "enabled": true,
13
- "typecheck": { "strategy": "single-project" },
12
+ "config": {
13
+ "enabled": true,
14
+ "typecheck": { "strategy": "single-project" },
15
+ },
16
+ "workflow": { "enabled": true },
14
17
  },
15
18
  },
16
19
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danieljvdm/dev-kit",
3
- "version": "0.7.2",
3
+ "version": "0.8.0",
4
4
  "private": false,
5
5
  "description": "Declarative project development toolkit with portable agent skills.",
6
6
  "license": "MIT",
@@ -161,7 +161,20 @@
161
161
  }
162
162
  },
163
163
  "vitePlusQualitySetup": {
164
- "description": "Manage the canonical Vite+ quality config and GitHub Actions workflow for supported Effect TypeScript-Go repositories.",
164
+ "description": "Opt into independently managed Vite+ quality resources for supported Effect TypeScript-Go repositories.",
165
+ "type": "object",
166
+ "additionalProperties": false,
167
+ "properties": {
168
+ "config": {
169
+ "$ref": "#/$defs/vitePlusQualityConfigSetup"
170
+ },
171
+ "workflow": {
172
+ "$ref": "#/$defs/vitePlusQualityWorkflowSetup"
173
+ }
174
+ }
175
+ },
176
+ "vitePlusQualityConfigSetup": {
177
+ "description": "Manage the canonical root Vite config without requiring Dev Kit to own CI.",
165
178
  "type": "object",
166
179
  "additionalProperties": false,
167
180
  "properties": {
@@ -174,6 +187,54 @@
174
187
  }
175
188
  }
176
189
  },
190
+ "vitePlusQualityWorkflowSetup": {
191
+ "description": "Manage the canonical GitHub Actions check workflow without requiring Dev Kit to own the Vite config.",
192
+ "type": "object",
193
+ "additionalProperties": false,
194
+ "properties": {
195
+ "enabled": {
196
+ "type": "boolean",
197
+ "default": false
198
+ },
199
+ "beforeChecks": {
200
+ "description": "Repository-specific preparation steps inserted after locked setup verification and before quality checks.",
201
+ "type": "array",
202
+ "items": {
203
+ "$ref": "#/$defs/vitePlusQualityWorkflowStep"
204
+ },
205
+ "default": []
206
+ },
207
+ "typecheck": {
208
+ "description": "Shell commands for the workflow's pure typecheck step.",
209
+ "type": "array",
210
+ "minItems": 1,
211
+ "items": {
212
+ "type": "string",
213
+ "minLength": 1
214
+ },
215
+ "default": ["vp run typecheck"]
216
+ }
217
+ }
218
+ },
219
+ "vitePlusQualityWorkflowStep": {
220
+ "type": "object",
221
+ "additionalProperties": false,
222
+ "required": ["name", "run"],
223
+ "properties": {
224
+ "name": {
225
+ "type": "string",
226
+ "minLength": 1
227
+ },
228
+ "run": {
229
+ "type": "array",
230
+ "minItems": 1,
231
+ "items": {
232
+ "type": "string",
233
+ "minLength": 1
234
+ }
235
+ }
236
+ }
237
+ },
177
238
  "vitePlusQualityTypecheckSetup": {
178
239
  "description": "Select the managed typecheck topology. Single-project is the default; workspace mode runs explicitly scoped package scripts through Vite Task.",
179
240
  "type": "object",
@@ -76,7 +76,10 @@ skill as `dev-kit` when project agents should carry the toolkit procedure.
76
76
  "claudeInstructions": { "enabled": true },
77
77
  "vitePlus": {
78
78
  "hooks": { "enabled": true },
79
- "quality": { "enabled": true },
79
+ "quality": {
80
+ "config": { "enabled": true },
81
+ "workflow": { "enabled": true },
82
+ },
80
83
  },
81
84
  },
82
85
  "targets": {
@@ -109,34 +112,26 @@ ignored dispatchers in linked worktrees. Preserve other hook managers; Dev Kit
109
112
  refuses to replace an unrelated `core.hooksPath`. Use `VITE_GIT_HOOKS=0` or
110
113
  `HUSKY=0` to skip hook setup for an invocation.
111
114
 
112
- Enable `setup.vitePlus.quality` only in supported Vite+/Effect repositories
113
- that explicitly want Dev Kit to own the canonical root `vite.config.ts` and
114
- `.github/workflows/check.yml`. It requires direct Dev Kit, Vite+, Effect,
115
- Effect TypeScript-Go, and native TypeScript dependencies, as well as
116
- `setup.effectTsgo.enabled`. It refuses custom destination files and conflicting
117
- `check` or `typecheck` package scripts. Exact canonical files can be adopted;
118
- later updates and cleanup occur only while the owned files remain unchanged.
119
- The installed Vite+ version must satisfy Dev Kit's package peer range; generated
120
- CI resolves the consumer's locked Vite+ rather than carrying a second version
121
- literal.
122
-
123
- The managed Vite config composes the shared formatter and linter presets,
124
- configures `vp staged`, and defines separate cached `check` and pure `typecheck`
125
- Vite tasks. The default `single-project` typecheck strategy runs
126
- `tsc --noEmit` and rejects root TypeScript project references. An explicit
127
- `workspace` strategy requires explicit package directories with consumer-owned
128
- `typecheck` scripts, then runs exactly that filter scope in workspace dependency
129
- order with caching enabled, unmatched-filter failure, and configurable bounded
130
- concurrency. Project-reference builds remain custom because they need
131
- repository-specific `.tsbuildinfo` inputs and outputs.
132
-
133
- The GitHub Actions workflow uses one frozen, script-suppressed install through
134
- an exact `setup-vp` release, then runs `vp exec dev-kit apply --locked`. This
135
- verifies the committed setup and converges the Effect TypeScript-Go patch before
136
- formatting, linting, tests, and typechecking. Vite+ maps the frozen and
137
- ignore-scripts flags to the detected package manager. Keep the exact setup
138
- action release commit current with Renovate or Dependabot; do not switch back
139
- to the frozen `v1` tag.
115
+ Enable `setup.vitePlus.quality.config` and
116
+ `setup.vitePlus.quality.workflow` independently in supported Vite+/Effect
117
+ repositories. Config owns canonical root `vite.config.ts`; workflow owns
118
+ `.github/workflows/check.yml`; selecting one must not constrain the other's
119
+ config, scripts, or TypeScript topology. Both require direct Dev Kit, compatible
120
+ Vite+, Effect, Effect TypeScript-Go, and native TypeScript dependencies with
121
+ `setup.effectTsgo.enabled`. Preserve unowned destinations and adopt only exact
122
+ rendered matches.
123
+
124
+ Managed config defines separate `check` and pure `typecheck` Vite tasks. Its
125
+ default single-project strategy rejects project references; workspace mode
126
+ requires explicit package directories with `typecheck` scripts and generates
127
+ cached, dependency-ordered, bounded-concurrency filters. Keep project-reference
128
+ builds custom. Workflow-only consumers may configure `workflow.beforeChecks`
129
+ and `workflow.typecheck`; treat these commands as trusted manifest input.
130
+
131
+ The workflow must use one frozen, script-suppressed install, then locked Dev Kit
132
+ convergence before preparation or checks. Keep `setup-vp` pinned to a reviewed
133
+ release commit—the `v1` tag is frozen—and let Vite+ resolve the consumer's
134
+ compatible locked version.
140
135
 
141
136
  ## Ownership and conflicts
142
137
 
@@ -270,8 +265,8 @@ in the consuming project.
270
265
 
271
266
  Manage skill outputs, the `setup.agentInstructions` wrapper, the
272
267
  `setup.claudeInstructions` link, the `setup.vitePlus.hooks` dispatcher, the
273
- opt-in `setup.vitePlus.quality` config and GitHub workflow, the
268
+ independently opt-in `setup.vitePlus.quality` config and GitHub workflow, the
274
269
  `setup.effectSource` checkout, and the explicit `setup.effectTsgo` task.
275
270
  Dependency and `tsconfig.json` contributions remain deliberate user-owned
276
- edits. Custom Vite configs compose the Oxlint and Oxfmt package exports manually
277
- and leave the canonical quality task disabled.
271
+ edits. Custom Vite configs compose the Oxlint and Oxfmt package exports
272
+ manually, leave managed config disabled, and may still use the managed workflow.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: effect-ts
3
- description: Use this skill whenever working in a repository that uses Effect, even if the current task is in a new file or the user does not explicitly ask for Effect help. Apply it to Effect patterns, services, layers, schemas, streams, runtimes, typed errors, observability, testing, HTTP, SQL, command-line scripts, project automation, and supporting tooling.
3
+ description: Use this skill whenever working in a repository that uses Effect, even if the current task is in a new file or the user does not explicitly ask for Effect help. Apply it to Effect patterns, services, layers, schemas, streams, runtimes, typed errors, DateTime, Effect Atom, observability, testing, HTTP, SQL, command-line scripts, project automation, and supporting tooling.
4
4
  ---
5
5
 
6
6
  # Effect Expert
@@ -106,6 +106,19 @@ When the task touches one of these areas, consult the matching guide before impl
106
106
  - `./references/guide-schedule.md` for retries, repeats, backoff, polling, cron, and schedule composition
107
107
  - `./references/guide-schema.md` for schema-first application modeling,
108
108
  service contracts, transformations, unions, recursion, and branded types
109
+ - `./references/guide-datetime.md` for current time, parsing, UTC and zoned
110
+ values, time zones, DST-safe arithmetic, formatting, Date interoperability,
111
+ and deterministic `TestClock` tests
112
+ - `./references/guide-atom-data-fetching.md` for the core Effect Atom HTTP
113
+ data-fetching workflow and ownership rules
114
+ - `./references/atom-cache-lifecycle.md` for Effect Atom registry scope,
115
+ runtime memoization, families, TTL, SWR, polling, and aggregation resets
116
+ - `./references/atom-http-and-invalidation.md` for `AtomHttpApi.Service`,
117
+ queries, mutations, reactivity keys, and invalidation
118
+ - `./references/atom-tanstack-start.md` only for TanStack Start provider
119
+ placement, SSR isolation, hydration, and focus guidance
120
+ - `./references/atom-testing.md` when adding or diagnosing deterministic Effect
121
+ Atom lifecycle tests
109
122
  - `./references/guide-sql.md` for Effect SQL usage, transactions, resolvers, schema-aware SQL, and migrations
110
123
  - `./references/guide-testing.md` for `@effect/vitest`, deterministic testing,
111
124
  honest test Layers, property tests, and protocol round trips
@@ -208,6 +221,15 @@ Install additional `@effect/*` packages only when the user task actually needs t
208
221
  - If a type is hard to express, simplify the design or introduce a properly typed helper instead of using unsafe TypeScript.
209
222
  - For layers, do not hide them inside `namespace` blocks. Prefer either `static` members on the service class or plain exported layer constants.
210
223
 
224
+ ### Date and Time
225
+
226
+ - Prefer Effect `DateTime` over vanilla JavaScript `Date` for application
227
+ logic. Keep `Date` as an interoperability type at external boundaries.
228
+ - Use `DateTime.now` inside Effect programs so current time remains driven by
229
+ the `Clock` service and deterministic under `TestClock`.
230
+ - Preserve the distinction between instants, zoned wall-clock values, and
231
+ date-only domain values.
232
+
211
233
  ### Code Quality
212
234
 
213
235
  - Write type-safe code that leverages Effect's type system.
@@ -233,6 +255,12 @@ rely on installed declarations and version-matched canonical Effect source.
233
255
  - `./references/guide-retries.md`
234
256
  - `./references/guide-schedule.md`
235
257
  - `./references/guide-schema.md`
258
+ - `./references/guide-datetime.md`
259
+ - `./references/guide-atom-data-fetching.md`
260
+ - `./references/atom-cache-lifecycle.md`
261
+ - `./references/atom-http-and-invalidation.md`
262
+ - `./references/atom-tanstack-start.md`
263
+ - `./references/atom-testing.md`
236
264
  - `./references/guide-sql.md`
237
265
  - `./references/guide-testing.md`
238
266
  - `./references/guide-cli.md`
@@ -1,4 +1,4 @@
1
1
  interface:
2
2
  display_name: "Effect Expert"
3
3
  short_description: "Apply current Effect v4 patterns and tooling"
4
- default_prompt: "Use $effect-ts to apply the relevant Effect architecture, schema, service, error, testing, HTTP, SQL, observability, or CLI guidance."
4
+ default_prompt: "Use $effect-ts to apply the relevant Effect architecture, DateTime, Atom, schema, service, error, testing, HTTP, SQL, observability, or CLI guidance."
@@ -1,4 +1,4 @@
1
- # Cache lifecycle
1
+ # Effect Atom cache lifecycle
2
2
 
3
3
  ## Registry and runtime scope
4
4
 
@@ -1,4 +1,4 @@
1
- # HTTP queries and invalidation
1
+ # Effect Atom HTTP queries and invalidation
2
2
 
3
3
  ## Build one API service
4
4
 
@@ -1,4 +1,4 @@
1
- # TanStack Start integration
1
+ # Effect Atom TanStack Start integration
2
2
 
3
3
  TanStack Start code is isomorphic by default, including route loaders. Treat every module used by a route as server-capable unless an explicit boundary says otherwise.
4
4
 
@@ -1,4 +1,4 @@
1
- # Deterministic lifecycle testing
1
+ # Deterministic Effect Atom lifecycle testing
2
2
 
3
3
  Test cache policy below React first with `AtomRegistry.make()`. Add a React integration test only for provider placement, hook behavior, a browser-only SSR boundary, or hydration.
4
4
 
@@ -1,8 +1,3 @@
1
- ---
2
- name: effect-atom-data-fetching
3
- description: Design, implement, debug, or review HTTP data fetching with Effect Atom in React applications. Use for AtomHttpApi.Service queries and mutations, parameterized Atom.family caches, RegistryProvider and runtime placement, TTL/SWR/polling behavior, reactivity-key invalidation, framework-appropriate SSR and focus handling, AsyncResult aggregation resets, and deterministic cache lifecycle tests.
4
- ---
5
-
6
1
  # Effect Atom Data Fetching
7
2
 
8
3
  Model server data as stable atoms owned outside React renders. Give the application one intentional registry boundary and one compatible runtime factory; then choose cache retention, freshness, polling, and invalidation independently.
@@ -28,13 +23,6 @@ Model server data as stable atoms owned outside React renders. Give the applicat
28
23
  - Ensure every input to a route-level `AsyncResult.all` has stable atom identity and compatible retention. One newly allocated or immediately evicted input returns to `Initial` and makes the whole aggregate appear reset even when the other inputs remain cached.
29
24
  - Do not describe manual refresh or polling as freshness caching. Refresh is forceful; `staleTime` only gates SWR's automatic mount/focus decisions.
30
25
 
31
- ## References
32
-
33
- - Read `./references/cache-lifecycle.md` for registry scope, runtime memoization, families, TTL, SWR, polling, and the aggregation trap.
34
- - Read `./references/http-and-invalidation.md` for `AtomHttpApi.Service`, query/mutation ownership, reactivity-key vocabulary, and invalidation.
35
- - Read `./references/tanstack-start.md` only for its framework-specific provider placement, SSR isolation, hydration, and focus guidance.
36
- - Read `./references/testing.md` when adding or diagnosing lifecycle tests.
37
-
38
26
  ## Completion check
39
27
 
40
28
  Confirm one registry boundary, stable atom identity, deliberate TTL/staleness/polling values, matching query and mutation keys, SSR-safe browser access, and tests for every lifecycle behavior changed.
@@ -0,0 +1,72 @@
1
+ # Effect DateTime
2
+
3
+ Prefer Effect `DateTime` over vanilla JavaScript `Date` for application logic. Keep `Date` as an interoperability type at external boundaries, not the domain model.
4
+
5
+ ## Workflow
6
+
7
+ 1. Inspect the installed `effect` version and `DateTime` source before relying on exact signatures. DateTime APIs are version-sensitive.
8
+ 2. Identify whether each value represents an absolute instant, a wall-clock time in a named zone, or only a calendar date. Do not silently collapse these meanings.
9
+ 3. Decode external strings, numbers, and `Date` objects at the boundary. Use `Schema.DateTimeUtc`, `Schema.DateTimeUtcFromString`, `Schema.DateTimeUtcFromMillis`, `Schema.DateTimeUtcFromDate`, or `Schema.DateTimeZoned` as appropriate.
10
+ 4. Keep domain values as `DateTime.Utc` by default. Use `DateTime.Zoned` when calendar operations or presentation must retain an IANA time zone.
11
+ 5. Use `DateTime.now` inside Effect programs so the current time comes from the `Clock` service. Reserve `DateTime.nowUnsafe()` and `Date.now()` for explicit synchronous host boundaries.
12
+ 6. Perform comparisons, arithmetic, rounding, and formatting with `DateTime` operations, then convert to `Date` or epoch milliseconds only when an external API requires them.
13
+ 7. Test time-dependent behavior with `it.effect` and `TestClock`; advance virtual time instead of sleeping or consulting the live clock.
14
+
15
+ ## Construction and boundaries
16
+
17
+ - Prefer safe decoding for untrusted input. `DateTime.make` and zoned constructors return `Option`; Schema decoders produce structured parse failures.
18
+ - Use `DateTime.makeUnsafe` only for literals and values already validated by the program. Do not turn user input into defects.
19
+ - Prefer explicit ISO 8601 strings with offsets at wire boundaries. Avoid implementation-dependent or locale-formatted date strings.
20
+ - Do not invent a midnight instant for a genuinely date-only value such as a birthday. Preserve a validated `YYYY-MM-DD` or a domain-specific year/month/day structure until an actual time and zone are chosen.
21
+ - Use `DateTime.makeZonedFromString` or `Schema.DateTimeZoned` when a serialized value must preserve its zone.
22
+ - Use `DateTime.toDateUtc`, `DateTime.toEpochMillis`, and `DateTime.fromDateUnsafe` only at interop boundaries.
23
+
24
+ ```ts
25
+ import { DateTime, Effect, Schema } from "effect";
26
+
27
+ const Timestamp = Schema.DateTimeUtcFromString;
28
+
29
+ const expiresAt = DateTime.makeUnsafe("2030-01-01T00:00:00Z");
30
+
31
+ const isExpired = Effect.gen(function* () {
32
+ const now = yield* DateTime.now;
33
+ return DateTime.isLessThanOrEqualTo(expiresAt, now);
34
+ });
35
+ ```
36
+
37
+ ## Instants, zones, and arithmetic
38
+
39
+ - Treat `DateTime.Utc` as an instant without retained zone information. Treat `DateTime.Zoned` as the same kind of instant plus a zone used for wall-clock parts, formatting, and zone-aware transformations.
40
+ - Use `DateTime.setZone` or `setZoneNamed` to view the same instant in another zone. When constructing from local wall-clock parts, use `makeZoned` with `adjustForTimeZone: true` and choose a deliberate `disambiguation` policy for DST gaps and repeated times.
41
+ - Use `DateTime.addDuration` and `subtractDuration` for elapsed time. Use `DateTime.add` and `subtract` for calendar arithmetic such as days, months, and years; these operations account for a zoned value's calendar rules.
42
+ - Use `DateTime.startOf`, `endOf`, or `nearest` rather than hand-editing fields. State `weekStartsOn` when week boundaries are domain-sensitive.
43
+ - Use `DateTime.Order`, `Equivalence`, `min`, `max`, `between`, and comparison helpers rather than comparing formatted strings or mutable `Date` objects.
44
+ - Use `formatIso` for UTC interchange, `formatIsoZoned` when preserving a zone, and `format` / `formatIntl` for presentation. Do not persist locale-formatted output.
45
+
46
+ ## Current time and tests
47
+
48
+ Code that asks what time it is must remain clock-driven:
49
+
50
+ ```ts
51
+ import { assert, it } from "@effect/vitest";
52
+ import { DateTime, Duration, Effect } from "effect";
53
+ import { TestClock } from "effect/testing";
54
+
55
+ it.effect("expires after an hour", () =>
56
+ Effect.gen(function* () {
57
+ yield* TestClock.setTime(DateTime.toEpochMillis(DateTime.makeUnsafe("2030-01-01T00:00:00Z")));
58
+ const startedAt = yield* DateTime.now;
59
+
60
+ yield* TestClock.adjust("1 hour");
61
+ const now = yield* DateTime.now;
62
+
63
+ assert.strictEqual(Duration.toMillis(DateTime.distance(startedAt, now)), 60 * 60 * 1000);
64
+ }),
65
+ );
66
+ ```
67
+
68
+ `@effect/vitest` supplies `TestClock` to `it.effect`. Fork work that sleeps, times out, retries, or follows a schedule before advancing the clock. Use `it.live` only when a test intentionally needs real time.
69
+
70
+ ## Completion check
71
+
72
+ Confirm that domain code uses `DateTime`, untrusted inputs are decoded, UTC versus zoned intent is explicit, DST behavior is deliberate, elapsed and calendar arithmetic are not confused, serialization is stable, and time-dependent tests use `TestClock` without wall-clock sleeps.
package/src/catalog.ts CHANGED
@@ -176,7 +176,8 @@ export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
176
176
  });
177
177
  }
178
178
  const families: Readonly<Record<string, ReadonlyArray<string>>> = {
179
- effect: ["effect-ts", "effect-atom-data-fetching"],
179
+ effect: ["effect-ts"],
180
+ "effect-atom-data-fetching": ["effect-ts"],
180
181
  ...Object.fromEntries(externalFamilies),
181
182
  };
182
183
 
package/src/index.ts CHANGED
@@ -11,10 +11,16 @@ export {
11
11
  EffectTsgoSetupSchema,
12
12
  type HarnessTarget,
13
13
  TargetConfigSchema,
14
+ type VitePlusQualityConfigSetup,
15
+ VitePlusQualityConfigSetupSchema,
14
16
  type VitePlusQualitySetup,
15
17
  VitePlusQualitySetupSchema,
16
18
  type VitePlusQualityTypecheckSetup,
17
19
  VitePlusQualityTypecheckSetupSchema,
20
+ type VitePlusQualityWorkflowSetup,
21
+ VitePlusQualityWorkflowSetupSchema,
22
+ type VitePlusQualityWorkflowStep,
23
+ VitePlusQualityWorkflowStepSchema,
18
24
  type VitePlusSetup,
19
25
  VitePlusSetupSchema,
20
26
  type VitePlusTypecheckStrategy,
package/src/manifest.ts CHANGED
@@ -63,10 +63,29 @@ export const VitePlusQualityTypecheckSetupSchema = Schema.Struct({
63
63
  });
64
64
  export type VitePlusQualityTypecheckSetup = typeof VitePlusQualityTypecheckSetupSchema.Type;
65
65
 
66
- export const VitePlusQualitySetupSchema = Schema.Struct({
66
+ export const VitePlusQualityConfigSetupSchema = Schema.Struct({
67
67
  enabled: Schema.optional(Schema.Boolean),
68
68
  typecheck: Schema.optional(VitePlusQualityTypecheckSetupSchema),
69
69
  });
70
+ export type VitePlusQualityConfigSetup = typeof VitePlusQualityConfigSetupSchema.Type;
71
+
72
+ export const VitePlusQualityWorkflowStepSchema = Schema.Struct({
73
+ name: Schema.String,
74
+ run: Schema.Array(Schema.String),
75
+ });
76
+ export type VitePlusQualityWorkflowStep = typeof VitePlusQualityWorkflowStepSchema.Type;
77
+
78
+ export const VitePlusQualityWorkflowSetupSchema = Schema.Struct({
79
+ enabled: Schema.optional(Schema.Boolean),
80
+ beforeChecks: Schema.optional(Schema.Array(VitePlusQualityWorkflowStepSchema)),
81
+ typecheck: Schema.optional(Schema.Array(Schema.String)),
82
+ });
83
+ export type VitePlusQualityWorkflowSetup = typeof VitePlusQualityWorkflowSetupSchema.Type;
84
+
85
+ export const VitePlusQualitySetupSchema = Schema.Struct({
86
+ config: Schema.optional(VitePlusQualityConfigSetupSchema),
87
+ workflow: Schema.optional(VitePlusQualityWorkflowSetupSchema),
88
+ });
70
89
  export type VitePlusQualitySetup = typeof VitePlusQualitySetupSchema.Type;
71
90
 
72
91
  export const VitePlusSetupSchema = Schema.Struct({
@@ -134,11 +153,18 @@ export type NormalizedManifest = {
134
153
  readonly enabled: boolean;
135
154
  };
136
155
  readonly quality: {
137
- readonly enabled: boolean;
138
- readonly typecheck: {
139
- readonly strategy: VitePlusTypecheckStrategy;
140
- readonly concurrency: number;
141
- readonly packages: ReadonlyArray<string>;
156
+ readonly config: {
157
+ readonly enabled: boolean;
158
+ readonly typecheck: {
159
+ readonly strategy: VitePlusTypecheckStrategy;
160
+ readonly concurrency: number;
161
+ readonly packages: ReadonlyArray<string>;
162
+ };
163
+ };
164
+ readonly workflow: {
165
+ readonly enabled: boolean;
166
+ readonly beforeChecks: ReadonlyArray<VitePlusQualityWorkflowStep>;
167
+ readonly typecheck: ReadonlyArray<string>;
142
168
  };
143
169
  };
144
170
  };
@@ -174,6 +200,8 @@ export const normalizeManifest = (manifest: DevKitManifest): NormalizedManifest
174
200
  };
175
201
  }
176
202
  }
203
+ const quality = manifest.setup?.vitePlus?.quality;
204
+ const typecheck = quality?.config?.typecheck;
177
205
 
178
206
  return {
179
207
  exclude: manifest.exclude ?? [],
@@ -202,11 +230,18 @@ export const normalizeManifest = (manifest: DevKitManifest): NormalizedManifest
202
230
  enabled: manifest.setup?.vitePlus?.hooks?.enabled ?? false,
203
231
  },
204
232
  quality: {
205
- enabled: manifest.setup?.vitePlus?.quality?.enabled ?? false,
206
- typecheck: {
207
- strategy: manifest.setup?.vitePlus?.quality?.typecheck?.strategy ?? "single-project",
208
- concurrency: manifest.setup?.vitePlus?.quality?.typecheck?.concurrency ?? 4,
209
- packages: manifest.setup?.vitePlus?.quality?.typecheck?.packages ?? [],
233
+ config: {
234
+ enabled: quality?.config?.enabled ?? false,
235
+ typecheck: {
236
+ strategy: typecheck?.strategy ?? "single-project",
237
+ concurrency: typecheck?.concurrency ?? 4,
238
+ packages: typecheck?.packages ?? [],
239
+ },
240
+ },
241
+ workflow: {
242
+ enabled: quality?.workflow?.enabled ?? false,
243
+ beforeChecks: quality?.workflow?.beforeChecks ?? [],
244
+ typecheck: quality?.workflow?.typecheck ?? ["vp run typecheck"],
210
245
  },
211
246
  },
212
247
  },
package/src/sync.ts CHANGED
@@ -225,7 +225,8 @@ class ApplyRaceError extends Schema.TaggedErrorClass<ApplyRaceError>()("ApplyRac
225
225
  }
226
226
 
227
227
  const SKILL_FAMILIES: SkillCatalog = {
228
- effect: ["effect-ts", "effect-atom-data-fetching"],
228
+ effect: ["effect-ts"],
229
+ "effect-atom-data-fetching": ["effect-ts"],
229
230
  };
230
231
 
231
232
  export const DEFAULT_MANIFEST = "dev-kit.jsonc";
@@ -634,43 +635,48 @@ const buildDesiredOutputs = Effect.fn("buildDesiredSkillOutputs")(function* (
634
635
  linkTarget,
635
636
  });
636
637
  }
637
- if (setup.vitePlus.quality.enabled) {
638
- for (const generated of [
639
- {
640
- resourceId: "setup:vite-plus-config" as const,
641
- path: VITE_PLUS_CONFIG_PATH,
642
- sourcePath: VITE_PLUS_CONFIG_TEMPLATE,
643
- },
644
- {
645
- resourceId: "setup:vite-plus-github-actions" as const,
646
- path: VITE_PLUS_GITHUB_ACTIONS_PATH,
647
- sourcePath: VITE_PLUS_GITHUB_ACTIONS_TEMPLATE,
648
- },
649
- ]) {
650
- const managed = yield* resolveManagedPath(projectDir, generated.path);
651
- const template = yield* readGeneratedFileTemplate(packageRoot, generated.sourcePath);
652
- const content =
653
- generated.resourceId === "setup:vite-plus-config"
654
- ? renderVitePlusConfigTemplate(template, setup.vitePlus.quality.typecheck)
655
- : renderVitePlusWorkflowTemplate(
656
- template,
657
- projectDir === packageRoot
658
- ? "./bin/dev-kit.mjs apply --locked"
659
- : "vp exec dev-kit apply --locked",
660
- );
638
+ if (setup.vitePlus.quality.config.enabled) {
639
+ const managed = yield* resolveManagedPath(projectDir, VITE_PLUS_CONFIG_PATH);
640
+ const template = yield* readGeneratedFileTemplate(packageRoot, VITE_PLUS_CONFIG_TEMPLATE);
641
+ const content = renderVitePlusConfigTemplate(template, setup.vitePlus.quality.config.typecheck);
661
642
 
662
- outputs.push({
663
- resourceId: generated.resourceId,
664
- path: managed.relative,
665
- sourcePath: generated.sourcePath,
666
- mode: "copy",
667
- kind: "file",
668
- digest: yield* digestFileContent(content),
669
- destination: managed.absolute,
670
- content,
671
- adoptIfExact: true,
672
- });
673
- }
643
+ outputs.push({
644
+ resourceId: "setup:vite-plus-config",
645
+ path: managed.relative,
646
+ sourcePath: VITE_PLUS_CONFIG_TEMPLATE,
647
+ mode: "copy",
648
+ kind: "file",
649
+ digest: yield* digestFileContent(content),
650
+ destination: managed.absolute,
651
+ content,
652
+ adoptIfExact: true,
653
+ });
654
+ }
655
+ if (setup.vitePlus.quality.workflow.enabled) {
656
+ const managed = yield* resolveManagedPath(projectDir, VITE_PLUS_GITHUB_ACTIONS_PATH);
657
+ const template = yield* readGeneratedFileTemplate(
658
+ packageRoot,
659
+ VITE_PLUS_GITHUB_ACTIONS_TEMPLATE,
660
+ );
661
+ const content = renderVitePlusWorkflowTemplate(template, {
662
+ devKitCommand:
663
+ projectDir === packageRoot
664
+ ? "./bin/dev-kit.mjs apply --locked"
665
+ : "vp exec dev-kit apply --locked",
666
+ workflow: setup.vitePlus.quality.workflow,
667
+ });
668
+
669
+ outputs.push({
670
+ resourceId: "setup:vite-plus-github-actions",
671
+ path: managed.relative,
672
+ sourcePath: VITE_PLUS_GITHUB_ACTIONS_TEMPLATE,
673
+ mode: "copy",
674
+ kind: "file",
675
+ digest: yield* digestFileContent(content),
676
+ destination: managed.absolute,
677
+ content,
678
+ adoptIfExact: true,
679
+ });
674
680
  }
675
681
  const agentsTarget = targets.agents;
676
682
  const duplicateOutput = skills.find(
@@ -918,18 +924,31 @@ export const planProjectSkills = Effect.fn("planProjectSkills")(function* (optio
918
924
  const packageRoot = yield* resolvePackageRoot();
919
925
  const manifest = normalizeManifest(yield* readManifest(manifestManaged.absolute));
920
926
 
921
- if (manifest.setup.vitePlus.quality.enabled) {
927
+ const vitePlusQuality = manifest.setup.vitePlus.quality;
928
+ const vitePlusQualityEnabled = vitePlusQuality.config.enabled || vitePlusQuality.workflow.enabled;
929
+
930
+ if (vitePlusQualityEnabled) {
922
931
  if (!manifest.setup.effectTsgo.enabled) {
923
932
  return yield* new InvalidProjectStateError({
924
933
  message:
925
- "setup.vitePlus.quality requires setup.effectTsgo.enabled so vp run typecheck uses the Effect-patched compiler",
934
+ "setup.vitePlus.quality requires setup.effectTsgo.enabled so managed quality setup converges the Effect-patched compiler",
926
935
  });
927
936
  }
928
937
  yield* validateVitePlusQualitySupport(
929
938
  projectDir,
930
939
  packageRoot,
931
940
  manifest.setup.effectTsgo.typescriptPackage,
932
- manifest.setup.vitePlus.quality.typecheck,
941
+ {
942
+ ...(vitePlusQuality.config.enabled ? { config: vitePlusQuality.config.typecheck } : {}),
943
+ ...(vitePlusQuality.workflow.enabled
944
+ ? {
945
+ workflow: {
946
+ beforeChecks: vitePlusQuality.workflow.beforeChecks,
947
+ typecheck: vitePlusQuality.workflow.typecheck,
948
+ },
949
+ }
950
+ : {}),
951
+ },
933
952
  );
934
953
  }
935
954
  const effectSource = manifest.setup.effectSource.enabled
@@ -1,7 +1,7 @@
1
1
  import { Effect, FileSystem, Path, Schema } from "effect";
2
2
  import { parse as parseJsonc, type ParseError } from "jsonc-parser";
3
3
 
4
- import type { VitePlusTypecheckStrategy } from "./manifest.ts";
4
+ import type { VitePlusQualityWorkflowStep, VitePlusTypecheckStrategy } from "./manifest.ts";
5
5
  import { readDirectDependencyNames, readProjectPackage } from "./project-package.ts";
6
6
  import { validateInstalledVitePlus } from "./vite-plus-dependency.ts";
7
7
 
@@ -21,8 +21,22 @@ export type VitePlusQualityTypecheck = {
21
21
  readonly packages: ReadonlyArray<string>;
22
22
  };
23
23
 
24
+ export type VitePlusQualityWorkflow = {
25
+ readonly beforeChecks: ReadonlyArray<VitePlusQualityWorkflowStep>;
26
+ readonly typecheck: ReadonlyArray<string>;
27
+ };
28
+
29
+ export type VitePlusQualitySelection = {
30
+ readonly config?: VitePlusQualityTypecheck;
31
+ readonly workflow?: VitePlusQualityWorkflow;
32
+ };
33
+
24
34
  const SINGLE_PROJECT_TYPECHECK_TASK = ' typecheck: "tsc --noEmit",';
25
35
  const LOCKED_DEV_KIT_COMMAND = "vp exec dev-kit apply --locked";
36
+ const BEFORE_CHECKS_MARKER =
37
+ " # Dev Kit inserts configured quality.workflow.beforeChecks steps here.\n\n";
38
+ const DEFAULT_WORKFLOW_TYPECHECK = ` - name: Type check with Effect TypeScript-Go
39
+ run: vp run typecheck`;
26
40
 
27
41
  const replaceUniqueTemplateMarker = (
28
42
  template: string,
@@ -62,18 +76,65 @@ export const renderVitePlusConfigTemplate = (
62
76
 
63
77
  export const renderVitePlusWorkflowTemplate = (
64
78
  template: string,
65
- devKitCommand = LOCKED_DEV_KIT_COMMAND,
66
- ): string =>
67
- devKitCommand === LOCKED_DEV_KIT_COMMAND
68
- ? template
69
- : replaceUniqueTemplateMarker(template, LOCKED_DEV_KIT_COMMAND, devKitCommand);
79
+ options: {
80
+ readonly devKitCommand?: string;
81
+ readonly workflow?: VitePlusQualityWorkflow;
82
+ } = {},
83
+ ): string => {
84
+ const devKitCommand = options.devKitCommand;
85
+ const workflow = options.workflow;
86
+ let rendered =
87
+ devKitCommand === undefined || devKitCommand === LOCKED_DEV_KIT_COMMAND
88
+ ? template
89
+ : replaceUniqueTemplateMarker(template, LOCKED_DEV_KIT_COMMAND, devKitCommand);
90
+
91
+ if (workflow !== undefined) {
92
+ const steps = workflow.beforeChecks
93
+ .map((step) => {
94
+ const commands = step.run
95
+ .flatMap((command) => command.split("\n"))
96
+ .map((line) => ` ${line}`)
97
+ .join("\n");
98
+
99
+ return ` - name: ${JSON.stringify(step.name)}
100
+ run: |
101
+ ${commands}`;
102
+ })
103
+ .join("\n\n");
104
+
105
+ rendered = replaceUniqueTemplateMarker(
106
+ rendered,
107
+ BEFORE_CHECKS_MARKER,
108
+ steps.length === 0 ? "" : `${steps}\n\n`,
109
+ );
110
+ }
111
+ if (
112
+ workflow !== undefined &&
113
+ (workflow.typecheck.length !== 1 || workflow.typecheck[0] !== "vp run typecheck")
114
+ ) {
115
+ const commands = workflow.typecheck
116
+ .flatMap((command) => command.split("\n"))
117
+ .map((line) => ` ${line}`)
118
+ .join("\n");
119
+
120
+ rendered = replaceUniqueTemplateMarker(
121
+ rendered,
122
+ DEFAULT_WORKFLOW_TYPECHECK,
123
+ ` - name: Type check with Effect TypeScript-Go
124
+ run: |
125
+ ${commands}`,
126
+ );
127
+ }
128
+
129
+ return rendered;
130
+ };
70
131
 
71
132
  export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQualitySupport")(
72
133
  function* (
73
134
  projectDir: string,
74
135
  packageRoot: string,
75
136
  typescriptPackage: string,
76
- typecheck: VitePlusQualityTypecheck,
137
+ selection: VitePlusQualitySelection,
77
138
  ) {
78
139
  const fs = yield* FileSystem.FileSystem;
79
140
  const path = yield* Path.Path;
@@ -93,24 +154,54 @@ export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQuality
93
154
  message: `setup.vitePlus.quality requires direct dependencies: ${missing.join(", ")}`,
94
155
  });
95
156
  }
157
+ if (selection.workflow !== undefined) {
158
+ if (selection.workflow.typecheck.length === 0) {
159
+ return yield* new VitePlusQualitySupportError({
160
+ message: "setup.vitePlus.quality.workflow.typecheck requires at least one command",
161
+ });
162
+ }
163
+ for (const command of selection.workflow.typecheck) {
164
+ if (command.trim().length === 0) {
165
+ return yield* new VitePlusQualitySupportError({
166
+ message: "setup.vitePlus.quality.workflow.typecheck commands must not be empty",
167
+ });
168
+ }
169
+ }
170
+ for (const step of selection.workflow.beforeChecks) {
171
+ if (step.name.trim().length === 0 || step.run.length === 0) {
172
+ return yield* new VitePlusQualitySupportError({
173
+ message:
174
+ "setup.vitePlus.quality.workflow.beforeChecks steps require a name and at least one command",
175
+ });
176
+ }
177
+ if (step.run.some((command) => command.trim().length === 0)) {
178
+ return yield* new VitePlusQualitySupportError({
179
+ message: "setup.vitePlus.quality.workflow.beforeChecks commands must not be empty",
180
+ });
181
+ }
182
+ }
183
+ }
184
+ const typecheck = selection.config;
185
+
186
+ if (typecheck === undefined) return;
96
187
  const conflictingScripts = ["check", "typecheck"].filter(
97
188
  (script) => packageJson.scripts?.[script] !== undefined,
98
189
  );
99
190
 
100
191
  if (conflictingScripts.length > 0) {
101
192
  return yield* new VitePlusQualitySupportError({
102
- message: `setup.vitePlus.quality defines Vite tasks that conflict with package scripts: ${conflictingScripts.join(", ")}`,
193
+ message: `setup.vitePlus.quality.config defines Vite tasks that conflict with package scripts: ${conflictingScripts.join(", ")}`,
103
194
  });
104
195
  }
105
196
  if (typecheck.concurrency < 1 || typecheck.concurrency > 32) {
106
197
  return yield* new VitePlusQualitySupportError({
107
- message: "setup.vitePlus.quality typecheck concurrency must be between 1 and 32",
198
+ message: "setup.vitePlus.quality.config typecheck concurrency must be between 1 and 32",
108
199
  });
109
200
  }
110
201
  if (typecheck.strategy === "single-project" && typecheck.packages.length > 0) {
111
202
  return yield* new VitePlusQualitySupportError({
112
203
  message:
113
- "setup.vitePlus.quality single-project typechecking does not accept workspace packages",
204
+ "setup.vitePlus.quality.config single-project typechecking does not accept workspace packages",
114
205
  });
115
206
  }
116
207
  const workspacePatterns = Array.isArray(packageJson.workspaces)
@@ -126,21 +217,22 @@ export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQuality
126
217
  !workspacePatterns.every((pattern) => typeof pattern === "string"))
127
218
  ) {
128
219
  return yield* new VitePlusQualitySupportError({
129
- message: "setup.vitePlus.quality workspace typechecking requires package.json workspaces",
220
+ message:
221
+ "setup.vitePlus.quality.config workspace typechecking requires package.json workspaces",
130
222
  });
131
223
  }
132
224
  if (typecheck.strategy === "workspace") {
133
225
  if (typecheck.packages.length === 0) {
134
226
  return yield* new VitePlusQualitySupportError({
135
227
  message:
136
- "setup.vitePlus.quality workspace typechecking requires explicit package directories",
228
+ "setup.vitePlus.quality.config workspace typechecking requires explicit package directories",
137
229
  });
138
230
  }
139
231
  const uniquePackages = new Set(typecheck.packages);
140
232
 
141
233
  if (uniquePackages.size !== typecheck.packages.length) {
142
234
  return yield* new VitePlusQualitySupportError({
143
- message: "setup.vitePlus.quality workspace typecheck packages must be unique",
235
+ message: "setup.vitePlus.quality.config workspace typecheck packages must be unique",
144
236
  });
145
237
  }
146
238
  for (const packageDir of typecheck.packages) {
@@ -155,21 +247,21 @@ export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQuality
155
247
  relative.startsWith(`..${path.sep}`)
156
248
  ) {
157
249
  return yield* new VitePlusQualitySupportError({
158
- message: `setup.vitePlus.quality workspace package must be a project-relative subdirectory: ${packageDir}`,
250
+ message: `setup.vitePlus.quality.config workspace package must be a project-relative subdirectory: ${packageDir}`,
159
251
  });
160
252
  }
161
253
  const workspacePackage = yield* readProjectPackage(absolute).pipe(
162
254
  Effect.mapError(
163
255
  () =>
164
256
  new VitePlusQualitySupportError({
165
- message: `setup.vitePlus.quality workspace package is missing a valid package.json: ${packageDir}`,
257
+ message: `setup.vitePlus.quality.config workspace package is missing a valid package.json: ${packageDir}`,
166
258
  }),
167
259
  ),
168
260
  );
169
261
 
170
262
  if (workspacePackage.scripts?.typecheck === undefined) {
171
263
  return yield* new VitePlusQualitySupportError({
172
- message: `setup.vitePlus.quality workspace package requires a typecheck script: ${packageDir}`,
264
+ message: `setup.vitePlus.quality.config workspace package requires a typecheck script: ${packageDir}`,
173
265
  });
174
266
  }
175
267
  }
@@ -179,7 +271,8 @@ export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQuality
179
271
 
180
272
  if (!(yield* fs.exists(tsconfigPath))) {
181
273
  return yield* new VitePlusQualitySupportError({
182
- message: "setup.vitePlus.quality single-project typechecking requires tsconfig.json",
274
+ message:
275
+ "setup.vitePlus.quality.config single-project typechecking requires tsconfig.json",
183
276
  });
184
277
  }
185
278
  const errors: Array<ParseError> = [];
@@ -189,7 +282,7 @@ export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQuality
189
282
 
190
283
  if (errors.length > 0 || typeof tsconfig !== "object" || tsconfig === null) {
191
284
  return yield* new VitePlusQualitySupportError({
192
- message: "setup.vitePlus.quality requires a valid root tsconfig.json",
285
+ message: "setup.vitePlus.quality.config requires a valid root tsconfig.json",
193
286
  });
194
287
  }
195
288
  const references = (tsconfig as { readonly references?: unknown }).references;
@@ -197,7 +290,7 @@ export const validateVitePlusQualitySupport = Effect.fn("validateVitePlusQuality
197
290
  if (Array.isArray(references) && references.length > 0) {
198
291
  return yield* new VitePlusQualitySupportError({
199
292
  message:
200
- "setup.vitePlus.quality single-project typechecking does not support tsconfig project references; select the workspace strategy or use a custom Vite config",
293
+ "setup.vitePlus.quality.config single-project typechecking does not support tsconfig project references; select the workspace strategy or use a custom Vite config",
201
294
  });
202
295
  }
203
296
  }
@@ -36,6 +36,8 @@ jobs:
36
36
  - name: Verify locked Dev Kit setup
37
37
  run: vp exec dev-kit apply --locked
38
38
 
39
+ # Dev Kit inserts configured quality.workflow.beforeChecks steps here.
40
+
39
41
  - name: Check formatting
40
42
  run: vp fmt --check
41
43
 
@@ -1,4 +0,0 @@
1
- interface:
2
- display_name: "Effect Atom Data Fetching"
3
- short_description: "Cache HTTP data safely with Effect Atom"
4
- default_prompt: "Use $effect-atom-data-fetching to design or debug Effect Atom HTTP queries, caching, polling, invalidation, and SSR integration in a React app."