@savvy-web/silk-effects 4.2.5 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +73 -192
  2. package/changesets/api/changelog.js +1 -2
  3. package/changesets/changelog/index.js +8 -10
  4. package/changesets/errors.js +0 -1
  5. package/changesets/index.js +1 -4
  6. package/changesets/services/changelog.js +1 -1
  7. package/changesets/services/config-inspector.js +36 -17
  8. package/changesets/services/github.js +1 -1
  9. package/changesets/utils/dependency-table.js +1 -1
  10. package/index.d.ts +95 -1398
  11. package/index.js +1 -20
  12. package/lint/cli/sections.js +9 -8
  13. package/package.json +7 -5
  14. package/schemas/SavvySections.js +26 -9
  15. package/schemas/VersioningSchemas.js +1 -32
  16. package/schemas/WorkspaceAnalysisSchemas.js +4 -5
  17. package/services/SilkPublishability.js +1 -1
  18. package/services/SilkWorkspaceAnalyzer.js +13 -16
  19. package/turbo/services/TurboInspector.js +11 -11
  20. package/changesets/services/markdown.js +0 -93
  21. package/errors/SectionParseError.js +0 -17
  22. package/errors/SectionValidationError.js +0 -17
  23. package/errors/SectionWriteError.js +0 -17
  24. package/errors/TagFormatError.js +0 -21
  25. package/errors/ToolNotFoundError.js +0 -12
  26. package/errors/ToolResolutionError.js +0 -12
  27. package/errors/ToolVersionMismatchError.js +0 -12
  28. package/errors/VersioningDetectionError.js +0 -21
  29. package/schemas/CommentStyle.js +0 -17
  30. package/schemas/ResolvedTool.js +0 -99
  31. package/schemas/SectionBlock.js +0 -71
  32. package/schemas/SectionDefinition.js +0 -123
  33. package/schemas/SectionResults.js +0 -21
  34. package/schemas/TagStrategySchemas.js +0 -19
  35. package/schemas/ToolDefinition.js +0 -40
  36. package/schemas/ToolResults.js +0 -28
  37. package/services/ManagedSection.js +0 -289
  38. package/services/TagStrategy.js +0 -56
  39. package/services/ToolDiscovery.js +0 -232
  40. package/services/VersioningStrategy.js +0 -69
  41. package/utils/ToolCommand.js +0 -67
