@danieljvdm/dev-kit 0.11.3 → 0.13.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 +111 -78
- package/dev-kit.example.jsonc +0 -4
- package/package.json +10 -6
- package/schema/dev-kit.schema.json +1 -46
- package/skills/build-effect-apis/SKILL.md +77 -0
- package/skills/build-effect-apis/agents/openai.yaml +4 -0
- package/skills/build-effect-apis/references/cloudflare-workers.md +71 -0
- package/skills/build-effect-apis/references/effect-atom-client.md +161 -0
- package/skills/build-effect-apis/references/effect-atom-lifecycle.md +78 -0
- package/skills/build-effect-apis/references/effect-atom-testing.md +74 -0
- package/skills/build-effect-apis/references/runtime-assembly.md +56 -0
- package/skills/build-effect-apis/references/server-and-middleware.md +174 -0
- package/skills/build-effect-apis/references/shared-contracts.md +108 -0
- package/skills/build-effect-apis/references/tanstack-start.md +86 -0
- package/skills/build-effect-apis/references/verification.md +50 -0
- package/skills/build-effect-clis/SKILL.md +61 -0
- package/skills/build-effect-clis/agents/openai.yaml +4 -0
- package/skills/build-effect-clis/references/command-design.md +93 -0
- package/skills/build-effect-clis/references/entrypoints-and-testing.md +59 -0
- package/skills/build-effect-clis/references/processes-and-platform.md +73 -0
- package/skills/dev-kit/SKILL.md +77 -48
- package/skills/effect-architecture-audit/SKILL.md +26 -0
- package/skills/effect-architecture-audit/agents/openai.yaml +4 -0
- package/skills/effect-architecture-audit/references/service-and-boundary-audit.md +150 -0
- package/skills/effect-ts/SKILL.md +21 -256
- package/skills/effect-ts/agents/openai.yaml +3 -3
- package/skills/testing/SKILL.md +5 -0
- package/src/catalog-manager.ts +16 -17
- package/src/catalog.ts +71 -16
- package/src/effect-source.ts +46 -24
- package/src/effect-tsgo.ts +86 -24
- package/src/gitignore.ts +5 -5
- package/src/index.ts +3 -6
- package/src/manifest.ts +0 -34
- package/src/node-symbolic-link.ts +2 -2
- package/src/oxfmt.js +5 -0
- package/src/oxfmt.ts +5 -0
- package/src/oxlint.js +5 -0
- package/src/oxlint.ts +5 -0
- package/src/package-skill-source.ts +51 -59
- package/src/path-digest.ts +7 -7
- package/src/project-package.ts +8 -7
- package/src/project-process-lock.ts +17 -12
- package/src/project-state.ts +1 -1
- package/src/skill-manager.ts +18 -16
- package/src/skill-selector.ts +12 -0
- package/src/sync.ts +181 -131
- package/src/tool-ignore-patterns.js +9 -0
- package/src/tool-ignore-patterns.ts +15 -0
- package/src/vendor.ts +67 -61
- package/src/vite-plus-dependency.ts +10 -11
- package/src/vite-plus-hooks.ts +24 -14
- package/src/vite-plus-quality.ts +21 -172
- package/src/vite-plus.js +81 -0
- package/src/vite-plus.ts +102 -0
- package/templates/AGENTS.md +1 -1
- package/skills/effect-ts/UPSTREAM.md +0 -28
- package/skills/effect-ts/references/atom-cache-lifecycle.md +0 -78
- package/skills/effect-ts/references/atom-http-and-invalidation.md +0 -97
- package/skills/effect-ts/references/atom-tanstack-start.md +0 -69
- package/skills/effect-ts/references/atom-testing.md +0 -67
- package/skills/effect-ts/references/audit-services.md +0 -144
- package/skills/effect-ts/references/features.md +0 -525
- package/skills/effect-ts/references/guide-atom-data-fetching.md +0 -44
- package/skills/effect-ts/references/guide-cli.md +0 -107
- package/skills/effect-ts/references/guide-datetime.md +0 -72
- package/skills/effect-ts/references/guide-effect.md +0 -440
- package/skills/effect-ts/references/guide-error-handling.md +0 -565
- package/skills/effect-ts/references/guide-http-boundaries.md +0 -55
- package/skills/effect-ts/references/guide-layers.md +0 -989
- package/skills/effect-ts/references/guide-observability.md +0 -746
- package/skills/effect-ts/references/guide-retries.md +0 -434
- package/skills/effect-ts/references/guide-schedule.md +0 -343
- package/skills/effect-ts/references/guide-schema.md +0 -664
- package/skills/effect-ts/references/guide-sql.md +0 -536
- package/skills/effect-ts/references/guide-testing.md +0 -532
- package/skills/effect-ts/references/guide-type-safety-and-boundaries.md +0 -131
- package/skills/effect-ts/references/version-and-source.md +0 -86
- package/templates/vite-plus/vite.config.ts +0 -22
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# TanStack Start integration
|
|
2
|
+
|
|
3
|
+
TanStack Start modules are isomorphic unless an explicit boundary says
|
|
4
|
+
otherwise. Choose one data-execution model before wiring Effect Atom.
|
|
5
|
+
|
|
6
|
+
## Client-only atom data
|
|
7
|
+
|
|
8
|
+
Use this when identity, focus signals, or other dependencies require browser
|
|
9
|
+
globals.
|
|
10
|
+
|
|
11
|
+
1. Mount one `RegistryProvider` around the application subtree whose client
|
|
12
|
+
navigations should share cache state.
|
|
13
|
+
2. Put browser-dependent atom consumers behind `ClientOnly`.
|
|
14
|
+
3. Keep the runtime, `AtomHttpApi.Service`, query families, and mutations as
|
|
15
|
+
client module singletons.
|
|
16
|
+
4. Render a useful server fallback and accept that API fetching begins after
|
|
17
|
+
hydration.
|
|
18
|
+
|
|
19
|
+
```tsx
|
|
20
|
+
import { RegistryProvider } from "@effect/atom-react";
|
|
21
|
+
import { ClientOnly, Outlet } from "@tanstack/react-router";
|
|
22
|
+
|
|
23
|
+
export function Root() {
|
|
24
|
+
return (
|
|
25
|
+
<RegistryProvider defaultIdleTTL={60_000}>
|
|
26
|
+
<ClientOnly fallback={<AppSkeleton />}>
|
|
27
|
+
<Outlet />
|
|
28
|
+
</ClientOnly>
|
|
29
|
+
</RegistryProvider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`Atom.windowFocusSignal`, `localStorage`, `window`, and `document` belong in
|
|
35
|
+
this client-only branch.
|
|
36
|
+
|
|
37
|
+
## SSR or loader-owned data
|
|
38
|
+
|
|
39
|
+
Prefer TanStack loaders or server functions when they already own SSR data.
|
|
40
|
+
Pass loader data into the client state graph or keep the query loader-owned;
|
|
41
|
+
avoid maintaining two independent server caches for the same request.
|
|
42
|
+
|
|
43
|
+
When atom SSR is intentional:
|
|
44
|
+
|
|
45
|
+
- create the registry and request-specific runtime/layers per request;
|
|
46
|
+
- give serializable queries deterministic `serializationKey` values;
|
|
47
|
+
- mount or run only the intended serializable atoms on the server;
|
|
48
|
+
- dehydrate only intended values;
|
|
49
|
+
- create the browser registry once and hydrate matching atom identities before
|
|
50
|
+
descendants read them;
|
|
51
|
+
- use `HydrationBoundary` when the installed React adapter supports it.
|
|
52
|
+
|
|
53
|
+
Process-global registries or memo maps can leak request-specific authentication
|
|
54
|
+
and server state across users. Verify request isolation with concurrent SSR
|
|
55
|
+
tests.
|
|
56
|
+
|
|
57
|
+
## Focus is browser-only
|
|
58
|
+
|
|
59
|
+
`Atom.windowFocusSignal` reads `window` and `document.visibilityState` when
|
|
60
|
+
mounted. Keep focus-enabled consumers behind `ClientOnly`, or inject a no-op
|
|
61
|
+
server signal and the browser signal on the client. `revalidateOnFocus: true`
|
|
62
|
+
respects `staleTime`; `"always"` forces a request on each focus signal.
|
|
63
|
+
|
|
64
|
+
## Separate API development
|
|
65
|
+
|
|
66
|
+
When TanStack Start and the Effect API run as separate local processes, proxy a
|
|
67
|
+
stable prefix and WebSocket upgrades through the app dev server:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
export default defineConfig({
|
|
71
|
+
plugins: [tanstackStart(), react()],
|
|
72
|
+
server: {
|
|
73
|
+
proxy: {
|
|
74
|
+
"/api": {
|
|
75
|
+
target: "http://127.0.0.1:8787",
|
|
76
|
+
changeOrigin: true,
|
|
77
|
+
ws: true,
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Keep the client base URL aligned with proxy and production routing. If
|
|
85
|
+
credentials cross origins, configure CORS and cookie/header behavior at the API
|
|
86
|
+
edge and test the deployed topology, not only the same-origin development path.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Verification
|
|
2
|
+
|
|
3
|
+
Test the contract spine at its seams. Use the repository's established Effect
|
|
4
|
+
test integration and command authority.
|
|
5
|
+
|
|
6
|
+
## Contract tests
|
|
7
|
+
|
|
8
|
+
- Decode and re-encode representative request, success, and error values.
|
|
9
|
+
- Prove branded path/query/header values reject invalid wire input.
|
|
10
|
+
- Encode any `Schema.Class` headers exactly as generated clients receive them.
|
|
11
|
+
- Assert each expected error carries the intended HTTP status and body encoding.
|
|
12
|
+
- Compare or smoke-test generated OpenAPI when the public contract changes.
|
|
13
|
+
|
|
14
|
+
## Server tests
|
|
15
|
+
|
|
16
|
+
- Build every changed group and fail the test if an endpoint handler is missing.
|
|
17
|
+
- Provide deterministic test layers for application services and middleware.
|
|
18
|
+
- Prove each handler exposes only errors declared by its endpoint; exercise
|
|
19
|
+
expected reason mapping and unexpected failure defects separately.
|
|
20
|
+
- Exercise success, declared failure, malformed input, and middleware rejection.
|
|
21
|
+
- Assert cross-field boundary invariants before the service workflow runs.
|
|
22
|
+
- Exercise raw routes separately and prove they enforce middleware-equivalent
|
|
23
|
+
identity/security rules.
|
|
24
|
+
|
|
25
|
+
Use the installed `HttpApiTest` or an in-memory `HttpClient` when available;
|
|
26
|
+
otherwise run the built HTTP application against representative requests. Avoid
|
|
27
|
+
mocking below the contract so heavily that request encoding and response
|
|
28
|
+
decoding are skipped.
|
|
29
|
+
|
|
30
|
+
## Client tests
|
|
31
|
+
|
|
32
|
+
- Derive the client from the same root API used by the server.
|
|
33
|
+
- Provide every `requiredForClient` middleware with
|
|
34
|
+
`HttpApiMiddleware.layerClient` and assert that it transforms the request.
|
|
35
|
+
- Assert params, query, headers, payload, and expected errors at least once for
|
|
36
|
+
every changed request shape.
|
|
37
|
+
- For Atom clients, complete every applicable scenario routed through
|
|
38
|
+
`effect-atom-testing.md`; use a deterministic HTTP layer so request encoding,
|
|
39
|
+
invalidation, and lifecycle remain observable.
|
|
40
|
+
|
|
41
|
+
## Completion matrix
|
|
42
|
+
|
|
43
|
+
Account for every changed endpoint across these columns:
|
|
44
|
+
|
|
45
|
+
| Contract | Middleware | Handler/service | Client/query/mutation | Tests |
|
|
46
|
+
| ------------------------------------------------ | -------------------------------- | -------------------------------- | ---------------------------------------------- | -------------------------------- |
|
|
47
|
+
| Params, query, headers, payload, success, errors | Scope, provided services, errors | Identifier, invariants, workflow | Typed call shape, identity, cache/invalidation | Round-trip and boundary behavior |
|
|
48
|
+
|
|
49
|
+
Completion means every changed endpoint has an entry in every applicable
|
|
50
|
+
column and the repository's formatter, linter, typechecker, and tests pass.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: build-effect-clis
|
|
3
|
+
description: Build and maintain command-line applications entirely with Effect. Use when creating or changing CLI commands, arguments, flags, subcommands, prompts, help, JSON output, dry-run or confirmation flows, platform services, child processes, Node/Bun entrypoints, or CLI integration tests.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Build Effect CLIs
|
|
7
|
+
|
|
8
|
+
Treat a CLI as an Effect application: the `Command` tree owns the user-facing
|
|
9
|
+
contract, handlers adapt decoded input into application workflows, services own
|
|
10
|
+
capabilities, and the executable entrypoint supplies platform Layers and runs
|
|
11
|
+
the program. Do not introduce a separate CLI framework for new command work.
|
|
12
|
+
|
|
13
|
+
Effect CLI and process APIs are version-sensitive. Read the target repository's
|
|
14
|
+
`node_modules/effect/AGENTS.md` completely, follow its CLI and child-process
|
|
15
|
+
references, and confirm exact signatures from the installed declarations before
|
|
16
|
+
editing.
|
|
17
|
+
|
|
18
|
+
## Build the command boundary
|
|
19
|
+
|
|
20
|
+
1. Inventory the existing executable entrypoints, package scripts, command
|
|
21
|
+
tree, shared flags, prompts, application services, platform Layers, output
|
|
22
|
+
modes, and subprocess helpers. Finish when every way to invoke and test the
|
|
23
|
+
CLI is known.
|
|
24
|
+
2. Read [command-design.md](references/command-design.md). Define arguments and
|
|
25
|
+
flags with `Argument` and `Flag`, compose commands with `Command`, and give
|
|
26
|
+
every public input useful help. Use `Effect.fn` handlers and yield the root
|
|
27
|
+
command when a subcommand needs shared parent input.
|
|
28
|
+
3. Keep handlers thin. Decode user and file input at the boundary, enforce
|
|
29
|
+
cross-input invariants, then call an application service. Keep persistence,
|
|
30
|
+
network calls, orchestration, retries, and transactions in services.
|
|
31
|
+
4. Keep expected operational failures typed with `Schema.TaggedError`; map
|
|
32
|
+
platform failures into application-owned errors near the adapter that knows
|
|
33
|
+
what the operation means. Let defects remain defects.
|
|
34
|
+
5. Read [entrypoints-and-testing.md](references/entrypoints-and-testing.md).
|
|
35
|
+
Export the command tree without running it, wire one Node or Bun entrypoint,
|
|
36
|
+
and verify help, parsing, successful execution, expected failure, JSON
|
|
37
|
+
output, and every dry-run or confirmation path.
|
|
38
|
+
|
|
39
|
+
## Optional branches
|
|
40
|
+
|
|
41
|
+
- Read [processes-and-platform.md](references/processes-and-platform.md) when a
|
|
42
|
+
command reads files, inspects the environment, starts child processes,
|
|
43
|
+
streams their output, or differs between Node and Bun.
|
|
44
|
+
- Use `Prompt` only for an intentionally interactive path. Keep required inputs
|
|
45
|
+
expressible as arguments or flags so automation never depends on a terminal.
|
|
46
|
+
- Add `--dry-run` for commands that mutate important state and `--yes` for
|
|
47
|
+
explicitly authorized non-interactive confirmation. Never prompt in JSON or
|
|
48
|
+
CI-oriented modes.
|
|
49
|
+
|
|
50
|
+
## CLI ownership rules
|
|
51
|
+
|
|
52
|
+
- Let `Command`, `Argument`, and `Flag` own syntax, defaults, aliases, examples,
|
|
53
|
+
and help text.
|
|
54
|
+
- Let schemas own untrusted structured input and machine-readable output.
|
|
55
|
+
- Let handlers own CLI-to-application mapping and presentation selection.
|
|
56
|
+
- Let services own reusable behavior and external capabilities.
|
|
57
|
+
- Let Layers own implementations and runtime dependencies.
|
|
58
|
+
- Let the executable entrypoint own `Command.run`, platform provisioning,
|
|
59
|
+
scopes, signal handling, and `NodeRuntime.runMain` or `BunRuntime.runMain`.
|
|
60
|
+
- Keep stdout stable for primary or machine-readable output. Send diagnostics
|
|
61
|
+
and progress elsewhere; never mix prose into JSON output.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Command design
|
|
2
|
+
|
|
3
|
+
Model the CLI's public surface as a typed contract. Prefer a small root command,
|
|
4
|
+
shared parent flags, focused subcommands, and Effect handlers.
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { Effect } from "effect";
|
|
8
|
+
import { Argument, Command, Flag } from "effect/unstable/cli";
|
|
9
|
+
|
|
10
|
+
const workspace = Flag.string("workspace").pipe(
|
|
11
|
+
Flag.withAlias("w"),
|
|
12
|
+
Flag.withDescription("Workspace to operate on"),
|
|
13
|
+
Flag.withDefault("personal"),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const root = Command.make("acme").pipe(
|
|
17
|
+
Command.withSharedFlags({
|
|
18
|
+
workspace,
|
|
19
|
+
verbose: Flag.boolean("verbose").pipe(
|
|
20
|
+
Flag.withAlias("v"),
|
|
21
|
+
Flag.withDescription("Print diagnostic output"),
|
|
22
|
+
),
|
|
23
|
+
}),
|
|
24
|
+
Command.withDescription("Operate Acme projects"),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const deploy = Command.make(
|
|
28
|
+
"deploy",
|
|
29
|
+
{
|
|
30
|
+
environment: Argument.string("environment").pipe(
|
|
31
|
+
Argument.withDescription("Target environment"),
|
|
32
|
+
),
|
|
33
|
+
dryRun: Flag.boolean("dry-run").pipe(
|
|
34
|
+
Flag.withDescription("Show the deployment plan without applying it"),
|
|
35
|
+
),
|
|
36
|
+
json: Flag.boolean("json").pipe(Flag.withDescription("Print machine-readable JSON")),
|
|
37
|
+
},
|
|
38
|
+
Effect.fn("deployCommand")(function* ({ dryRun, environment, json }) {
|
|
39
|
+
const shared = yield* root;
|
|
40
|
+
const deployments = yield* Deployments;
|
|
41
|
+
const plan = yield* deployments.plan({ environment, workspace: shared.workspace });
|
|
42
|
+
|
|
43
|
+
if (dryRun) return yield* renderPlan(plan, { json });
|
|
44
|
+
|
|
45
|
+
const result = yield* deployments.apply(plan);
|
|
46
|
+
return yield* renderDeployment(result, { json });
|
|
47
|
+
}),
|
|
48
|
+
).pipe(
|
|
49
|
+
Command.withDescription("Deploy a workspace"),
|
|
50
|
+
Command.withExamples([
|
|
51
|
+
{
|
|
52
|
+
command: "acme --workspace team deploy production --dry-run",
|
|
53
|
+
description: "Preview a production deployment",
|
|
54
|
+
},
|
|
55
|
+
]),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
export const command = root.pipe(Command.withSubcommands([deploy]));
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Input and help
|
|
62
|
+
|
|
63
|
+
- Use arguments for essential positional identity and flags for optional
|
|
64
|
+
behavior. Prefer named flags when position would be ambiguous.
|
|
65
|
+
- Give public commands, arguments, and non-obvious flags descriptions. Add
|
|
66
|
+
examples for quoting, shared flags, or surprising combinations.
|
|
67
|
+
- Put cross-command inputs in `Command.withSharedFlags`; read them by yielding
|
|
68
|
+
the parent command instead of duplicating parsing or reading globals.
|
|
69
|
+
- Use `Flag.choice` for closed vocabularies and schemas for structured values
|
|
70
|
+
loaded from JSON, files, or environment boundaries.
|
|
71
|
+
- Keep aliases additive and unsurprising. Never give two concepts the same
|
|
72
|
+
short flag in one command path.
|
|
73
|
+
|
|
74
|
+
## Interactive and automated use
|
|
75
|
+
|
|
76
|
+
- Make every required value available non-interactively. A fallback `Prompt`
|
|
77
|
+
may improve terminal use but must not be the only way to supply input.
|
|
78
|
+
- Prompt only after deterministic discovery cannot choose safely. Summarize the
|
|
79
|
+
detected state and the exact mutation before requesting confirmation.
|
|
80
|
+
- Pair destructive execution with a genuine plan/apply split. `--dry-run` must
|
|
81
|
+
run the real discovery and planning logic while skipping writes.
|
|
82
|
+
- Require explicit `--yes` or an equivalent authorization flag when a
|
|
83
|
+
non-interactive destructive path cannot prompt.
|
|
84
|
+
|
|
85
|
+
## Output contract
|
|
86
|
+
|
|
87
|
+
- Treat human and machine output as separate renderers over the same result.
|
|
88
|
+
- In `--json` mode, emit one documented schema-encoded value to stdout. Send no
|
|
89
|
+
headings, spinners, progress, or warnings to stdout.
|
|
90
|
+
- Use stderr for diagnostics and failures. Keep normal human output concise and
|
|
91
|
+
stable enough for users to understand without reading source.
|
|
92
|
+
- Do not expose stack traces for expected failures. Preserve causes for logs and
|
|
93
|
+
tests, then render an actionable message at the executable boundary.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Entrypoints and testing
|
|
2
|
+
|
|
3
|
+
Separate command definition from execution. Importing a command module in a test
|
|
4
|
+
or another program must not parse `process.argv`, start fibers, or terminate the
|
|
5
|
+
process.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
// src/cli/command.ts
|
|
9
|
+
export const command = root.pipe(Command.withSubcommands([deploy, status]));
|
|
10
|
+
|
|
11
|
+
// src/bin/acme.ts
|
|
12
|
+
import { NodeRuntime, NodeServices } from "@effect/platform-node";
|
|
13
|
+
import { Effect } from "effect";
|
|
14
|
+
import { Command } from "effect/unstable/cli";
|
|
15
|
+
import { command } from "../cli/command";
|
|
16
|
+
|
|
17
|
+
const program = Command.run(command, { version: VERSION }).pipe(
|
|
18
|
+
Effect.scoped,
|
|
19
|
+
Effect.provide(ApplicationLive),
|
|
20
|
+
Effect.provide(NodeServices.layer),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
NodeRuntime.runMain(program, { disableErrorReporting: true });
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Use `BunRuntime` and `BunServices` together when Bun owns the executable. Let
|
|
27
|
+
`runMain` own signals, interruption, and process completion. Do not call
|
|
28
|
+
`Effect.runPromise`, `process.exit`, or runtime globals inside command handlers.
|
|
29
|
+
If expected errors need custom presentation, catch and render them immediately
|
|
30
|
+
before `runMain` while preserving CLI control-flow errors such as help output.
|
|
31
|
+
|
|
32
|
+
## Test at three seams
|
|
33
|
+
|
|
34
|
+
1. Test application services directly with deterministic Layers. Cover domain
|
|
35
|
+
success, expected failure, interruption, and plan/apply separation without
|
|
36
|
+
involving argument parsing.
|
|
37
|
+
2. Test thin command handlers through their services when CLI input mapping or
|
|
38
|
+
output-mode selection contains meaningful logic.
|
|
39
|
+
3. Spawn the real executable for boundary behavior. At minimum cover:
|
|
40
|
+
- root and changed-command `--help`;
|
|
41
|
+
- representative valid arguments and flags;
|
|
42
|
+
- missing or invalid input and a non-zero exit;
|
|
43
|
+
- one expected operational failure with an actionable message;
|
|
44
|
+
- exact JSON output with no prose contamination;
|
|
45
|
+
- dry-run proving writes did not occur;
|
|
46
|
+
- confirmation behavior in both interactive and non-interactive modes.
|
|
47
|
+
|
|
48
|
+
Run executable tests through Effect's child-process APIs with an explicit `cwd`,
|
|
49
|
+
captured stdout/stderr, and controlled environment. Use the repository's normal
|
|
50
|
+
runtime launcher and command authority; a test that bypasses the packaged or
|
|
51
|
+
declared entrypoint does not prove the CLI works for users.
|
|
52
|
+
|
|
53
|
+
## Completion checks
|
|
54
|
+
|
|
55
|
+
- Ensure every executable TypeScript entrypoint belongs to a checked project.
|
|
56
|
+
- Run the repository's formatter, linter, typechecker, and tests.
|
|
57
|
+
- Execute `--help` through the real entrypoint.
|
|
58
|
+
- Exercise a harmless dry-run or read-only command outside the source module.
|
|
59
|
+
- Verify non-zero exits for parsing and expected operational failures.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Processes and platform services
|
|
2
|
+
|
|
3
|
+
Use Effect platform services inside CLI workflows. Keep direct `node:*`, Bun
|
|
4
|
+
globals, `process`, filesystem calls, and shell execution at explicit adapters
|
|
5
|
+
or the executable boundary.
|
|
6
|
+
|
|
7
|
+
## Own subprocess behavior in a service
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Context, Effect, Layer, Schema, String } from "effect";
|
|
11
|
+
import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";
|
|
12
|
+
|
|
13
|
+
export class ToolError extends Schema.TaggedError<ToolError>()("ToolError", {
|
|
14
|
+
command: Schema.String,
|
|
15
|
+
cause: Schema.Defect(),
|
|
16
|
+
}) {}
|
|
17
|
+
|
|
18
|
+
export class Tools extends Context.Service<
|
|
19
|
+
Tools,
|
|
20
|
+
{
|
|
21
|
+
readonly gitVersion: Effect.Effect<string, ToolError>;
|
|
22
|
+
changedFiles(baseRef: string): Effect.Effect<ReadonlyArray<string>, ToolError>;
|
|
23
|
+
}
|
|
24
|
+
>()("app/Tools") {
|
|
25
|
+
static readonly layer = Layer.effect(
|
|
26
|
+
Tools,
|
|
27
|
+
Effect.gen(function* () {
|
|
28
|
+
const spawner = yield* ChildProcessSpawner.ChildProcessSpawner;
|
|
29
|
+
const gitVersion = spawner.string(ChildProcess.make("git", ["--version"])).pipe(
|
|
30
|
+
Effect.map(String.trim),
|
|
31
|
+
Effect.mapError((cause) => new ToolError({ command: "git --version", cause })),
|
|
32
|
+
);
|
|
33
|
+
const changedFiles = Effect.fn("Tools.changedFiles")(function* (baseRef: string) {
|
|
34
|
+
return yield* spawner
|
|
35
|
+
.lines(ChildProcess.make("git", ["diff", "--name-only", `${baseRef}...HEAD`]))
|
|
36
|
+
.pipe(
|
|
37
|
+
Effect.mapError(
|
|
38
|
+
(cause) =>
|
|
39
|
+
new ToolError({ command: `git diff --name-only ${baseRef}...HEAD`, cause }),
|
|
40
|
+
),
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
return Tools.of({ changedFiles, gitVersion });
|
|
45
|
+
}),
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Pass the executable and arguments separately to `ChildProcess.make`; never
|
|
51
|
+
construct a shell command from user input. Set `cwd` explicitly when repository
|
|
52
|
+
identity matters. Provide `env` narrowly and choose `extendEnv` deliberately so
|
|
53
|
+
tests and CI do not inherit accidental machine state.
|
|
54
|
+
|
|
55
|
+
Use the smallest spawner operation that matches the contract:
|
|
56
|
+
|
|
57
|
+
- `string` for bounded complete output;
|
|
58
|
+
- `lines` for bounded line-oriented output;
|
|
59
|
+
- `spawn` plus a scoped process handle for streaming, interactive, or
|
|
60
|
+
exit-code-sensitive work;
|
|
61
|
+
- `ChildProcess.pipeTo` for a real pipeline without invoking a shell.
|
|
62
|
+
|
|
63
|
+
When using `spawn`, consume stdout/stderr without deadlocking, inspect
|
|
64
|
+
`handle.exitCode`, and wrap the whole process lifetime in `Effect.scoped`.
|
|
65
|
+
Map platform failures once into an operation-specific error; do not expose a
|
|
66
|
+
generic subprocess error throughout the application.
|
|
67
|
+
|
|
68
|
+
## Keep the runtime choice at the edge
|
|
69
|
+
|
|
70
|
+
Application services may require `FileSystem`, `Path`, `Terminal`, or
|
|
71
|
+
`ChildProcessSpawner`, but they should not import `NodeServices` or `BunServices`.
|
|
72
|
+
Provide the matching platform Layer only in the executable entrypoint. This
|
|
73
|
+
keeps workflows reusable and lets tests provide deterministic substitutes.
|
package/skills/dev-kit/SKILL.md
CHANGED
|
@@ -66,6 +66,18 @@ external source ID is also a family, but expands to every approved skill from
|
|
|
66
66
|
that source, so prefer individually relevant external skills. Include this
|
|
67
67
|
skill as `dev-kit` when project agents should carry the toolkit procedure.
|
|
68
68
|
|
|
69
|
+
Select skills bundled inside installed packages with the exact
|
|
70
|
+
`<package>#<skill>` selector. The selector stays package-qualified in the
|
|
71
|
+
manifest, lock, and CLI listings, and the installed output keeps that identity:
|
|
72
|
+
the copied directory is named by flattening the package name (drop `@`, turn
|
|
73
|
+
every other non-alphanumeric run into one dash) and appending the skill name,
|
|
74
|
+
so `@tanstack/table-core#core` installs as `tanstack-table-core-core`. The
|
|
75
|
+
copied `SKILL.md` frontmatter `name:` is rewritten to the same install name;
|
|
76
|
+
everything else is verbatim. Symlink-mode targets still point at
|
|
77
|
+
`node_modules`, so their frontmatter keeps the upstream bare name. Two selected
|
|
78
|
+
skills that flatten to the same install name are rejected before any output
|
|
79
|
+
changes.
|
|
80
|
+
|
|
69
81
|
```jsonc
|
|
70
82
|
{
|
|
71
83
|
"$schema": "./node_modules/@danieljvdm/dev-kit/schema/dev-kit.schema.json",
|
|
@@ -77,7 +89,6 @@ skill as `dev-kit` when project agents should carry the toolkit procedure.
|
|
|
77
89
|
"vitePlus": {
|
|
78
90
|
"hooks": { "enabled": true },
|
|
79
91
|
"quality": {
|
|
80
|
-
"config": { "enabled": true },
|
|
81
92
|
"workflow": { "enabled": true },
|
|
82
93
|
},
|
|
83
94
|
},
|
|
@@ -105,10 +116,16 @@ useful unified-toolchain overview, help and documentation entry points, and
|
|
|
105
116
|
contradict the repository policy. Treat duplicate, overlapping, reversed, or
|
|
106
117
|
unmatched managed markers as a conflict rather than guessing which content Dev
|
|
107
118
|
Kit owns; remove a legacy owned Vite+ section during migration.
|
|
119
|
+
When `effect` is a declared direct dependency and the installed package ships a
|
|
120
|
+
regular `node_modules/effect/AGENTS.md` file, the managed section also directs
|
|
121
|
+
agents to read that version-matched guide completely and use
|
|
122
|
+
`node_modules/effect/src` for gaps. Omit the pointer when the package is absent,
|
|
123
|
+
transitive, or too old to ship the guide; never generate a dangling path.
|
|
108
124
|
The managed section also publishes the repository's command authority. Direct
|
|
109
|
-
Vite+ projects must use `vp` built-ins and `vp run <task>`;
|
|
110
|
-
|
|
111
|
-
and `vp run typecheck` for the Effect-patched
|
|
125
|
+
Vite+ projects must use `vp` built-ins and `vp run <task>`; projects using the
|
|
126
|
+
recommended Vite+ factory use `vp run check` for the complete
|
|
127
|
+
format/lint/test/typecheck suite and `vp run typecheck` for the Effect-patched
|
|
128
|
+
compiler. Non-Vite+ projects run
|
|
112
129
|
existing root quality scripts through `bun run`; package-manager metadata and
|
|
113
130
|
lockfiles affect dependency-install guidance only. Never substitute another
|
|
114
131
|
script runner or call raw `tsc`, test, lint, or format binaries when a project
|
|
@@ -122,27 +139,31 @@ place.
|
|
|
122
139
|
|
|
123
140
|
Enable `setup.vitePlus.hooks` when an installed direct `vite-plus` dependency
|
|
124
141
|
should manage Git hooks. Each apply checks the local `.vite-hooks/_` dispatcher,
|
|
125
|
-
the portable `.vite-hooks/pre-commit` hook, and
|
|
126
|
-
project-local `vp config --no-agent` when they
|
|
127
|
-
ignored dispatchers in linked worktrees.
|
|
142
|
+
its internal `.gitignore`, the portable `.vite-hooks/pre-commit` hook, and
|
|
143
|
+
`core.hooksPath`, then runs the project-local `vp config --no-agent` when they
|
|
144
|
+
need convergence. This recreates ignored dispatchers in linked worktrees.
|
|
145
|
+
Preserve other hook managers; Dev Kit
|
|
128
146
|
refuses to replace an unrelated `core.hooksPath`. Use `VITE_GIT_HOOKS=0` or
|
|
129
147
|
`HUSKY=0` to skip hook setup for an invocation.
|
|
130
148
|
|
|
131
|
-
|
|
132
|
-
`
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
149
|
+
The repository always owns `vite.config.ts`. Compose
|
|
150
|
+
`createRecommendedVitePlusConfig` from `@danieljvdm/dev-kit/vite-plus` there;
|
|
151
|
+
Dev Kit never adopts, rewrites, or removes the config. The factory provides
|
|
152
|
+
matching Oxlint/Oxfmt ignores, staged checks, and separate `check` and pure
|
|
153
|
+
`typecheck` Vite tasks. Add repository/framework-generated paths through its
|
|
154
|
+
`ignorePatterns` option, including any harness target paths that override the
|
|
155
|
+
manifest defaults. Workspace mode accepts explicit package directories with
|
|
156
|
+
pure `typecheck` scripts and generates cached, dependency-ordered,
|
|
157
|
+
bounded-concurrency filters. Spread the returned top-level config before local
|
|
158
|
+
options; spread a returned nested block before overriding that block, and merge
|
|
159
|
+
nested collections such as `lint.rules` so the recommended rules remain active.
|
|
160
|
+
|
|
161
|
+
Enable `setup.vitePlus.quality.workflow` to own only
|
|
162
|
+
`.github/workflows/check.yml`. It requires direct Dev Kit, compatible Vite+,
|
|
163
|
+
Effect, Effect TypeScript-Go, and native TypeScript dependencies with
|
|
164
|
+
`setup.effectTsgo.enabled`. Preserve unowned workflows and adopt only an exact
|
|
165
|
+
rendered match. Consumers may configure `workflow.beforeChecks` and
|
|
166
|
+
`workflow.typecheck`; treat these commands as trusted manifest input.
|
|
146
167
|
|
|
147
168
|
The workflow must use one frozen, script-suppressed install, then locked Dev Kit
|
|
148
169
|
convergence before preparation or checks. Set up Bun from the consumer's
|
|
@@ -234,39 +255,42 @@ Enable the setup task in the same manifest:
|
|
|
234
255
|
Install the exact `@effect/tsgo` and native `typescript` versions required by
|
|
235
256
|
the installed dev-kit. Point `tsconfig.json` at
|
|
236
257
|
`./node_modules/@effect/tsgo/schema.json` and configure the
|
|
237
|
-
`@effect/language-service` compiler plugin
|
|
238
|
-
|
|
258
|
+
`@effect/language-service` compiler plugin with Dev Kit's exported
|
|
259
|
+
`recommendedEffectTsgoPlugin` profile: warnings for
|
|
260
|
+
`anyUnknownInErrorContext` and `unsafeEffectTypeAssertion`; suggestions for
|
|
261
|
+
`instanceOfSchema`, `nestedEffectGenYield`, `newSchemaClass`, and
|
|
262
|
+
`preferSchemaTypeProperty`; plus a `src/**/*.ts` override that warns on
|
|
263
|
+
`nodeBuiltinImport` and suggests `preferSchemaOverJson`. Copy the exact JSON
|
|
264
|
+
profile from the README into JSON tsconfigs. In monorepos, child
|
|
265
|
+
`compilerOptions.plugins` arrays replace rather than merge the root array, so
|
|
266
|
+
workspace configs must inherit the root plugin without redeclaring it and the
|
|
267
|
+
source override must be relative to the config that contains it. `dev-kit plan`
|
|
268
|
+
validates the local dependencies; `dev-kit apply` patches once and then
|
|
269
|
+
converges.
|
|
239
270
|
|
|
240
271
|
Use `dev-kit tsgo patch --dry-run` for focused diagnosis. Use `--force` only
|
|
241
272
|
after the user accepts a potentially commit-incompatible TypeScript binary.
|
|
242
273
|
|
|
243
274
|
## Oxlint and Oxfmt configurations
|
|
244
275
|
|
|
245
|
-
Use Dev Kit's
|
|
276
|
+
Use Dev Kit's composable factory in Vite+ projects:
|
|
246
277
|
|
|
247
278
|
```ts
|
|
248
|
-
import {
|
|
249
|
-
import { recommendedOxfmtConfig } from "@danieljvdm/dev-kit/oxfmt";
|
|
279
|
+
import { createRecommendedVitePlusConfig } from "@danieljvdm/dev-kit/vite-plus";
|
|
250
280
|
import { defineConfig } from "vite-plus";
|
|
251
281
|
|
|
252
|
-
export default defineConfig(
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
},
|
|
256
|
-
|
|
257
|
-
extends: [recommendedOxlintConfig],
|
|
258
|
-
rules: {
|
|
259
|
-
// Project-specific rules apply after the shared preset.
|
|
260
|
-
},
|
|
261
|
-
},
|
|
262
|
-
});
|
|
282
|
+
export default defineConfig(
|
|
283
|
+
createRecommendedVitePlusConfig({
|
|
284
|
+
ignorePatterns: ["src/routeTree.gen.ts"],
|
|
285
|
+
}),
|
|
286
|
+
);
|
|
263
287
|
```
|
|
264
288
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
`extends: [recommendedOxlintConfig]`; standalone `oxfmt.config.ts` spreads
|
|
269
|
-
|
|
289
|
+
The factory composes the canonical Oxlint/Oxfmt objects, excludes tracked skill
|
|
290
|
+
copies plus symlinked harness targets from both tools, and accepts additional
|
|
291
|
+
project-owned ignores. Standalone `oxlint.config.ts` uses
|
|
292
|
+
`extends: [recommendedOxlintConfig]`; standalone `oxfmt.config.ts` spreads
|
|
293
|
+
`recommendedOxfmtConfig`. The shared lint preset enables `typeAware` for
|
|
270
294
|
semantic lint rules but leaves `typeCheck` disabled. Effect TypeScript-Go
|
|
271
295
|
projects must run the patched native compiler separately with
|
|
272
296
|
`vp run typecheck` after `vp fmt --check`, `vp lint`, and `vp test`; Oxlint's
|
|
@@ -277,6 +301,12 @@ The Oxlint preset enables the fixable
|
|
|
277
301
|
declarations grouped, requires a blank line before the next logical statement,
|
|
278
302
|
and separates every `return` statement from the preceding statement.
|
|
279
303
|
|
|
304
|
+
Vite+ 0.2.6 forwards the preset's JavaScript-plugin declarations but its native
|
|
305
|
+
Oxlint path does not register or execute their rules. Treat native rules and
|
|
306
|
+
Oxfmt as active through `vp`, and use standalone Oxlint when the `effect/*` or
|
|
307
|
+
`stylistic/padding-line-between-statements` rules must be enforced. Re-enable a
|
|
308
|
+
Vite+ execution assertion when a supported release adds JS-plugin execution.
|
|
309
|
+
|
|
280
310
|
The Oxlint preset registers Dev Kit's shared Effect plugin as `effect`, but
|
|
281
311
|
does not enable its scope-sensitive rules globally. Effect projects should
|
|
282
312
|
enable rules such as `effect/no-effect-run`, `effect/no-unsafe-promise`, and
|
|
@@ -290,8 +320,7 @@ in the consuming project.
|
|
|
290
320
|
|
|
291
321
|
Manage skill outputs, the `setup.agentInstructions` marked sections, the
|
|
292
322
|
`setup.claudeInstructions` link, the `setup.vitePlus.hooks` dispatcher, the
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
manually, leave managed config disabled, and may still use the managed workflow.
|
|
323
|
+
opt-in `setup.vitePlus.quality.workflow`, the `setup.effectSource` checkout,
|
|
324
|
+
and the explicit `setup.effectTsgo` task. `vite.config.ts`, dependency, and
|
|
325
|
+
`tsconfig.json` contributions remain deliberate user-owned edits. Compose the
|
|
326
|
+
Vite+ factory or lower-level Oxlint/Oxfmt exports locally.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: effect-architecture-audit
|
|
3
|
+
description: Audit an Effect TypeScript codebase's services, Layers, dependency ownership, hidden requirements, test substitutes, Schema boundaries, assertions, unknown values, and typed error channels. Use for architecture reviews, service refactors, dependency-injection audits, and type-boundary investigations; do not use as a general Effect API reference.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Audit Effect Architecture
|
|
7
|
+
|
|
8
|
+
Read the target repository's `node_modules/effect/AGENTS.md` completely before
|
|
9
|
+
evaluating Effect code. Follow its version-matched references for library APIs;
|
|
10
|
+
use this skill only for the application-architecture judgments it does not own.
|
|
11
|
+
|
|
12
|
+
Read [service-and-boundary-audit.md](references/service-and-boundary-audit.md),
|
|
13
|
+
then:
|
|
14
|
+
|
|
15
|
+
1. Establish the repository's local architecture and testing rules.
|
|
16
|
+
2. Inventory every service, Layer, dependency path, runtime authority, test
|
|
17
|
+
substitute, and unsafe type boundary in scope.
|
|
18
|
+
3. Trace each capability to its owner and composition root.
|
|
19
|
+
4. Classify each candidate as a built-in capability, application authority,
|
|
20
|
+
technology adapter, explicit value, framework boundary, or pass-through
|
|
21
|
+
abstraction.
|
|
22
|
+
5. Report evidence-backed findings and explicit keep decisions. Do not propose
|
|
23
|
+
a wrapper, service, or Schema merely to make the inventory symmetrical.
|
|
24
|
+
|
|
25
|
+
When asked only to audit or review, stop after the findings. Implement changes
|
|
26
|
+
only when the user also requests them.
|