@savvy-web/silk-effects 3.3.1 → 4.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 (72) hide show
  1. package/changesets/changelog/getReleaseLine.js +1 -1
  2. package/changesets/constants.js +0 -18
  3. package/changesets/index.js +10 -24
  4. package/changesets/schemas/changeset.js +8 -4
  5. package/changesets/schemas/dependency-table.js +15 -4
  6. package/changesets/schemas/git.js +7 -2
  7. package/changesets/schemas/github.js +4 -4
  8. package/changesets/schemas/options.js +3 -3
  9. package/changesets/schemas/package-scope.js +2 -5
  10. package/changesets/schemas/primitives.js +2 -2
  11. package/changesets/schemas/release-plan.js +12 -8
  12. package/changesets/schemas/version-files.js +4 -4
  13. package/changesets/services/branch-analyzer.js +60 -191
  14. package/changesets/services/changelog.js +2 -15
  15. package/changesets/services/config-inspector.js +109 -75
  16. package/changesets/services/deps-regen.js +58 -32
  17. package/changesets/services/github.js +3 -16
  18. package/changesets/services/maintenance-reason.js +5 -1
  19. package/changesets/services/markdown.js +3 -16
  20. package/changesets/services/release-planner.js +4 -13
  21. package/changesets/utils/commit-parser.js +0 -18
  22. package/changesets/utils/dep-diff.js +0 -27
  23. package/changesets/utils/git.js +15 -50
  24. package/changesets/utils/issue-refs.js +0 -13
  25. package/changesets/utils/logger.js +0 -13
  26. package/changesets/utils/markdown-link.js +0 -14
  27. package/changesets/utils/publishability.js +11 -24
  28. package/changesets/utils/strip-frontmatter.js +0 -19
  29. package/changesets/utils/version-files.js +85 -51
  30. package/commitlint/config/schema.js +9 -5
  31. package/commitlint/detection/scopes.js +2 -7
  32. package/commitlint/formatter/messages.js +0 -5
  33. package/commitlint/hook/diagnostics/branch.js +12 -12
  34. package/commitlint/hook/diagnostics/open-issues.js +13 -7
  35. package/commitlint/hook/diagnostics/signing.js +30 -37
  36. package/commitlint/hook/envelope.js +2 -8
  37. package/commitlint/hook/silence-logger.js +14 -12
  38. package/index.d.ts +897 -932
  39. package/lint/cli/templates/markdownlint.gen.js +0 -9
  40. package/lint/handlers/PnpmWorkspace.js +26 -12
  41. package/lint/utils/Filter.js +0 -13
  42. package/lint/utils/Workspace.js +10 -6
  43. package/package.json +11 -10
  44. package/repos/index.js +3 -5
  45. package/repos/schemas/manifest.js +7 -13
  46. package/repos/schemas/reports.js +5 -1
  47. package/repos/services/config-store.js +6 -10
  48. package/repos/services/manager.js +52 -112
  49. package/schemas/CommentStyle.js +1 -1
  50. package/schemas/ResolvedTool.js +52 -17
  51. package/schemas/SectionBlock.js +1 -1
  52. package/schemas/SectionDefinition.js +2 -2
  53. package/schemas/TagStrategySchemas.js +1 -1
  54. package/schemas/ToolDefinition.js +1 -1
  55. package/schemas/ToolResults.js +1 -1
  56. package/schemas/VersioningSchemas.js +28 -9
  57. package/schemas/WorkspaceAnalysisSchemas.js +27 -17
  58. package/services/BiomeSchemaSync.js +7 -8
  59. package/services/ChangesetConfig.js +2 -2
  60. package/services/ChangesetConfigReader.js +7 -8
  61. package/services/ConfigDiscovery.js +5 -6
  62. package/services/ManagedSection.js +3 -4
  63. package/services/SilkPublishability.js +38 -15
  64. package/services/SilkWorkspaceAnalyzer.js +7 -9
  65. package/services/TagStrategy.js +3 -3
  66. package/services/ToolDiscovery.js +22 -21
  67. package/services/VersioningStrategy.js +2 -2
  68. package/tsdoc-metadata.json +1 -1
  69. package/turbo/schemas/DryRun.js +8 -14
  70. package/turbo/schemas/results.js +8 -8
  71. package/turbo/services/TurboInspector.js +30 -23
  72. package/utils/ToolCommand.js +38 -12
@@ -7,24 +7,24 @@ import { ConfigInspector, ConfigInspectorLive } from "./config-inspector.js";
7
7
  import { computeWorkspaceDependencyDiffs } from "../utils/dep-diff.js";
8
8
  import { gitListChangesetFilesAtRef, gitMergeBase } from "../utils/git.js";
9
9
  import { listPublishablePackageNames } from "../utils/publishability.js";
10
- import { Context, Effect, Layer, Option } from "effect";
10
+ import { Context, Effect, FileSystem, Layer, Option } from "effect";
11
11
  import { basename, join, resolve } from "node:path";