package/README.md CHANGED
@@ -3,27 +3,28 @@
3
3
  [![npm](https://img.shields.io/npm/v/@savvy-web%2Fsilk-effects?label=npm&color=cb3837)](https://www.npmjs.com/package/@savvy-web/silk-effects)
4
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-4caf50.svg)](https://opensource.org/licenses/MIT)
5
5
 
6
- Shared [Effect](https://effect.website/) library providing Silk Suite conventions for publishability detection, versioning strategy, tag formatting, managed file sections, config discovery, Biome schema synchronization and CLI tool resolution. Platform-agnostic — consumers provide their own runtime layer (`NodeContext`, `BunContext`, etc.).
6
+ Shared [Effect](https://effect.website/) library providing Silk Suite conventions for publishability detection, changeset config access, release planning, config discovery, Biome schema synchronization and read-only Turborepo inspection. Platform-agnostic — consumers provide their own platform layer (`NodeServices.layer` from `@effect/platform-node`, or the equivalent for their runtime).
7
7
 
8
8
  ## Features
9
9
 
10
- - Detect a package's publish targets from its `package.json` `publishConfig`, with multi-registry support and a changeset-ignore-aware override for `workspaces-effect`'s `PublishabilityDetector`
10
+ - Detect a package's publish targets from its `package.json` `publishConfig`, with multi-registry support and a changeset-ignore-aware override for `@effected/workspaces`'s `PublishabilityDetector`
11
11
  - Read changeset config through a typed accessor service that reports silk vs vanilla mode, ignore patterns and fixed groups
12
- - Manage tool-owned sections in user-editable files without clobbering surrounding content, including ordered multi-section sync for composing several managed regions per file
13
- - Discover and resolve CLI tools globally or locally with version enforcement and caching
14
- - Detect versioning strategy and format git tags from changeset configuration
15
- - Locate config files and keep Biome schema URLs in sync across workspaces
12
+ - Plan, preview and apply a release over the genuine changesets engine, lint changeset files against the section-aware Silk rules and regenerate pure dependency changesets from a branch diff
13
+ - Supply the Silk commitlint config, its prompt and formatter, and the `silk/body-no-markdown` rule
14
+ - Run the lint-staged handlers and the `savvy lint fmt` formatters from a single implementation so the hook and the CLI cannot drift
16
15
  - Inspect a Turborepo read-only — diagnose per-package cache hits, derive the task graph and compute affected packages, all over `turbo --dry`
16
+ - Manage the vendored reference repos declared in `.repos/config.json`, including submodule sync, pinning and drift reporting
17
+ - Locate config files and keep Biome schema URLs in sync across workspaces
17
18
 
18
19
  ## Install
19
20
 
20
21
  ```bash
21
- npm install @savvy-web/silk-effects effect @effect/platform @effect/platform-node
22
+ npm install @savvy-web/silk-effects effect @effect/platform-node
22
23
  # or
23
- pnpm add @savvy-web/silk-effects effect @effect/platform @effect/platform-node
24
+ pnpm add @savvy-web/silk-effects effect @effect/platform-node
24
25
  ```
25
26
 
26
- `effect` and `@effect/platform` are peer dependencies. Install a platform package (`@effect/platform-node`, `@effect/platform-bun`) matching your runtime.
27
+ `effect` is a peer dependency. Install the platform package matching your runtime — `@effect/platform-node` for Node.js — to satisfy the `FileSystem` and process services the file-reading services need.
27
28
 
28
29
  ## Quick start
29
30
 
@@ -32,8 +33,8 @@ All exports come from the package root:
32
33
  ```typescript
33
34
  import {
34
35
  SilkPublishability,
35
- ManagedSection, ManagedSectionLive, SectionDefinition,
36
- ToolDiscovery, ToolDiscoveryLive, ToolDefinition,
36
+ ChangesetConfig, ChangesetConfigLive,
37
+ Changesets, Lint, Turbo,
37
38
  } from "@savvy-web/silk-effects";
38
39
  ```
39
40
 
@@ -66,7 +67,7 @@ These services are pure logic — no filesystem or shell access needed.
66
67
 
67
68
  #### SilkPublishability
68
69
 
69
- Apply silk publishability rules to a raw `package.json` and the bundler's resolved target binding, and resolve the publish targets. Targets are `PublishTarget` records from `workspaces-effect` with `name`, `registry`, `directory`, `access` and `provenance` fields. The static `detect` helper is pure; `resolveTargets` and `listPublishable` are Effects that read from disk (see below), and `readTargetsBinding` reads the binding `detect` consumes.
70
+ Apply silk publishability rules to a raw `package.json` and the bundler's resolved target binding, and resolve the publish targets. Targets are `PublishTarget` records from `@effected/workspaces` with `name`, `registry`, `directory`, `access` and `provenance` fields. The static `detect` helper is pure; `resolveTargets` and `listPublishable` are Effects that read from disk (see below), and `readTargetsBinding` reads the binding `detect` consumes.
70
71
 
71
72
  In silk mode `private: true` is the norm on workspace `package.json` files. Publishability is derived from `publishConfig`, with the `private` flag consulted only as a last-resort default. Publish targets are declared as the bundler's keyed `publishConfig.targets` map; the legacy array form is no longer supported.
72
73
 
@@ -90,43 +91,39 @@ const none = SilkPublishability.detect("@my-org/internal", { private: true }, nu
90
91
  // => []
91
92
  ```
92
93
 
93
- See [Publishability](./docs/publishability.md) for the full rule order and the disk-reading helpers.
94
+ See [Publishability](./docs/03-publishability.md) for the full rule order and the disk-reading helpers.
94
95
 
95
- #### TagStrategy
96
+ #### Tags and versioning classification
96
97
 
97
- Determine git-tag naming strategy and format tag strings. Strategy is `"single"` (one publishable package, tags like `1.2.3`) or `"scoped"` (multiple packages, tags like `@scope/pkg@1.2.3`). Tag format follows strict SemVer 2.0.0 with no `v` prefix.
98
+ Git-tag naming and versioning classification live upstream in `@effected/workspaces` as pure value classes `ReleaseTag`, `TrackingTag`, `classifyTag` and `VersioningStrategy` so there is no service, no layer and no error channel.
98
99
 
99
100
  ```typescript
100
- import { Effect } from "effect";
101
- import { TagStrategy, TagStrategyLive } from "@savvy-web/silk-effects";
101
+ import { VersioningStrategy } from "@effected/workspaces";
102
102
 
103
- const tag = await Effect.runPromise(
104
- Effect.gen(function* () {
105
- const ts = yield* TagStrategy;
106
- const strategy = yield* ts.determine(versioningResult);
107
- return yield* ts.formatTag("@savvy-web/silk-effects", "1.0.0", strategy);
108
- }).pipe(Effect.provide(TagStrategyLive)),
109
- );
110
- // => "@savvy-web/silk-effects@1.0.0"
103
+ const strategy = VersioningStrategy.classify({ packages: publishablePackages, fixedGroups });
104
+ const tags = strategy.tagsFor([{ name: "@savvy-web/silk-effects", version: "1.0.0" }]);
105
+ // => [ReleaseTag { value: "@savvy-web/silk-effects@1.0.0" }]
111
106
  ```
112
107
 
108
+ `WorkspaceAnalysis.versioning` and `WorkspaceAnalysis.tagStrategy` carry those kit types directly.
109
+
113
110
  ---
114
111
 
115
112
  ### FileSystem layer required
116
113
 
117
- These services read or write files. Provide a platform layer such as `NodeContext.layer` or `BunContext.layer`.
114
+ These services read or write files. Provide the platform layer for your runtime, such as `NodeServices.layer`.
118
115
 
119
116
  #### SilkPublishabilityDetectorLive and PublishabilityDetectorAdaptiveLive
120
117
 
121
- `SilkPublishability.detect` is also exposed through `workspaces-effect`'s `PublishabilityDetector` Tag so consumers can swap silk rules into any program that already yields the detector. Two layers override the Tag:
118
+ `SilkPublishability.detect` is also exposed through `@effected/workspaces`'s `PublishabilityDetector` Tag so consumers can swap silk rules into any program that already yields the detector. Two layers override the Tag:
122
119
 
123
120
  - `SilkPublishabilityDetectorLive` — applies silk rules unconditionally. Requires `FileSystem`.
124
- - `PublishabilityDetectorAdaptiveLive` — ignore-aware. Changeset-`ignore`d packages resolve to `[]`, then it dispatches by changeset mode (`none` → `[]`, `silk` → silk rules, `vanilla` → the `workspaces-effect` default). Requires `FileSystem` and `ChangesetConfig`.
121
+ - `PublishabilityDetectorAdaptiveLive` — ignore-aware. Changeset-`ignore`d packages resolve to `[]`, then it dispatches by changeset mode (`none` → `[]`, `silk` → silk rules, `vanilla` → the `@effected/workspaces` default). Requires `FileSystem` and `ChangesetConfig`.
125
122
 
126
123
  ```typescript
127
124
  import { Effect } from "effect";
128
- import { NodeContext } from "@effect/platform-node";
129
- import { PublishabilityDetector } from "workspaces-effect";
125
+ import { NodeServices } from "@effect/platform-node";
126
+ import { PublishabilityDetector } from "@effected/workspaces";
130
127
  import { SilkPublishabilityDetectorLive } from "@savvy-web/silk-effects";
131
128
 
132
129
  const targets = await Effect.runPromise(
@@ -135,13 +132,13 @@ const targets = await Effect.runPromise(
135
132
  return yield* detector.detect(pkg, root);
136
133
  }).pipe(
137
134
  Effect.provide(SilkPublishabilityDetectorLive),
138
- Effect.provide(NodeContext.layer),
135
+ Effect.provide(NodeServices.layer),
139
136
  ),
140
137
  );
141
138
  // => ReadonlyArray<PublishTarget>
142
139
  ```
143
140
 
144
- See [Publishability](./docs/publishability.md) for the adaptive layer and the `ChangesetConfig` service.
141
+ See [Publishability](./docs/03-publishability.md) for the adaptive layer and the `ChangesetConfig` service.
145
142
 
146
143
  #### ChangesetConfig
147
144
 
@@ -149,7 +146,7 @@ Typed accessor over a workspace root's `.changeset/config.json`, reading through
149
146
 
150
147
  ```typescript
151
148
  import { Effect } from "effect";
152
- import { NodeContext } from "@effect/platform-node";
149
+ import { NodeServices } from "@effect/platform-node";
153
150
  import {
154
151
  ChangesetConfig, ChangesetConfigLive, ChangesetConfigReaderLive,
155
152
  } from "@savvy-web/silk-effects";
@@ -161,128 +158,48 @@ const mode = await Effect.runPromise(
161
158
  }).pipe(
162
159
  Effect.provide(ChangesetConfigLive),
163
160
  Effect.provide(ChangesetConfigReaderLive),
164
- Effect.provide(NodeContext.layer),
161
+ Effect.provide(NodeServices.layer),
165
162
  ),
166
163
  );
167
164
  // => "silk" | "vanilla" | "none"
168
165
  ```
169
166
 
170
- #### ManagedSection
171
-
172
- Manage tool-owned delimited sections inside user-editable files. Sections are bounded by markers like `# --- BEGIN TOOL MANAGED SECTION ---` / `# --- END ... ---`. User content outside the markers is never touched.
173
-
174
- `SectionDefinition` is a value object representing section identity (tool name + comment style). It creates `SectionBlock` instances that hold the actual content. Definitions support typed content factories via `generate()` and `generateEffect()`.
175
-
176
- `SectionBlock` represents the content between markers. It supports `diff()`, `prepend()` and `append()` operations and uses normalized content for equality comparison.
177
-
178
- Methods: `read`, `write`, `sync`, `syncMany`, `check`, `remove`, `isManaged` — all support dual API (data-first and data-last) for pipe composition. `sync` manages one section; `syncMany` manages several ordered sections in one file; `remove` deletes a section including its markers.
179
-
180
- ```typescript
181
- import { Effect } from "effect";
182
- import { NodeContext } from "@effect/platform-node";
183
- import {
184
- ManagedSection, ManagedSectionLive, SectionDefinition,
185
- } from "@savvy-web/silk-effects";
186
-
187
- // Define section identity
188
- const def = SectionDefinition.make({ toolName: "LINT-STAGED" });
189
-
190
- // Create a content block from the definition
191
- const block = def.block("\nnpx lint-staged\n");
192
-
193
- await Effect.runPromise(
194
- Effect.gen(function* () {
195
- const ms = yield* ManagedSection;
196
-
197
- // Sync: creates the section if missing, updates if changed, no-op if identical
198
- const result = yield* ms.sync(".husky/pre-commit", block);
199
- // => SyncResult: Created | Updated | Unchanged
200
-
201
- // Check: compare file content against expected block
202
- const check = yield* ms.check(".husky/pre-commit", block);
203
- // => CheckResult: Found | NotFound
204
-
205
- // Remove: delete the section and its markers, collapsing the leftover blank line
206
- const removed = yield* ms.remove(".husky/pre-commit", def);
207
- // => true if a section was removed, false if none was present
208
- }).pipe(
209
- Effect.provide(ManagedSectionLive),
210
- Effect.provide(NodeContext.layer),
211
- ),
212
- );
213
- ```
214
-
215
- `SectionDefinition` also supports `//` comment style for JavaScript/TypeScript files:
167
+ #### Husky hook sections
216
168
 
217
- ```typescript
218
- const jsDef = SectionDefinition.make({ toolName: "MY-TOOL", commentStyle: "//" });
219
- ```
220
-
221
- Use `ShellSectionDefinition` when the comment style is always `#` and should not be configurable.
222
-
223
- `syncMany` keeps several sections in one file in their declared relative order. It updates existing sections in place, inserts a missing section next to its declared sibling, normalizes order when sections drift out of order and preserves user content and unrelated tool sections. It returns one `SyncResult` per input block in input order and is idempotent.
224
-
225
- The `SavvySections` exports compose ordered managed sections per husky hook file. A base section defines shared shell, then each consumer layers its own one-line tool section on top:
169
+ The Silk husky hooks are data. `SavvySections` supplies the section identities and the shell that goes inside them, and [`@effected/templates`](https://www.npmjs.com/package/@effected/templates)' `ManagedSection` service does the reading and writing, so user content outside the `# --- BEGIN ... ---` markers is never touched.
226
170
 
227
- - `SavvyBaseSection` is a `ShellSectionDefinition` (tool name `savvy-base`); pair it with `savvyBasePreamble()`, which defines `ROOT`, the `in_ci` predicate, `PM` via package-manager detection and `pm_exec`.
228
- - `SavvyHooksSection` (tool name `savvy-hooks`) pairs with `savvyHooksHygiene()`, a self-guarded repo-hygiene block that runs outside CI.
229
- - `savvyToolSection(toolName, command)` builds a consumer's one-line tool section whose content is exactly `in_ci || pm_exec <command>` — the command is appended verbatim, so shell tokens like `$ROOT` and `$1` survive into the output. A `savvy-base` section must precede it in the same hook file, so pass both to `syncMany` in that order.
171
+ - `SavvyBaseSection` is the shared preamble's identity (key `SAVVY-BASE`); pair it with `savvyBasePreamble()`, which defines `ROOT`, the `in_ci` predicate, `PM` via package-manager detection and `pm_exec`.
172
+ - `SavvyHooksSection` (key `SAVVY-HOOKS`) pairs with `savvyHooksHygiene()`, a self-guarded repo-hygiene block that runs outside CI.
173
+ - `savvyToolSection(toolName, command)` builds a consumer's one-line tool section whose content is exactly `in_ci || pm_exec <command>` — the command is appended verbatim, so shell tokens like `$ROOT` and `$1` survive into the output. A `savvy-base` section must come first in the same hook file, so pass both to `syncAll` in that order.
230
174
 
231
175
  ```typescript
232
176
  import { Effect } from "effect";
233
- import { NodeContext } from "@effect/platform-node";
234
- import {
235
- ManagedSection, ManagedSectionLive,
236
- SavvyBaseSection, savvyBasePreamble, savvyToolSection,
237
- } from "@savvy-web/silk-effects";
177
+ import { NodeServices } from "@effect/platform-node";
178
+ import { ManagedSection } from "@effected/templates";
179
+ import { SavvyBaseSection, savvyBasePreamble, savvyToolSection } from "@savvy-web/silk-effects";
238
180
 
239
181
  await Effect.runPromise(
240
182
  Effect.gen(function* () {
241
183
  const ms = yield* ManagedSection;
242
- const results = yield* ms.syncMany(".husky/commit-msg", [
243
- SavvyBaseSection.block(savvyBasePreamble()),
184
+ const results = yield* ms.syncAll(".husky/commit-msg", [
185
+ SavvyBaseSection.section(savvyBasePreamble()),
244
186
  savvyToolSection("savvy-commit", 'commitlint --config "$ROOT/lib/configs/commitlint.config.ts" --edit "$1"'),
245
187
  ]);
246
- // => ReadonlyArray<SyncResult>, one per input block in declared order
188
+ // => one sync result per input section, in declared order
247
189
  }).pipe(
248
- Effect.provide(ManagedSectionLive),
249
- Effect.provide(NodeContext.layer),
190
+ Effect.provide(ManagedSection.layer),
191
+ Effect.provide(NodeServices.layer),
250
192
  ),
251
193
  );
252
194
  ```
253
195
 
254
- #### VersioningStrategy
255
-
256
- Classify the versioning strategy from changeset configuration. Outputs `"single"` (0-1 publishable packages), `"fixed-group"` (all packages in one fixed group) or `"independent"` (multiple packages, not in a single group). Falls back gracefully if config is missing.
257
-
258
- ```typescript
259
- import { Effect } from "effect";
260
- import { NodeContext } from "@effect/platform-node";
261
- import {
262
- VersioningStrategy, VersioningStrategyLive,
263
- ChangesetConfigReaderLive,
264
- } from "@savvy-web/silk-effects";
265
-
266
- const result = await Effect.runPromise(
267
- Effect.gen(function* () {
268
- const vs = yield* VersioningStrategy;
269
- return yield* vs.detect(publishablePackages, process.cwd());
270
- }).pipe(
271
- Effect.provide(VersioningStrategyLive),
272
- Effect.provide(ChangesetConfigReaderLive),
273
- Effect.provide(NodeContext.layer),
274
- ),
275
- );
276
- // => { strategy: "single" | "fixed-group" | "independent", ... }
277
- ```
278
-
279
196
  #### ChangesetConfigReader
280
197
 
281
198
  Read and decode `.changeset/config.json`. Auto-detects whether the project uses `@savvy-web/changesets` (returning `SilkChangesetConfigFile` with `_isSilk: true`) or standard changesets (returning `ChangesetConfigFile`).
282
199
 
283
200
  ```typescript
284
201
  import { Effect } from "effect";
285
- import { NodeContext } from "@effect/platform-node";
202
+ import { NodeServices } from "@effect/platform-node";
286
203
  import {
287
204
  ChangesetConfigReader, ChangesetConfigReaderLive,
288
205
  } from "@savvy-web/silk-effects";
@@ -293,7 +210,7 @@ const config = await Effect.runPromise(
293
210
  return yield* reader.read(process.cwd());
294
211
  }).pipe(
295
212
  Effect.provide(ChangesetConfigReaderLive),
296
- Effect.provide(NodeContext.layer),
213
+ Effect.provide(NodeServices.layer),
297
214
  ),
298
215
  );
299
216
  // => ChangesetConfigFile | SilkChangesetConfigFile
@@ -305,7 +222,7 @@ Locate config files using a priority-based search convention. Checks `lib/config
305
222
 
306
223
  ```typescript
307
224
  import { Effect } from "effect";
308
- import { NodeContext } from "@effect/platform-node";
225
+ import { NodeServices } from "@effect/platform-node";
309
226
  import { ConfigDiscovery, ConfigDiscoveryLive } from "@savvy-web/silk-effects";
310
227
 
311
228
  const result = await Effect.runPromise(
@@ -314,7 +231,7 @@ const result = await Effect.runPromise(
314
231
  return yield* cd.find("biome.jsonc");
315
232
  }).pipe(
316
233
  Effect.provide(ConfigDiscoveryLive),
317
- Effect.provide(NodeContext.layer),
234
+ Effect.provide(NodeServices.layer),
318
235
  ),
319
236
  );
320
237
  // => { path: "/project/biome.jsonc", source: "root" } | null
@@ -326,7 +243,7 @@ Keep Biome config `$schema` URLs current. Locates `biome.json` or `biome.jsonc`,
326
243
 
327
244
  ```typescript
328
245
  import { Effect } from "effect";
329
- import { NodeContext } from "@effect/platform-node";
246
+ import { NodeServices } from "@effect/platform-node";
330
247
  import { BiomeSchemaSync, BiomeSchemaSyncLive } from "@savvy-web/silk-effects";
331
248
 
332
249
  const result = await Effect.runPromise(
@@ -335,7 +252,7 @@ const result = await Effect.runPromise(
335
252
  return yield* bss.sync("2.0.0");
336
253
  }).pipe(
337
254
  Effect.provide(BiomeSchemaSyncLive),
338
- Effect.provide(NodeContext.layer),
255
+ Effect.provide(NodeServices.layer),
339
256
  ),
340
257
  );
341
258
  // => { updated: true, skipped: false, current: "2.0.0" }
@@ -343,59 +260,27 @@ const result = await Effect.runPromise(
343
260
 
344
261
  ---
345
262
 
346
- ### FileSystem + CommandExecutor layer required
347
-
348
- #### ToolDiscovery
349
-
350
- Locate CLI tools globally (PATH) or locally (via package manager), extract versions, enforce constraints and cache results.
263
+ ### FileSystem + process layer required
351
264
 
352
- `ToolDefinition` configures how a tool is resolved: `VersionExtractor` (Flag, Json or None), `ResolutionPolicy` (Report, PreferLocal, PreferGlobal, RequireMatch) and `SourceRequirement` (Any, OnlyLocal, OnlyGlobal, Both). Equality is based on tool name only.
353
-
354
- `ResolvedTool` is the result of resolution. It carries the tool's name, source (`"global"` or `"local"`), version and package manager. Its `exec()` and `dlx()` methods return a `ToolCommand` — a wrapper around `@effect/platform` `Command` with instance-method ergonomics (`string()`, `lines()`, `exitCode()`, `stream()`).
355
-
356
- ```typescript
357
- import { Effect } from "effect";
358
- import { NodeContext } from "@effect/platform-node";
359
- import {
360
- ToolDiscovery, ToolDiscoveryLive, ToolDefinition,
361
- } from "@savvy-web/silk-effects";
362
-
363
- const output = await Effect.runPromise(
364
- Effect.gen(function* () {
365
- const td = yield* ToolDiscovery;
366
-
367
- // Resolve a tool (results are cached by name)
368
- const biome = yield* td.resolve(ToolDefinition.make({ name: "biome" }));
369
-
370
- // Check availability without throwing
371
- const hasBiome = yield* td.isAvailable(ToolDefinition.make({ name: "biome" }));
265
+ #### TurboInspector
372
266
 
373
- // Execute the resolved tool
374
- return yield* biome.exec("check", ".").string();
375
- }).pipe(
376
- Effect.provide(ToolDiscoveryLive),
377
- Effect.provide(NodeContext.layer),
378
- ),
379
- );
380
- ```
267
+ Read-only Turborepo inspection. Every method shells out to `turbo` with `--dry=json`, so no task ever runs. `diagnoseCache(task, cwd)` reports a per-package cache HIT/MISS breakdown for a task, `taskGraph(cwd, task?)` derives the task graph and its critical path and `affected(cwd, base?)` lists the packages affected relative to `base` (default `main`). It resolves the `turbo` binary through `ToolDiscovery` from [`@effected/commands`](https://www.npmjs.com/package/@effected/commands) and fails with a tagged error when `turbo` is missing or the directory is not a Turborepo. The service tag and its layer are exported under the `Turbo` namespace.
381
268
 
382
- Use `require()` to fail with a descriptive error if the tool is not found:
269
+ `Turbo.TurboInspectorLive` requires `ToolDiscovery`, `Git` from [`@effected/git`](https://www.npmjs.com/package/@effected/git), `FileSystem` and the platform process spawner. Wire `ToolDiscovery` to the workspace with `Workspaces.localExecLayer()`, which teaches it the argv prefix that runs a project-local binary:
383
270
 
384
271
  ```typescript
385
- const biome = yield* td.require(
386
- ToolDefinition.make({ name: "biome" }),
387
- "Biome is required for linting",
272
+ import { Effect, Layer } from "effect";
273
+ import { NodeServices } from "@effect/platform-node";
274
+ import { ToolDiscovery } from "@effected/commands";
275
+ import { Git } from "@effected/git";
276
+ import { PackageManagerDetector, WorkspaceRoot, Workspaces } from "@effected/workspaces";
277
+ import { Turbo } from "@savvy-web/silk-effects";
278
+
279
+ const WorkspaceLive = Layer.mergeAll(WorkspaceRoot.layer, PackageManagerDetector.layer);
280
+ const ToolsLive = ToolDiscovery.layer.pipe(
281
+ Layer.provide(Workspaces.localExecLayer()),
282
+ Layer.provide(WorkspaceLive),
388
283
  );
389
- ```
390
-
391
- #### TurboInspector
392
-
393
- Read-only Turborepo inspection. Every method shells out to `turbo` with `--dry=json`, so no task ever runs. `diagnoseCache(task, cwd)` reports a per-package cache HIT/MISS breakdown for a task, `taskGraph(cwd, task?)` derives the task graph and its critical path and `affected(cwd, base?)` lists the packages affected relative to `base` (default `main`). It resolves the `turbo` binary through `ToolDiscovery` and fails with a tagged error when `turbo` is missing or the directory is not a Turborepo. The service tag and its layer are exported under the `Turbo` namespace.
394
-
395
- ```typescript
396
- import { Effect } from "effect";
397
- import { NodeContext } from "@effect/platform-node";
398
- import { Turbo, ToolDiscoveryLive } from "@savvy-web/silk-effects";
399
284
 
400
285
  const diagnosis = await Effect.runPromise(
401
286
  Effect.gen(function* () {
@@ -403,8 +288,8 @@ const diagnosis = await Effect.runPromise(
403
288
  return yield* turbo.diagnoseCache("build:dev", process.cwd());
404
289
  }).pipe(
405
290
  Effect.provide(Turbo.TurboInspectorLive),
406
- Effect.provide(ToolDiscoveryLive),
407
- Effect.provide(NodeContext.layer),
291
+ Effect.provide(Layer.mergeAll(ToolsLive, Git.layer)),
292
+ Effect.provide(NodeServices.layer),
408
293
  ),
409
294
  );
410
295
  // => CacheDiagnosis: per-package HIT/MISS breakdown for the task
@@ -412,16 +297,12 @@ const diagnosis = await Effect.runPromise(
412
297
 
413
298
  ## Documentation
414
299
 
415
- - [Overview](./docs/overview.md) — what the library is, its design philosophy and platform-layer model
416
- - [Publishability](./docs/publishability.md) — silk publishability rules, the detector overrides and the ChangesetConfig service
417
- - [Changeset config](./docs/changeset-config.md) — reading and decoding `.changeset/config.json`
418
- - [Platform layers](./docs/platform-layers.md) — composing layers and providing platform dependencies
419
- - [Managed sections](./docs/managed-section.md) — tool-owned regions in user-editable files
420
- - [Tool discovery](./docs/tool-discovery.md) — locating and resolving CLI tools
421
- - [Versioning strategy](./docs/versioning-strategy.md) — classifying workspace versioning
422
- - [Config discovery](./docs/config-discovery.md) — priority-based config file search
423
- - [Biome sync](./docs/biome-sync.md) — keeping Biome `$schema` URLs current
424
- - [Tag strategy](./docs/tag-strategy.md) — git tag naming and formatting
300
+ - [Overview](./docs/01-overview.md) — what the library is, its design philosophy and platform-layer model
301
+ - [Platform layers](./docs/02-platform-layers.md) — composing layers and providing platform dependencies
302
+ - [Publishability](./docs/03-publishability.md) — silk publishability rules, the detector overrides and the ChangesetConfig service
303
+ - [Changeset config](./docs/04-changeset-config.md) — reading and decoding `.changeset/config.json`
304
+ - [Config discovery](./docs/05-config-discovery.md) — priority-based config file search
305
+ - [Biome sync](./docs/06-biome-sync.md) — keeping Biome `$schema` URLs current
425
306
 
426
307
  ## License
427
308
 
@@ -15,8 +15,7 @@ import changelogFunctions from "../changelog/index.js";
15
15
  * Delegates to the Changesets-compatible `getReleaseLine` and
16
16
  * `getDependencyReleaseLine` functions. Internally, these use the
17
17
  * {@link ChangelogService} Effect service layer, which coordinates
18
- * the {@link GitHubService} (for commit/PR metadata) and
19
- * {@link MarkdownService} (for AST manipulation) to produce
18
+ * the {@link GitHubService} (for commit/PR metadata) to produce
20
19
  * structured changelog entries.
21
20
  *
22
21
  * @remarks
@@ -1,9 +1,8 @@
1
1
  import { validateChangesetOptions } from "../schemas/options.js";
2
2
  import { GitHubLive } from "../services/github.js";
3
- import { MarkdownLive } from "../services/markdown.js";
4
3
  import { getDependencyReleaseLine } from "./getDependencyReleaseLine.js";
5
4
  import { getReleaseLine } from "./getReleaseLine.js";
6
- import { Effect, Layer } from "effect";
5
+ import { Effect } from "effect";
7
6
 
8
7
  //#region src/changesets/changelog/index.ts
9
8
  /**
@@ -17,8 +16,7 @@ import { Effect, Layer } from "effect";
17
16
  * @remarks
18
17
  * The module composes two Effect programs — {@link getReleaseLine} and
19
18
  * {@link getDependencyReleaseLine} — and runs each through
20
- * `Effect.runPromise` with a merged layer of {@link GitHubLive} (for commit
21
- * metadata) and {@link MarkdownLive} (for mdast parsing). Options are
19
+ * `Effect.runPromise` with {@link GitHubLive} (for commit metadata). Options are
22
20
  * validated at the boundary via `validateChangesetOptions` before being
23
21
  * passed to the formatters.
24
22
  *
@@ -58,16 +56,16 @@ import { Effect, Layer } from "effect";
58
56
  * @see {@link getDependencyReleaseLine} in `./getDependencyReleaseLine.ts` for dependency table formatting
59
57
  */
60
58
  /**
61
- * Combined layer providing all services needed by the formatters.
59
+ * The layer providing every service the formatters need.
62
60
  *
63
- * Merges {@link GitHubLive} and {@link MarkdownLive} into a single layer
64
- * that satisfies the environment requirements of both `getReleaseLine`
65
- * (which needs `GitHubService` and `MarkdownService`) and
66
- * `getDependencyReleaseLine` (which needs `GitHubService`).
61
+ * {@link GitHubLive} satisfies the requirements of both `getReleaseLine` and
62
+ * `getDependencyReleaseLine`, which each need only `GitHubService`. Markdown
63
+ * parsing is not a layer: the formatters call the remark pipeline's
64
+ * `parseMarkdown` / `stringifyMarkdown` functions directly.
67
65
  *
68
66
  * @internal
69
67
  */
70
- const MainLayer = Layer.mergeAll(GitHubLive, MarkdownLive);
68
+ const MainLayer = GitHubLive;
71
69
  /**
72
70
  * Changesets API `ChangelogFunctions` implementation.
73
71
  *
@@ -154,7 +154,6 @@ const MarkdownParseErrorBase = Data.TaggedError("MarkdownParseError");
154
154
  * );
155
155
  * ```
156
156
  *
157
- * @see {@link MarkdownService} for the Effect service that may produce this error
158
157
  *
159
158
  * @public
160
159
  */
@@ -5,7 +5,6 @@ import { JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema
5
5
  import { GlobSchema, PackageScopeSchema, PackagesRecordSchema } from "./schemas/package-scope.js";
6
6
  import { ChangesetOptionsSchema, RepoSchema } from "./schemas/options.js";
7
7
  import { GitHubLive, GitHubService, makeGitHubTest } from "./services/github.js";
8
- import { MarkdownLive, MarkdownService } from "./services/markdown.js";
9
8
  import { NonEmptyString, PositiveInteger } from "./schemas/primitives.js";
10
9
  import { DependencyActionSchema, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, VERSION_RE, VersionOrEmptySchema } from "./schemas/dependency-table.js";
11
10
  import { serializeDependencyTableToMarkdown } from "./utils/dependency-table.js";
@@ -108,9 +107,7 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
108
107
  MaintenanceNotePlugin: () => MaintenanceNotePlugin,
109
108
  MaintenanceReasonSchema: () => MaintenanceReasonSchema,
110
109
  MaintenanceTriggerSchema: () => MaintenanceTriggerSchema,
111
- MarkdownLive: () => MarkdownLive,
112
110
  MarkdownParseError: () => MarkdownParseError,
113
- MarkdownService: () => MarkdownService,
114
111
  MarkdownlintContentStructureRule: () => ContentStructureRule$1,
115
112
  MarkdownlintDependencyTableFormatRule: () => DependencyTableFormatRule$1,
116
113
  MarkdownlintHeadingHierarchyRule: () => HeadingHierarchyRule$1,
@@ -162,4 +159,4 @@ var changesets_exports = /* @__PURE__ */ __exportAll({
162
159
  });
163
160
 
164
161
  //#endregion
165
- export { AggregateDependencyTablesPlugin, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerLive, BranchFileEntrySchema, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogTransformer, ChangesetIOError, ChangesetLinter, ChangesetOptionsSchema, ChangesetPreviewSchema, ChangesetSchema, ChangesetSummarySchema, ChangesetValidationError, ClassificationReasonSchema, ClassificationSchema, CommitHashSchema, ConfigInspector, ConfigInspectorLive, ConfigurationError, ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, DependencyActionSchema, DependencyTable, DependencyTableFormatRule, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, DependencyTypeSchema, DependencyUpdateSchema, DepsRegen, DepsRegenDefault, DepsRegenLive, FileStatusSchema, GitError, GitHubApiError, GitHubInfoSchema, GitHubLive, GitHubService, GlobSchema, HeadingHierarchyRule, InspectedConfigSchema, IssueLinkRefsPlugin, IssueNumberSchema, JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, MaintenanceNotePlugin, MaintenanceReasonSchema, MaintenanceTriggerSchema, MarkdownLive, MarkdownParseError, MarkdownService, ContentStructureRule$1 as MarkdownlintContentStructureRule, DependencyTableFormatRule$1 as MarkdownlintDependencyTableFormatRule, HeadingHierarchyRule$1 as MarkdownlintHeadingHierarchyRule, RequiredSectionsRule$1 as MarkdownlintRequiredSectionsRule, UncategorizedContentRule$1 as MarkdownlintUncategorizedContentRule, MergeSectionsPlugin, NonEmptyString, NormalizeFormatPlugin, PackageScopeSchema, PackagesRecordSchema, PendingChangesetSchema, PositiveInteger, PreviewReleaseSchema, ReleasePlanError, ReleasePlanner, ReleasePlannerLive, ReorderSectionsPlugin, RepoSchema, RequiredSectionsRule, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, SectionCategorySchema, SilkChangesetPreset, SilkChangesetTransformPreset, SilkChangesetsRules, UncategorizedContentRule, UrlOrMarkdownLinkSchema, UsernameSchema, VERSION_RE, VersionFileConfigSchema, VersionFileError, VersionFileUpdateRecordSchema, VersionFiles, VersionFilesSchema, VersionOrEmptySchema, VersionTypeSchema, changelogFunctions, changesets_exports, computeWorkspaceDependencyDiffs, deriveMaintenanceReason, gitMergeBase, isPureDependencyChangeset, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeDepsRegenDefault, makeGitHubTest, makeReleasePlannerTest, serializeDependencyTableToMarkdown };
162
+ export { AggregateDependencyTablesPlugin, AppliedReleaseEntrySchema, AppliedReleaseSchema, BranchAnalysisSchema, BranchAnalyzer, BranchAnalyzerLive, BranchFileEntrySchema, BumpTypeSchema, Categories, Changelog, ChangelogService, ChangelogTransformer, ChangesetIOError, ChangesetLinter, ChangesetOptionsSchema, ChangesetPreviewSchema, ChangesetSchema, ChangesetSummarySchema, ChangesetValidationError, ClassificationReasonSchema, ClassificationSchema, CommitHashSchema, ConfigInspector, ConfigInspectorLive, ConfigurationError, ContentStructureRule, ContributorFootnotesPlugin, DeduplicateItemsPlugin, DependencyActionSchema, DependencyTable, DependencyTableFormatRule, DependencyTableRowSchema, DependencyTableSchema, DependencyTableTypeSchema, DependencyTypeSchema, DependencyUpdateSchema, DepsRegen, DepsRegenDefault, DepsRegenLive, FileStatusSchema, GitError, GitHubApiError, GitHubInfoSchema, GitHubLive, GitHubService, GlobSchema, HeadingHierarchyRule, InspectedConfigSchema, IssueLinkRefsPlugin, IssueNumberSchema, JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, MaintenanceNotePlugin, MaintenanceReasonSchema, MaintenanceTriggerSchema, MarkdownParseError, ContentStructureRule$1 as MarkdownlintContentStructureRule, DependencyTableFormatRule$1 as MarkdownlintDependencyTableFormatRule, HeadingHierarchyRule$1 as MarkdownlintHeadingHierarchyRule, RequiredSectionsRule$1 as MarkdownlintRequiredSectionsRule, UncategorizedContentRule$1 as MarkdownlintUncategorizedContentRule, MergeSectionsPlugin, NonEmptyString, NormalizeFormatPlugin, PackageScopeSchema, PackagesRecordSchema, PendingChangesetSchema, PositiveInteger, PreviewReleaseSchema, ReleasePlanError, ReleasePlanner, ReleasePlannerLive, ReorderSectionsPlugin, RepoSchema, RequiredSectionsRule, ResolvedPackageScopeSchema, ResolvedVersionFileSchema, SectionCategorySchema, SilkChangesetPreset, SilkChangesetTransformPreset, SilkChangesetsRules, UncategorizedContentRule, UrlOrMarkdownLinkSchema, UsernameSchema, VERSION_RE, VersionFileConfigSchema, VersionFileError, VersionFileUpdateRecordSchema, VersionFiles, VersionFilesSchema, VersionOrEmptySchema, VersionTypeSchema, changelogFunctions, changesets_exports, computeWorkspaceDependencyDiffs, deriveMaintenanceReason, gitMergeBase, isPureDependencyChangeset, listPublishablePackageNames, makeBranchAnalyzerTest, makeConfigInspectorTest, makeDepsRegenDefault, makeGitHubTest, makeReleasePlannerTest, serializeDependencyTableToMarkdown };
@@ -18,7 +18,7 @@ import { Context } from "effect";
18
18
  * ```typescript
19
19
  * import { Effect } from "effect";
20
20
  * import type { ChangesetOptions } from "\@savvy-web/changesets";
21
- * import { ChangelogService, GitHubLive, MarkdownLive } from "\@savvy-web/changesets";
21
+ * import { ChangelogService, GitHubLive } from "\@savvy-web/changesets";
22
22
  *
23
23
  * const program = Effect.gen(function* () {
24
24
  * const changelog = yield* ChangelogService;