@danieljvdm/dev-kit 0.12.0 → 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 CHANGED
@@ -199,8 +199,10 @@ tool versions. A project-local process lock also prevents concurrent applies.
199
199
 
200
200
  - `dev-kit` installs guidance for operating the toolkit itself.
201
201
  - `effect` expands to the package-guidance `effect-ts` bootstrap, the
202
- opinionated `effect-architecture-audit`, and `build-effect-apis` for shared
203
- HTTP contracts, Effect Atom clients, TanStack Start, and Cloudflare Workers.
202
+ opinionated `effect-architecture-audit`, `build-effect-apis` for shared HTTP
203
+ contracts and clients, and `build-effect-clis` for typed command-line
204
+ applications. The focused references cover Effect Atom, TanStack Start,
205
+ Cloudflare Workers, child processes, runtime entrypoints, and CLI testing.
204
206
  - Prefer individual external skills such as `workers-best-practices` and
205
207
  `wrangler`, selected after scanning the project for relevant technologies.
206
208
  - `serve-sim` selects the approved Evan Bacon simulator skill directly.
@@ -305,7 +307,9 @@ export default defineConfig({
305
307
 
306
308
  Spread the returned top-level config before local options. When overriding a
307
309
  `fmt`, `lint`, `run`, or `staged` block, spread that returned block as well so
308
- its defaults remain composed.
310
+ its defaults remain composed. Merge nested collections too; for example, a
311
+ local lint rule block starts with `...recommended.lint.rules` before adding
312
+ repository-specific rules.
309
313
 
310
314
  The factory configures `vp staged`, matching Oxlint/Oxfmt ignores for Dev Kit's
311
315
  tool-owned paths, and separate `vp run check` and pure `vp run typecheck` tasks.
@@ -313,6 +317,13 @@ Project and framework-generated paths belong in `ignorePatterns` as shown;
313
317
  custom harness target paths belong there too. Dev Kit does not grow a global
314
318
  framework ignore list.
315
319
 
320
+ Vite+ 0.2.6 forwards JavaScript-plugin declarations into its effective lint
321
+ config but its bundled native Oxlint path does not register or execute those
322
+ rules. Native Oxlint rules and Oxfmt settings remain active; run standalone
323
+ Oxlint when enforcement of Dev Kit's `effect/*` or
324
+ `stylistic/padding-line-between-statements` rules is required. This limitation
325
+ can be removed once a supported Vite+ release executes configured JS plugins.
326
+
316
327
  ```jsonc
317
328
  {
318
329
  "include": ["dev-kit", "effect"],
@@ -439,7 +450,30 @@ Pin the compatible packages in the consuming project:
439
450
  {
440
451
  "$schema": "./node_modules/@effect/tsgo/schema.json",
441
452
  "compilerOptions": {
442
- "plugins": [{ "name": "@effect/language-service" }],
453
+ "plugins": [
454
+ {
455
+ "name": "@effect/language-service",
456
+ "diagnosticSeverity": {
457
+ "anyUnknownInErrorContext": "warning",
458
+ "instanceOfSchema": "suggestion",
459
+ "nestedEffectGenYield": "suggestion",
460
+ "newSchemaClass": "suggestion",
461
+ "preferSchemaTypeProperty": "suggestion",
462
+ "unsafeEffectTypeAssertion": "warning",
463
+ },
464
+ "overrides": [
465
+ {
466
+ "include": ["src/**/*.ts"],
467
+ "options": {
468
+ "diagnosticSeverity": {
469
+ "nodeBuiltinImport": "warning",
470
+ "preferSchemaOverJson": "suggestion",
471
+ },
472
+ },
473
+ },
474
+ ],
475
+ },
476
+ ],
443
477
  },
444
478
  }
445
479
  ```
@@ -449,7 +483,12 @@ native TypeScript compiler. It does not download dependencies and skips an
449
483
  installation that is already patched. Use `dev-kit tsgo patch --dry-run` when
450
484
  troubleshooting the task directly.
451
485
 
452
- Dependency and `tsconfig.json` edits remain explicit.
486
+ The same typed object is exported as `recommendedEffectTsgoPlugin` for
487
+ programmatic configuration tooling. Dependency and `tsconfig.json` edits remain
488
+ explicit. In a monorepo, put the plugin in the shared root config and ensure
489
+ every workspace extends it without redeclaring `compilerOptions.plugins`:
490
+ TypeScript replaces that array in child configs rather than merging it. Adjust
491
+ the `src/**/*.ts` override to the source layout seen from each config file.
453
492
 
454
493
  ## Installed package skills
455
494
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danieljvdm/dev-kit",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "private": false,
5
5
  "description": "Declarative project development toolkit with portable agent skills.",
6
6
  "license": "MIT",
@@ -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,4 @@
1
+ interface:
2
+ display_name: "Build Effect CLIs"
3
+ short_description: "Build typed Effect command-line applications"
4
+ default_prompt: "Use $build-effect-clis to build a typed Effect CLI with clear commands, platform boundaries, and tests."
@@ -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.
@@ -155,7 +155,8 @@ matching Oxlint/Oxfmt ignores, staged checks, and separate `check` and pure
155
155
  manifest defaults. Workspace mode accepts explicit package directories with
156
156
  pure `typecheck` scripts and generates cached, dependency-ordered,
157
157
  bounded-concurrency filters. Spread the returned top-level config before local
158
- options; spread a returned nested block before overriding that block.
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.
159
160
 
160
161
  Enable `setup.vitePlus.quality.workflow` to own only
161
162
  `.github/workflows/check.yml`. It requires direct Dev Kit, compatible Vite+,
@@ -254,8 +255,18 @@ Enable the setup task in the same manifest:
254
255
  Install the exact `@effect/tsgo` and native `typescript` versions required by
255
256
  the installed dev-kit. Point `tsconfig.json` at
256
257
  `./node_modules/@effect/tsgo/schema.json` and configure the
257
- `@effect/language-service` compiler plugin. `dev-kit plan` validates these local
258
- dependencies; `dev-kit apply` patches once and then converges.
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.
259
270
 
260
271
  Use `dev-kit tsgo patch --dry-run` for focused diagnosis. Use `--force` only
261
272
  after the user accepts a potentially commit-incompatible TypeScript binary.
@@ -290,6 +301,12 @@ The Oxlint preset enables the fixable
290
301
  declarations grouped, requires a blank line before the next logical statement,
291
302
  and separates every `return` statement from the preceding statement.
292
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
+
293
310
  The Oxlint preset registers Dev Kit's shared Effect plugin as `effect`, but
294
311
  does not enable its scope-sensitive rules globally. Effect projects should
295
312
  enable rules such as `effect/no-effect-run`, `effect/no-unsafe-promise`, and
package/src/catalog.ts CHANGED
@@ -181,7 +181,7 @@ export const loadSkillCatalog = Effect.fn("loadSkillCatalog")(function* (
181
181
  });
182
182
  }
183
183
  const families: Readonly<Record<string, ReadonlyArray<string>>> = {
184
- effect: ["effect-ts", "effect-architecture-audit", "build-effect-apis"],
184
+ effect: ["effect-ts", "effect-architecture-audit", "build-effect-apis", "build-effect-clis"],
185
185
  ...Object.fromEntries(externalFamilies),
186
186
  };
187
187
 
@@ -9,6 +9,43 @@ export const EFFECT_TSGO_VERSION = "0.33.0";
9
9
  export const EFFECT_TSGO_TYPESCRIPT_VERSION = "7.0.2";
10
10
  export const EFFECT_TSGO_PLUGIN_NAME = "@effect/language-service";
11
11
 
12
+ export type EffectTsgoDiagnosticSeverity = "off" | "error" | "warning" | "message" | "suggestion";
13
+
14
+ export type EffectTsgoPluginConfig = {
15
+ readonly name: typeof EFFECT_TSGO_PLUGIN_NAME;
16
+ readonly diagnosticSeverity: Readonly<Record<string, EffectTsgoDiagnosticSeverity>>;
17
+ readonly overrides: ReadonlyArray<{
18
+ readonly include: ReadonlyArray<string>;
19
+ readonly options: {
20
+ readonly diagnosticSeverity: Readonly<Record<string, EffectTsgoDiagnosticSeverity>>;
21
+ };
22
+ }>;
23
+ };
24
+
25
+ /** Recommended diagnostics for projects using the Effect TypeScript-Go plugin. */
26
+ export const recommendedEffectTsgoPlugin = {
27
+ name: EFFECT_TSGO_PLUGIN_NAME,
28
+ diagnosticSeverity: {
29
+ anyUnknownInErrorContext: "warning",
30
+ instanceOfSchema: "suggestion",
31
+ nestedEffectGenYield: "suggestion",
32
+ newSchemaClass: "suggestion",
33
+ preferSchemaTypeProperty: "suggestion",
34
+ unsafeEffectTypeAssertion: "warning",
35
+ },
36
+ overrides: [
37
+ {
38
+ include: ["src/**/*.ts"],
39
+ options: {
40
+ diagnosticSeverity: {
41
+ nodeBuiltinImport: "warning",
42
+ preferSchemaOverJson: "suggestion",
43
+ },
44
+ },
45
+ },
46
+ ],
47
+ } as const satisfies EffectTsgoPluginConfig;
48
+
12
49
  export type EffectTsgoPatchOptions = {
13
50
  readonly dryRun?: boolean;
14
51
  readonly force?: boolean;
package/src/index.ts CHANGED
@@ -43,13 +43,16 @@ export {
43
43
  EFFECT_TSGO_PLUGIN_NAME,
44
44
  EFFECT_TSGO_TYPESCRIPT_VERSION,
45
45
  EFFECT_TSGO_VERSION,
46
+ type EffectTsgoDiagnosticSeverity,
46
47
  EffectTsgoDependencyError,
47
48
  InvalidEffectTsgoPackageNameError,
48
49
  type EffectTsgoPatchOptions,
49
50
  type EffectTsgoPatchPlan,
51
+ type EffectTsgoPluginConfig,
50
52
  EffectTsgoPatchCommandError,
51
53
  patchEffectTsgo,
52
54
  planEffectTsgoPatch,
55
+ recommendedEffectTsgoPlugin,
53
56
  } from "./effect-tsgo.ts";
54
57
  export {
55
58
  ExternalSkillSourceSchema,
@@ -143,9 +143,9 @@ const readManifest = Effect.fn("readManagedSkillManifest")(function* (
143
143
  if (errors.length > 0) {
144
144
  return yield* SkillManagerError.make({ message: `could not parse ${paths.manifestPath}` });
145
145
  }
146
- const manifest = yield* Schema.decodeUnknownEffect(DevKitManifestSchema)(parsed).pipe(
147
- Effect.mapError((error) => SkillManagerError.make({ message: error.message })),
148
- );
146
+ const manifest = yield* Schema.decodeUnknownEffect(DevKitManifestSchema, {
147
+ onExcessProperty: "error",
148
+ })(parsed).pipe(Effect.mapError((error) => SkillManagerError.make({ message: error.message })));
149
149
 
150
150
  return { ...paths, manifest, raw };
151
151
  });
package/src/sync.ts CHANGED
@@ -270,7 +270,7 @@ const encodePlanSnapshotJson = Schema.encodeSync(Schema.fromJsonString(Schema.Un
270
270
  const encodeAppliedStatePrettyJson = Schema.encodeSync(fromJsonString(AppliedStateSchema, 2));
271
271
 
272
272
  const SKILL_FAMILIES: SkillCatalog = {
273
- effect: ["effect-ts", "effect-architecture-audit", "build-effect-apis"],
273
+ effect: ["effect-ts", "effect-architecture-audit", "build-effect-apis", "build-effect-clis"],
274
274
  };
275
275
 
276
276
  export const DEFAULT_MANIFEST = "dev-kit.jsonc";
@@ -564,6 +564,7 @@ const parseStructuredFile = Effect.fn("parseStructuredFile")(function* <A>(
564
564
  filePath: string,
565
565
  raw: string,
566
566
  schema: Schema.ConstraintDecoder<A>,
567
+ options: { readonly rejectExcessProperties?: boolean } = {},
567
568
  ) {
568
569
  const errors: Array<ParseError> = [];
569
570
  const parsed = parseJsonc(raw, errors, { allowTrailingComma: true });
@@ -576,7 +577,10 @@ const parseStructuredFile = Effect.fn("parseStructuredFile")(function* <A>(
576
577
  });
577
578
  }
578
579
 
579
- return yield* Schema.decodeUnknownEffect(schema)(parsed).pipe(
580
+ return yield* Schema.decodeUnknownEffect(
581
+ schema,
582
+ options.rejectExcessProperties ? { onExcessProperty: "error" } : undefined,
583
+ )(parsed).pipe(
580
584
  Effect.mapError((cause) =>
581
585
  StructuredFileError.make({ path: filePath, message: cause.message }),
582
586
  ),
@@ -591,7 +595,9 @@ const readManifest = Effect.fn("readManifest")(function* (manifestPath: string)
591
595
  }
592
596
  const raw = yield* fs.readFileString(manifestPath);
593
597
 
594
- return yield* parseStructuredFile(manifestPath, raw, DevKitManifestSchema);
598
+ return yield* parseStructuredFile(manifestPath, raw, DevKitManifestSchema, {
599
+ rejectExcessProperties: true,
600
+ });
595
601
  });
596
602
 
597
603
  const readOptionalStructuredFile = Effect.fn("readOptionalStructuredFile")(function* <A>(
@@ -1752,7 +1758,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
1752
1758
  });
1753
1759
  const stageDir = path.join(tempDir, "stage");
1754
1760
  const backupDir = path.join(tempDir, "backup");
1755
- const stagedByResource = new Map<string, string>();
1761
+ const stagedByAction = new Map<(typeof mutating)[number], string>();
1756
1762
  let stageIndex = 0;
1757
1763
 
1758
1764
  for (const action of mutating) {
@@ -1816,10 +1822,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
1816
1822
  });
1817
1823
  }
1818
1824
  }
1819
- stagedByResource.set(
1820
- action.action === "remove" ? action.previous.resourceId : action.desired.resourceId,
1821
- staged,
1822
- );
1825
+ stagedByAction.set(action, staged);
1823
1826
  }
1824
1827
 
1825
1828
  yield* verifyPackageSkillSources(plan);
@@ -1841,10 +1844,7 @@ const applyPlannedSkillChanges = Effect.fn("applyPlannedSkillChanges")(function*
1841
1844
  let replacementIndex = 0;
1842
1845
 
1843
1846
  for (const action of mutating) {
1844
- const staged =
1845
- action.action === "remove"
1846
- ? stagedByResource.get(action.previous.resourceId)
1847
- : stagedByResource.get(action.desired.resourceId);
1847
+ const staged = stagedByAction.get(action);
1848
1848
 
1849
1849
  if (
1850
1850
  (action.action !== "remove" || action.stagedContent !== undefined) &&
package/src/vite-plus.js CHANGED
@@ -68,7 +68,7 @@ export const createRecommendedVitePlusConfig = (options = {}) => {
68
68
  ignorePatterns,
69
69
  },
70
70
  lint: {
71
- extends: [recommendedOxlintConfig],
71
+ ...recommendedOxlintConfig,
72
72
  ignorePatterns,
73
73
  },
74
74
  run: {
package/src/vite-plus.ts CHANGED
@@ -89,7 +89,7 @@ export const createRecommendedVitePlusConfig = (options: RecommendedVitePlusConf
89
89
  ignorePatterns,
90
90
  },
91
91
  lint: {
92
- extends: [recommendedOxlintConfig],
92
+ ...recommendedOxlintConfig,
93
93
  ignorePatterns,
94
94
  },
95
95
  run: {