12
- import { FileSystem } from "@effect/platform";
13
- import { PointInTimeWorkspace, PointInTimeWorkspaceLive, PublishabilityDetector, WorkspaceDiscovery, WorkspaceDiscoveryLive, WorkspaceRootLive } from "workspaces-effect";
12
+ import { Git } from "@effected/git";
13
+ import { PublishabilityDetector, WorkspaceDiscovery, WorkspaceSnapshots, Workspaces } from "@effected/workspaces";
14
14
 
15
15
  //#region src/changesets/services/deps-regen.ts
16
16
  /**
17
17
  * `Changesets.DepsRegen` service — lift the `deps regen` / `deps detect`
18
- * orchestration out of the CLI into a `Context.Tag` service with a
18
+ * orchestration out of the CLI into a `Context.Service` with a
19
19
  * `plan()` / `execute()` split.
20
20
  *
21
21
  * @remarks
22
22
  * `plan()` computes the cumulative dependency diff (merge-base → working
23
23
  * tree by default, or between two explicit refs) by snapshotting both
24
- * sides through `PointInTimeWorkspace`, which resolves `catalog:` /
25
- * `workspace:` specifiers against each ref's own catalogs and package
26
- * versions before diffing. It drops `devDependency` rows (unless
27
- * `includeDevDeps`), and returns a complete {@link RegenPlan}
24
+ * sides through `@effected/workspaces`' `WorkspaceSnapshots`, which
25
+ * resolves `catalog:` / `workspace:` specifiers against each ref's own
26
+ * catalogs and package versions before diffing. It drops `devDependency`
27
+ * rows (unless `includeDevDeps`), and returns a complete {@link RegenPlan}
28
28
  * (target filenames + stale-changeset deletes) WITHOUT touching the
29
29
  * filesystem. `execute()` applies a plan — writing the fresh changesets
30
30
  * first, then deleting the stale pure-dependency ones (so an interrupted
@@ -176,7 +176,7 @@ function isPureDependencyChangeset(content) {
176
176
  * unreadable `.changeset` dir must not silently masquerade as "no
177
177
  * changesets".
178
178
  */
