@danieljvdm/dev-kit 0.7.2 → 0.9.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 +60 -53
- package/dev-kit.example.jsonc +5 -2
- package/package.json +1 -1
- package/schema/dev-kit.schema.json +62 -1
- package/skill-sources.jsonc +8 -0
- package/skill-sources.lock.json +18 -0
- package/skills/dev-kit/SKILL.md +27 -32
- package/skills/effect-ts/SKILL.md +29 -1
- package/skills/effect-ts/agents/openai.yaml +1 -1
- package/skills/{effect-atom-data-fetching/references/cache-lifecycle.md → effect-ts/references/atom-cache-lifecycle.md} +1 -1
- package/skills/{effect-atom-data-fetching/references/http-and-invalidation.md → effect-ts/references/atom-http-and-invalidation.md} +1 -1
- package/skills/{effect-atom-data-fetching/references/tanstack-start.md → effect-ts/references/atom-tanstack-start.md} +1 -1
- package/skills/{effect-atom-data-fetching/references/testing.md → effect-ts/references/atom-testing.md} +1 -1
- package/skills/{effect-atom-data-fetching/SKILL.md → effect-ts/references/guide-atom-data-fetching.md} +0 -12
- package/skills/effect-ts/references/guide-datetime.md +72 -0
- package/skills/testing/SKILL.md +23 -0
- package/skills/testing/agents/openai.yaml +4 -0
- package/src/catalog.ts +2 -1
- package/src/index.ts +6 -0
- package/src/manifest.ts +46 -11
- package/src/sync.ts +59 -40
- package/src/vite-plus-quality.ts +112 -19
- package/templates/vite-plus/github-actions-check.yml +2 -0
- package/skills/effect-atom-data-fetching/agents/openai.yaml +0 -4
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`
|
|
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
|
-
|
|
263
|
-
|
|
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": {
|
|
273
|
+
"quality": {
|
|
274
|
+
"config": { "enabled": true },
|
|
275
|
+
"workflow": { "enabled": true },
|
|
276
|
+
},
|
|
273
277
|
},
|
|
274
278
|
},
|
|
275
279
|
}
|
|
276
280
|
```
|
|
277
281
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
`vite-plus`
|
|
282
|
-
|
|
283
|
-
|
|
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`
|
|
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
|
-
"
|
|
301
|
-
|
|
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
|
-
|
|
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
|
-
"
|
|
317
|
-
|
|
318
|
-
"
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
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
|
|
351
|
-
templates.
|
|
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.
|
|
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.
|
|
551
|
-
|
|
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
|
package/dev-kit.example.jsonc
CHANGED
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
"vitePlus": {
|
|
10
10
|
"hooks": { "enabled": true },
|
|
11
11
|
"quality": {
|
|
12
|
-
"
|
|
13
|
-
|
|
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
|
@@ -161,7 +161,20 @@
|
|
|
161
161
|
}
|
|
162
162
|
},
|
|
163
163
|
"vitePlusQualitySetup": {
|
|
164
|
-
"description": "
|
|
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",
|
package/skill-sources.jsonc
CHANGED
|
@@ -47,5 +47,13 @@
|
|
|
47
47
|
"include": ["serve-sim"],
|
|
48
48
|
"licensePath": "LICENSE",
|
|
49
49
|
},
|
|
50
|
+
{
|
|
51
|
+
"id": "mattpocock-skills",
|
|
52
|
+
"repository": "https://github.com/mattpocock/skills.git",
|
|
53
|
+
"ref": "main",
|
|
54
|
+
"skillsPath": "skills/engineering",
|
|
55
|
+
"include": ["tdd", "improve-codebase-architecture"],
|
|
56
|
+
"licensePath": "LICENSE",
|
|
57
|
+
},
|
|
50
58
|
],
|
|
51
59
|
}
|
package/skill-sources.lock.json
CHANGED
|
@@ -125,6 +125,24 @@
|
|
|
125
125
|
"serve-sim": "sha256:782f41d4d79100d5bfc9ec1211e5d4766e978299bbb93fd8e34c6f8d2ecf2114"
|
|
126
126
|
},
|
|
127
127
|
"licensePath": "LICENSE"
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
"id": "mattpocock-skills",
|
|
131
|
+
"repository": "https://github.com/mattpocock/skills.git",
|
|
132
|
+
"ref": "main",
|
|
133
|
+
"resolved": "2ab958093e83e0ec752e6c1c5932da465bf23e0c",
|
|
134
|
+
"skillsPath": "skills/engineering",
|
|
135
|
+
"include": ["tdd", "improve-codebase-architecture"],
|
|
136
|
+
"skills": ["improve-codebase-architecture", "tdd"],
|
|
137
|
+
"descriptions": {
|
|
138
|
+
"improve-codebase-architecture": "Scan a codebase for deepening opportunities, present them as a visual HTML report, then grill through whichever one you pick.",
|
|
139
|
+
"tdd": "Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions \"red-green-refactor\", or wants integration tests."
|
|
140
|
+
},
|
|
141
|
+
"digests": {
|
|
142
|
+
"improve-codebase-architecture": "sha256:43721b6125a4c703810414e649fbbab252f1f85a62385cdb23f313e0e06ba26a",
|
|
143
|
+
"tdd": "sha256:ea2d26eda005b10a2de4bdb7de44d33072f2a8e82dbf670952ce30a9c2d7c475"
|
|
144
|
+
},
|
|
145
|
+
"licensePath": "LICENSE"
|
|
128
146
|
}
|
|
129
147
|
]
|
|
130
148
|
}
|
package/skills/dev-kit/SKILL.md
CHANGED
|
@@ -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": {
|
|
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`
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
277
|
-
|
|
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,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.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: testing
|
|
3
|
+
description: Set an extremely high bar for automated tests. Use whenever software work raises the question of adding, changing, retaining, or removing tests.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Testing
|
|
7
|
+
|
|
8
|
+
**If in doubt, don't test it.**
|
|
9
|
+
|
|
10
|
+
Committed tests must prove durable value. Tests are permanent product code, not a reflexive companion to each source file.
|
|
11
|
+
|
|
12
|
+
Write a test only when every condition holds:
|
|
13
|
+
|
|
14
|
+
- It protects stable product behavior rather than an implementation detail.
|
|
15
|
+
- A regression is plausible and would be costly.
|
|
16
|
+
- The behavior is observable through a stable public seam.
|
|
17
|
+
- The test will survive likely refactors.
|
|
18
|
+
- The expected result is independent of the implementation.
|
|
19
|
+
- Existing tests, types, static checks, or a smoke check cannot cover the risk adequately.
|
|
20
|
+
|
|
21
|
+
If any condition fails, skip the test and use the cheapest sufficient verification instead. Moving MVP behavior often warrants no committed tests.
|
|
22
|
+
|
|
23
|
+
Test capabilities, not files. Use the fewest tests that protect the risk, and ignore coverage targets unless the user explicitly asks for them.
|
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"
|
|
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
|
|
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
|
|
138
|
-
|
|
139
|
-
readonly
|
|
140
|
-
|
|
141
|
-
|
|
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
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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"
|
|
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
|
-
|
|
639
|
-
|
|
640
|
-
|
|
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
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
package/src/vite-plus-quality.ts
CHANGED
|
@@ -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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
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:
|
|
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:
|
|
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
|
}
|
|
@@ -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."
|