179
- const listChangesetFiles = (fs, changesetDir) => fs.readDirectory(changesetDir).pipe(Effect.map((names) => names.filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => join(changesetDir, f))), Effect.catchAll((e) => e._tag === "SystemError" && e.reason === "NotFound" ? Effect.succeed([]) : Effect.fail(new ChangesetIOError({
179
+ const listChangesetFiles = (fs, changesetDir) => fs.readDirectory(changesetDir).pipe(Effect.map((names) => names.filter((f) => f.endsWith(".md") && f !== "README.md").map((f) => join(changesetDir, f))), Effect.catch((e) => e._tag === "PlatformError" && e.reason._tag === "NotFound" ? Effect.succeed([]) : Effect.fail(new ChangesetIOError({
180
180
  path: changesetDir,
181
181
  operation: "list",
182
182
  reason: String(e)
@@ -225,11 +225,6 @@ const findMixedDependencyChangesets = (fs, changesetDir) => Effect.gen(function*
225
225
  function renderChangesetContent(diff) {
226
226
  return `${`---\n"${diff.package}": patch\n---`}\n\n## Dependencies\n\n${serializeDependencyTableToMarkdown([...diff.rows])}\n`;
227
227
  }
228
- const _tag = Context.Tag("Changesets/DepsRegen");
229
- /**
230
- * @internal
231
- */
232
- const DepsRegenBase = _tag();
233
228
  /**
234
229
  * Effect service tag for {@link DepsRegenShape}.
235
230
  *
@@ -247,13 +242,13 @@ const DepsRegenBase = _tag();
247
242
  *
248
243
  * @public
249
244
  */
250
- var DepsRegen = class extends DepsRegenBase {};
245
+ var DepsRegen = class extends Context.Service()("Changesets/DepsRegen") {};
251
246
  /**
252
247
  * Build a {@link DepsRegenShape} that closes over already-resolved service
253
248
  * implementations, keeping the public `plan`/`execute` signatures
254
249
  * requirement-free (`R = never`).
255
250
  */
256
- function makeShape(pit, inspector, discovery, detector, config, fs) {
251
+ function makeShape(snapshots, inspector, discovery, detector, config, fs, provideGit) {
257
252
  const provideDetector = Layer.succeed(PublishabilityDetector, detector);
258
253
  const fileExists = (p) => fs.exists(p).pipe(Effect.orElseSucceed(() => false));
259
254
  const plan = (options) => Effect.gen(function* () {
@@ -265,12 +260,12 @@ function makeShape(pit, inspector, discovery, detector, config, fs) {
265
260
  if (!fromRef) {
266
261
  let baseBranch = options.base;
267
262
  if (!baseBranch) baseBranch = (yield* inspector.inspect(resolvedCwd).pipe(Effect.catchTag("ConfigurationError", () => Effect.succeed({ baseBranch: "main" })))).baseBranch;
268
- fromRef = yield* gitMergeBase(resolvedCwd, baseBranch);
263
+ fromRef = yield* gitMergeBase(resolvedCwd, baseBranch).pipe(Effect.provide(provideGit));
269
264
  }
270
- const rawDiffs = computeWorkspaceDependencyDiffs(yield* pit.at(fromRef, { cwd: resolvedCwd }), options.to ? yield* pit.at(options.to, { cwd: resolvedCwd }) : yield* pit.worktree({ cwd: resolvedCwd }));
265
+ const rawDiffs = computeWorkspaceDependencyDiffs(yield* snapshots.at(fromRef), options.to ? yield* snapshots.at(options.to) : yield* snapshots.worktree());
271
266
  const explicitTargets = /* @__PURE__ */ new Set([...options.packages ?? [], ...options.package ? [options.package] : []]);
272
267
  const excluded = new Set(options.exclude ?? []);
273
- const livePackages = yield* discovery.listPackages(resolvedCwd);
268
+ const livePackages = yield* discovery.listPackages();
274
269
  const publishable = yield* listPublishablePackageNames(livePackages, resolvedCwd).pipe(Effect.provide(provideDetector));
275
270
  const versionPrivate = yield* config.versionPrivate(resolvedCwd);
276
271
  const inScope = /* @__PURE__ */ new Set();
@@ -298,7 +293,7 @@ function makeShape(pit, inspector, discovery, detector, config, fs) {
298
293
  const existingPure = yield* findPureDependencyChangesets(fs, changesetDir);
299
294
  const skippedMixed = yield* findMixedDependencyChangesets(fs, changesetDir);
300
295
  const rewrittenPackages = new Set(resolved.map((d) => d.package));
301
- const atMergeBase = yield* gitListChangesetFilesAtRef(resolvedCwd, fromRef);
296
+ const atMergeBase = yield* gitListChangesetFilesAtRef(resolvedCwd, fromRef).pipe(Effect.provide(provideGit));
302
297
  const authoredOnBranch = (file) => !atMergeBase.has(basename(file));
303
298
  const toDelete = existingPure.filter((p) => inScopeFor(p.package) && rewrittenPackages.has(p.package) && authoredOnBranch(p.file));
304
299
  const chosenFilenames = /* @__PURE__ */ new Set();
@@ -343,8 +338,9 @@ function makeShape(pit, inspector, discovery, detector, config, fs) {
343
338
  /**
344
339
  * Live layer for {@link DepsRegen}.
345
340
  *
346
- * Requires `PointInTimeWorkspace`, `WorkspaceDiscovery`,
347
- * `PublishabilityDetector` (all from `workspaces-effect`),
341
+ * Requires `WorkspaceSnapshots`, `WorkspaceDiscovery`,
342
+ * `PublishabilityDetector` (all from `@effected/workspaces`),
343
+ * `Git` (from `@effected/git`, backing merge-base resolution),
348
344
  * {@link ConfigInspector}, {@link ChangesetConfig}, and
349
345
  * `FileSystem.FileSystem` (resolved once at construction and closed over by
350
346
  * the shape, keeping `plan`/`execute` themselves requirement-free).
@@ -352,17 +348,47 @@ function makeShape(pit, inspector, discovery, detector, config, fs) {
352
348
  * @public
353
349
  */
354
350
  const DepsRegenLive = Layer.effect(DepsRegen, Effect.gen(function* () {
355
- return makeShape(yield* PointInTimeWorkspace, yield* ConfigInspector, yield* WorkspaceDiscovery, yield* PublishabilityDetector, yield* ChangesetConfig, yield* FileSystem.FileSystem);
351
+ const snapshots = yield* WorkspaceSnapshots;
352
+ const inspector = yield* ConfigInspector;
353
+ const discovery = yield* WorkspaceDiscovery;
354
+ const detector = yield* PublishabilityDetector;
355
+ const config = yield* ChangesetConfig;
356
+ const fs = yield* FileSystem.FileSystem;
357
+ const git = yield* Git;
358
+ return makeShape(snapshots, inspector, discovery, detector, config, fs, Layer.succeed(Git, git));
356
359
  }));
357
- const WorkspaceGraph = Layer.mergeAll(WorkspaceRootLive, WorkspaceDiscoveryLive.pipe(Layer.provide(WorkspaceRootLive)));
358
360
  const ConfigGraph = ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReaderLive));
359
361
  /**
362
+ * Build the batteries-included {@link DepsRegen} layer over a
363
+ * `@effected/workspaces` kit graph bound to `options.cwd`.
364
+ *
365
+ * @remarks
366
+ * The kit's root-consuming services (`WorkspaceDiscovery`,
367
+ * `WorkspaceSnapshots`, `WorkspaceRoot`) resolve their workspace root from
368
+ * the layer they were built with — single-root by design. {@link DepsRegenDefault}
369
+ * is this builder bound with no options (root from `process.cwd()`, read
370
+ * lazily); pass an explicit `cwd` when planning against a different root
371
+ * (fixtures, multi-repo hosts).
372
+ *
373
+ * `Workspaces.layerWithGit` mints a fresh layer reference per call, so the
374
+ * graph is bound ONCE per builder call and shared across every internal
375
+ * branch — layer memoization by reference constructs each kit service
376
+ * exactly once.
377
+ *
378
+ * @public
379
+ */
380
+ function makeDepsRegenDefault(options) {
381
+ const kitGraph = Workspaces.layerWithGit(options);
382
+ return DepsRegenLive.pipe(Layer.provide(ConfigInspectorLive.pipe(Layer.provide(Layer.mergeAll(ChangesetConfigReaderLive, kitGraph)))), Layer.provide(PublishabilityDetectorAdaptiveLive.pipe(Layer.provide(Layer.mergeAll(ConfigGraph, kitGraph)))), Layer.provide(ConfigGraph), Layer.provide(kitGraph));
383
+ }
384
+ /**
360
385
  * Batteries-included {@link DepsRegen} layer: silk's opinionated default
361
- * composition of the full dependency graph. Only the platform services
362
- * remain — note that {@link PointInTimeWorkspace} reads git history, so
363
- * this layer genuinely requires `CommandExecutor` in addition to
386
+ * composition of the full dependency graph, root-bound to `process.cwd()`
387
+ * (see {@link makeDepsRegenDefault} for an explicit root). Only the platform
388
+ * services remain note that `WorkspaceSnapshots` reads git history, so
389
+ * this layer genuinely requires `ChildProcessSpawner` in addition to
364
390
  * `FileSystem`/`Path`: provide a git-capable platform layer
365
- * (`NodeContext.layer`), not a bare filesystem-only layer.
391
+ * (`NodeServices.layer`), not a bare filesystem-only layer.
366
392
  *
367
393
  * Gating uses silk's adaptive publishability detector
368
394
  * ({@link PublishabilityDetectorAdaptiveLive}), so the default semantics
@@ -373,16 +399,16 @@ const ConfigGraph = ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReader
373
399
  *
374
400
  * @example
375
401
  * ```typescript
376
- * import { NodeContext } from "@effect/platform-node";
402
+ * import { NodeServices } from "@effect/platform-node";
377
403
  * import { Layer } from "effect";
378
404
  * import { Changesets } from "@savvy-web/silk-effects";
379
405
  *
380
- * const depsRegen = Changesets.DepsRegenDefault.pipe(Layer.provide(NodeContext.layer));
406
+ * const depsRegen = Changesets.DepsRegenDefault.pipe(Layer.provide(NodeServices.layer));
381
407
  * ```
382
408
  *
383
409
  * @public
384
410
  */
385
- const DepsRegenDefault = DepsRegenLive.pipe(Layer.provide(PointInTimeWorkspaceLive.pipe(Layer.provide(WorkspaceGraph))), Layer.provide(ConfigInspectorLive.pipe(Layer.provide(Layer.mergeAll(ChangesetConfigReaderLive, WorkspaceGraph)))), Layer.provide(PublishabilityDetectorAdaptiveLive.pipe(Layer.provide(ConfigGraph))), Layer.provide(ConfigGraph), Layer.provide(WorkspaceGraph));
411
+ const DepsRegenDefault = makeDepsRegenDefault();
386
412
 
387
413
  //#endregion
388
- export { DepsRegen, DepsRegenBase, DepsRegenDefault, DepsRegenLive, isPureDependencyChangeset };
414
+ export { DepsRegen, DepsRegenDefault, DepsRegenLive, isPureDependencyChangeset, makeDepsRegenDefault };
@@ -23,18 +23,6 @@ import { Context, Effect, Layer } from "effect";
23
23
  * @see {@link GitHubLive} for the production layer
24
24
  * @see {@link makeGitHubTest} for constructing test layers
25
25
  */
26
- const _tag = Context.Tag("GitHubService");
27
- /**
28
- * Base class for GitHubService.
29
- *
30
- * @privateRemarks
31
- * This export is required for api-extractor documentation generation.
32
- * Effect's Context.Tag creates an anonymous base class that must be
33
- * explicitly exported to avoid "forgotten export" warnings. Do not delete.
34
- *
35
- * @internal
36
- */
37
- const GitHubServiceBase = _tag();
38
26
  /**
39
27
  * Effect service tag for GitHub API operations.
40
28
  *
@@ -43,7 +31,7 @@ const GitHubServiceBase = _tag();
43
31
  * service instance.
44
32
  *
45
33
  * @remarks
46
- * This tag follows the standard Effect `Context.Tag` pattern. Two layers
34
+ * This tag follows the standard Effect `Context.Service` pattern. Two layers
47
35
  * are provided out of the box:
48
36
  *
49
37
  * - {@link GitHubLive} — production layer backed by the GitHub REST API
@@ -90,11 +78,10 @@ const GitHubServiceBase = _tag();
90
78
  * @see {@link GitHubServiceShape} for the service interface
91
79
  * @see {@link GitHubLive} for the production layer
92
80
  * @see {@link makeGitHubTest} for creating test layers
93
- * @see {@link GitHubServiceBase} for the api-extractor base class
94
81
  *
95
82
  * @public
96
83
  */
97
- var GitHubService = class extends GitHubServiceBase {};
84
+ var GitHubService = class extends Context.Service()("GitHubService") {};
98
85
  /**
99
86
  * Production layer for {@link GitHubService}.
100
87
  *
@@ -175,4 +162,4 @@ function makeGitHubTest(responses) {
175
162
  }
176
163
 
177
164
  //#endregion
178
- export { GitHubLive, GitHubService, GitHubServiceBase, makeGitHubTest };
165
+ export { GitHubLive, GitHubService, makeGitHubTest };
@@ -20,7 +20,11 @@ const MaintenanceTriggerSchema = Schema.Struct({
20
20
  */
21
21
  const MaintenanceReasonSchema = Schema.Struct({
22
22
  /** Coupling that forced the release; `"unspecified"` when undetermined. */
23
- kind: Schema.Literal("fixed", "linked", "unspecified"),
23
+ kind: Schema.Literals([
24
+ "fixed",
25
+ "linked",
26
+ "unspecified"
27
+ ]),
24
28
  /** Triggering co-members; empty for `"unspecified"`. */
25
29
  triggers: Schema.Array(MaintenanceTriggerSchema)
26
30
  });
@@ -22,18 +22,6 @@ import { Context, Effect, Layer } from "effect";
22
22
  * @see {@link MarkdownServiceShape} for the service interface
23
23
  * @see {@link MarkdownLive} for the production layer
24
24
  */
25
- const _tag = Context.Tag("MarkdownService");
26
- /**
27
- * Base class for MarkdownService.
28
- *
29
- * @privateRemarks
30
- * This export is required for api-extractor documentation generation.
31
- * Effect's Context.Tag creates an anonymous base class that must be
32
- * explicitly exported to avoid "forgotten export" warnings. Do not delete.
33
- *
34
- * @internal
35
- */
36
- const MarkdownServiceBase = _tag();
37
25
  /**
38
26
  * Effect service tag for markdown parsing and stringification.
39
27
  *
@@ -42,7 +30,7 @@ const MarkdownServiceBase = _tag();
42
30
  * service instance.
43
31
  *
44
32
  * @remarks
45
- * This tag follows the standard Effect `Context.Tag` pattern. The
33
+ * This tag follows the standard Effect `Context.Service` pattern. The
46
34
  * {@link MarkdownLive} layer provides the default implementation backed by
47
35
  * the remark/unified pipeline. For testing, you can supply a custom layer
48
36
  * via `Layer.succeed(MarkdownService, { parse: ..., stringify: ... })`.
@@ -64,11 +52,10 @@ const MarkdownServiceBase = _tag();
64
52
  *
65
53
  * @see {@link MarkdownServiceShape} for the service interface
66
54
  * @see {@link MarkdownLive} for the production layer
67
- * @see {@link MarkdownServiceBase} for the api-extractor base class
68
55
  *
69
56
  * @public
70
57
  */
71
- var MarkdownService = class extends MarkdownServiceBase {};
58
+ var MarkdownService = class extends Context.Service()("MarkdownService") {};
72
59
  /**
73
60
  * Production layer for {@link MarkdownService}.
74
61
  *
@@ -103,4 +90,4 @@ const MarkdownLive = Layer.succeed(MarkdownService, {
103
90
  });
104
91
 
105
92
  //#endregion
106
- export { MarkdownLive, MarkdownService, MarkdownServiceBase };
93
+ export { MarkdownLive, MarkdownService };
@@ -3,9 +3,8 @@ import { ChangelogTransformer } from "../api/transformer.js";
3
3
  import { ConfigInspector } from "./config-inspector.js";
4
4
  import { deriveMaintenanceReason } from "./maintenance-reason.js";
5
5
  import { VersionFiles } from "../utils/version-files.js";
6
- import { Context, Effect, Layer } from "effect";
6
+ import { Context, Effect, FileSystem, Layer } from "effect";
7
7
  import { dirname, isAbsolute, join, relative } from "node:path";
8
- import { FileSystem } from "@effect/platform";
9
8
  import { applyReleasePlan } from "@changesets/apply-release-plan";
10
9
  import { readConfig } from "@changesets/config";
11
10
  import { getReleasePlan } from "@changesets/get-release-plan";
@@ -39,16 +38,8 @@ async function loadConfig(root, packages) {
39
38
  warnings: configResult.warnings
40
39
  };
41
40
  }
42
- const _tag = Context.Tag("ReleasePlanner");
43
- /**
44
- * Base class for {@link ReleasePlanner}.
45
- *
46
- * @privateRemarks Required export for api-extractor (anonymous Context.Tag base). Do not delete.
47
- * @internal
48
- */
49
- const ReleasePlannerBase = _tag();
50
41
  /** Effect service tag for the release planner. @public */
51
- var ReleasePlanner = class extends ReleasePlannerBase {};
42
+ var ReleasePlanner = class extends Context.Service()("ReleasePlanner") {};
52
43
  /** Build the service shape over a resolved {@link ConfigInspector} and {@link FileSystem.FileSystem}. */
53
44
  function makeShape(inspector, fs) {
54
45
  const plan = (root) => Effect.tryPromise({
@@ -316,7 +307,7 @@ function applyEffect(root, dryRun, changelogModules, inspector, fs) {
316
307
  });
317
308
  }
318
309
  const newVersionByName = new Map(plan.releases.map((r) => [r.name, r.newVersion]));
319
- const inspected = yield* inspector.inspect(root).pipe(Effect.catchAll((error) => Effect.logWarning(`Skipping versionFiles update: ${errMsg(error)}`).pipe(Effect.as(null))));
310
+ const inspected = yield* inspector.inspect(root).pipe(Effect.catch((error) => Effect.logWarning(`Skipping versionFiles update: ${errMsg(error)}`).pipe(Effect.as(null))));
320
311
  let versionFileUpdates = [];
321
312
  if (inspected) {
322
313
  const candidates = inspected.packages.filter((p) => p.versionFiles.length > 0);
@@ -346,4 +337,4 @@ function applyEffect(root, dryRun, changelogModules, inspector, fs) {
346
337
  }
347
338
 
348
339
  //#endregion
349
- export { ReleasePlanner, ReleasePlannerBase, ReleasePlannerLive, extractVersionBlock, makeReleasePlannerTest };
340
+ export { ReleasePlanner, ReleasePlannerLive, extractVersionBlock, makeReleasePlannerTest };
@@ -1,23 +1,5 @@
1
1
  //#region src/changesets/utils/commit-parser.ts
2
2
  /**
3
- * Conventional commit message parsing.
4
- *
5
- * Implements a parser for the
6
- * {@link https://www.conventionalcommits.org/ | Conventional Commits}
7
- * specification, extracting type, scope, breaking-change indicator,
8
- * description, and body from commit messages.
9
- *
10
- * @remarks
11
- * The parser uses a single regex pass for the first line, then splits
12
- * remaining lines to extract the body. Non-conventional messages are
13
- * returned with only the `description` field populated (set to the
14
- * full message text).
15
- *
16
- * @see {@link Changelog} for the public API that consumes parsed commits
17
- *
18
- * @internal
19
- */
20
- /**
21
3
  * Matches conventional commit format: `type(scope)!: description`.
22
4
  *
23
5
  * Capture groups:
@@ -2,33 +2,6 @@ import { sortDependencyRows } from "./dependency-table.js";
2
2
  import { Option } from "effect";
3
3
 
4
4
  //#region src/changesets/utils/dep-diff.ts
5
- /**
6
- * Compute per-workspace-package dependency-table rows from two
7
- * {@link WorkspaceStateSnapshot}s, resolving `catalog:` / `workspace:`
8
- * specifiers against each side's own catalogs and package versions BEFORE
9
- * comparing.
10
- *
11
- * @remarks
12
- * Operates on declared dependencies only (the `dependencies` /
13
- * `devDependencies` / `peerDependencies` / `optionalDependencies` fields
14
- * of each workspace's `package.json`). Lockfile-only movements
15
- * (resolved versions changing while declared ranges stay put) are
16
- * intentionally excluded — those happen on every `pnpm install` and
17
- * would generate constant noise.
18
- *
19
- * Each side carries its own catalogs and package versions, so a specifier
20
- * is resolved against the snapshot it belongs to: `catalog:silk` resolves
21
- * to that ref's `silk` catalog entry, `workspace:*` to that ref's target
22
- * package version. A row is emitted iff the two RESOLVED values differ (or
23
- * the dependency was added/removed) — a package that merely adopted a
24
- * `catalog:` specifier without changing the concrete version produces NO
25
- * row. When a side cannot resolve a specifier (no matching catalog entry,
26
- * plain range, etc.) it falls back to the raw specifier string.
27
- *
28
- * @see {@link DependencyTableRow} for the row schema
29
- * @see {@link WorkspaceStateSnapshot} for the input shape
30
- *
31
- */
32
5
  /** The em-dash sentinel (U+2014) used for added ("from") / removed ("to") cells. */
33
6
  const EM_DASH = "—";
34
7
  const DEP_TYPE_MAP = [
@@ -1,17 +1,18 @@
1
1
  import { GitError } from "../errors.js";
2
2
  import { Effect } from "effect";
3
3
  import { basename } from "node:path";
4
- import { execFileSync } from "node:child_process";
4
+ import { Git } from "@effected/git";
5
5
 
6
6
  //#region src/changesets/utils/git.ts
7
7
  /**
8
8
  * Git helpers for the changesets deps regen/detect orchestration.
9
9
  *
10
10
  * @remarks
11
- * `PointInTimeWorkspace` (from `workspaces-effect`) reads both sides of a
12
- * dependency diff over `CommandExecutor`. The one git operation it does not
13
- * cover is resolving the default `--from` ref — the merge-base with the base
14
- * branch which stays here as a synchronous `execFileSync` shell-out.
11
+ * `WorkspaceSnapshots` (from `@effected/workspaces`) reads both sides of a
12
+ * dependency diff. The one git operation it does not cover is resolving the
13
+ * default `--from` ref — the merge-base with the base branch — which is
14
+ * delegated to `@effected/git`'s `Git` service (spawning through
15
+ * `ChildProcessSpawner`, provided at the app edge).
15
16
  *
16
17
  * @internal
17
18
  */
@@ -22,34 +23,17 @@ import { execFileSync } from "node:child_process";
22
23
  * @internal
23
24
  */
24
25
  function gitMergeBase(cwd, base) {
25
- return Effect.try({
26
- try: () => execFileSync("git", [
27
- "merge-base",
28
- base,
29
- "HEAD"
30
- ], {
26
+ return Effect.gen(function* () {
27
+ return yield* (yield* Git).mergeBase(cwd, base, "HEAD").pipe(Effect.mapError((error) => new GitError({
28
+ command: `git merge-base ${base} HEAD`,
31
29
  cwd,
32
- encoding: "utf8",
33
- stdio: [
34
- "ignore",
35
- "pipe",
36
- "pipe"
37
- ]
38
- }).trim(),
39
- catch: (error) => {
40
- const stderr = error.stderr;
41
- const text = typeof stderr === "string" ? stderr : stderr?.toString() ?? "";
42
- return new GitError({
43
- command: `git merge-base ${base} HEAD`,
44
- cwd,
45
- reason: text.trim() || (error.message ?? String(error))
46
- });
47
- }
30
+ reason: error.message
31
+ })));
48
32
  });
49
33
  }
50
34
  /**
51
35
  * List the basenames of `.changeset/*.md` files tracked at `ref` (e.g. the
52
- * merge base), via `git ls-tree -r --name-only`. Used by `DepsRegen.plan()`
36
+ * merge base), via `git ls-tree -r`. Used by `DepsRegen.plan()`
53
37
  * to protect changesets authored by already-merged PRs from being deleted by
54
38
  * an unrelated branch's regen run (#258).
55
39
  *
@@ -65,28 +49,9 @@ function gitMergeBase(cwd, base) {
65
49
  * @internal
66
50
  */
67
51
  function gitListChangesetFilesAtRef(cwd, ref) {
68
- return Effect.sync(() => {
69
- try {
70
- const out = execFileSync("git", [
71
- "ls-tree",
72
- "-r",
73
- "--name-only",
74
- ref,
75
- "--",
76
- ".changeset"
77
- ], {
78
- cwd,
79
- encoding: "utf8",
80
- stdio: [
81
- "ignore",
82
- "pipe",
83
- "pipe"
84
- ]
85
- });
86
- return new Set(out.split(/\r?\n/).filter((line) => line.trim().length > 0).map((path) => basename(path)));
87
- } catch {
88
- return /* @__PURE__ */ new Set();
89
- }
52
+ return Effect.gen(function* () {
53
+ const entries = yield* (yield* Git).lsTree(cwd, ref, { pathspec: [".changeset"] }).pipe(Effect.catch(() => Effect.succeed([])));
54
+ return new Set(entries.filter((entry) => entry.path.trim().length > 0).map((entry) => basename(entry.path)));
90
55
  });
91
56
  }
92
57
 
@@ -1,18 +1,5 @@
1
1
  //#region src/changesets/utils/issue-refs.ts
2
2
  /**
3
- * GitHub issue reference parsing from commit messages.
4
- *
5
- * @remarks
6
- * Extracts categorized issue references (`Closes`, `Fixes`, `Refs`) from
7
- * commit message bodies. Supports multiple formats including with/without
8
- * colons, singular/plural keywords, and comma-separated issue lists.
9
- * Input is truncated to 10,000 characters to prevent ReDoS on adversarial input.
10
- *
11
- * @see {@link Changelog} for the public API that consumes issue references
12
- *
13
- * @internal
14
- */
15
- /**
16
3
  * Matches "closes #123" or "close: #456, #789" patterns (case-insensitive).
17
4
  *
18
5
  * Capture group `[1]` contains the comma-separated issue number list.
@@ -1,18 +1,5 @@
1
1
  //#region src/changesets/utils/logger.ts
2
2
  /**
3
- * Simple logging utility for changelog generation.
4
- *
5
- * @remarks
6
- * Provides environment-aware warning output:
7
- * - **GitHub Actions**: emits `::warning::` annotations that surface in
8
- * the Actions UI
9
- * - **Local development**: falls back to `console.warn`
10
- * - **Test environment**: suppressed entirely when `process.env.VITEST`
11
- * is set, preventing noisy test output
12
- *
13
- * @internal
14
- */
15
- /**
16
3
  * Log a warning message.
17
4
  *
18
5
  * @remarks
@@ -1,19 +1,5 @@
1
1
  //#region src/changesets/utils/markdown-link.ts
2
2
  /**
3
- * Markdown link extraction utilities.
4
- *
5
- * @remarks
6
- * Handles the inconsistency where `\@changesets/get-github-info` sometimes
7
- * returns markdown-formatted links (e.g., `[#17](https://...)`) instead
8
- * of plain URLs. Provides a regex pattern and extraction function to
9
- * normalize these values.
10
- *
11
- * @see {@link Changelog} for the public API that uses link extraction
12
- * during changelog generation
13
- *
14
- * @internal
15
- */
16
- /**
17
3
  * Pattern matching markdown link format: `[text](url)`.
18
4
  *
19
5
  * Capture groups:
@@ -1,47 +1,34 @@
1
1
  import { Effect } from "effect";
2
- import { PublishabilityDetector } from "workspaces-effect";
2
+ import { PublishabilityDetector } from "@effected/workspaces";
3
3
 
4
4
  //#region src/changesets/utils/publishability.ts
5
5
  /**
6
- * Publishability helpers for the changeset CLI commands.
7
- *
8
- * @remarks
9
- * Provides `listPublishablePackageNames`, a convenience wrapper around
10
- * {@link SilkPublishability} that returns a `Set<string>` of
11
- * publishable package names. Used by the `deps detect` and `deps regen`
12
- * commands to filter out workspace packages whose dependency changes
13
- * would never reach a release.
14
- *
15
- */
16
- /**
17
6
  * Compute the set of currently-publishable workspace package names.
18
7
  *
19
8
  * @remarks
20
9
  * Uses the currently-active {@link SilkPublishability} — wire the
21
10
  * {@link SilkPublishabilityDetectorLive} layer to get silk semantics.
22
11
  *
23
- * `root` is passed through verbatim to `detector.detect(pkg, root)` for every
24
- * packageit must be the project root (the directory containing
25
- * `.changeset/`), NOT the individual package's directory. The vanilla
26
- * `PublishabilityDetectorLive` and plain `SilkPublishabilityDetectorLive`
27
- * both ignore this argument, but the ignore/mode-aware
28
- * `PublishabilityDetectorAdaptiveLive` reads `.changeset/config.json`
29
- * relative to it, so passing a package subdirectory silently makes every
30
- * package resolve to "not publishable". Mirrors
31
- * {@link SilkPublishability.listPublishable}, which takes the same
12
+ * The kit's `PublishabilityDetector.detect` contract no longer receives the
13
+ * workspace root — the ignore/mode-aware `PublishabilityDetectorAdaptiveLive`
14
+ * derives the `.changeset/config.json` root per package from the package's
15
+ * own discovery coordinates (`pkg.path` ascended by `pkg.relativePath`).
16
+ * The `root` parameter is retained for signature stability
17
+ * but is no longer consulted. Mirrors
18
+ * {@link SilkPublishability.listPublishable}, which keeps the same inert
32
19
  * single-root parameter for the same reason.
33
20
  *
34
21
  * @param packages - The workspace packages to evaluate
35
- * @param root - Absolute path to the project root containing `.changeset/`
22
+ * @param _root - Retained for signature stability; no longer consulted
36
23
  * @returns An Effect yielding a `Set` of publishable package names
37
24
  *
38
25
  * @public
39
26
  */
40
- function listPublishablePackageNames(packages, root) {
27
+ function listPublishablePackageNames(packages, _root) {
41
28
  return Effect.gen(function* () {
42
29
  const detector = yield* PublishabilityDetector;
43
30
  const names = /* @__PURE__ */ new Set();
44
- for (const pkg of packages) if ((yield* detector.detect(pkg, root)).length > 0) names.add(pkg.name);
31
+ for (const pkg of packages) if ((yield* detector.detect(pkg)).length > 0) names.add(pkg.name);
45
32
  return names;
46
33
  });
47
34
  }
@@ -1,24 +1,5 @@
1
1
  //#region src/changesets/utils/strip-frontmatter.ts
2
2
  /**
3
- * Utility for stripping YAML frontmatter from changeset files.
4
- *
5
- * @remarks
6
- * Changeset `.md` files produced by `\@changesets/cli` contain YAML
7
- * frontmatter (delimited by `---`) specifying which packages are
8
- * affected and their version bump types. This frontmatter must be
9
- * removed before remark-lint processing, as remark-parse does not
10
- * handle YAML frontmatter natively without `remark-frontmatter`.
11
- *
12
- * The regex used is non-greedy (`[\s\S]*?`) to match only the first
13
- * frontmatter block and avoid stripping content after a second `---`
14
- * delimiter that might appear in the document body.
15
- *
16
- * @see {@link ChangesetLinter} for the public API that uses frontmatter
17
- * stripping during changeset validation
18
- *
19
- * @internal
20
- */
21
- /**
22
3
  * Strip leading YAML frontmatter from a markdown string.
23
4
  *
24
5
  * Removes the `---\n...\n---\n` block at the start of the content.