@savvy-web/silk-effects 0.6.1 → 1.0.1

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 (155) hide show
  1. package/README.md +48 -17
  2. package/_virtual/_rolldown/runtime.js +18 -0
  3. package/changesets/api/categories.js +247 -0
  4. package/changesets/api/changelog.js +134 -0
  5. package/changesets/api/dependency-table.js +163 -0
  6. package/changesets/api/linter.js +168 -0
  7. package/changesets/api/transformer.js +140 -0
  8. package/changesets/categories/index.js +299 -0
  9. package/changesets/categories/types.js +66 -0
  10. package/changesets/changelog/formatting.js +119 -0
  11. package/changesets/changelog/getDependencyReleaseLine.js +114 -0
  12. package/changesets/changelog/getReleaseLine.js +122 -0
  13. package/changesets/changelog/index.js +99 -0
  14. package/changesets/constants.js +43 -0
  15. package/changesets/errors.js +305 -0
  16. package/changesets/index.js +146 -0
  17. package/changesets/markdownlint/index.js +29 -0
  18. package/changesets/markdownlint/rules/content-structure.js +98 -0
  19. package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
  20. package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
  21. package/changesets/markdownlint/rules/required-sections.js +54 -0
  22. package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
  23. package/changesets/markdownlint/rules/utils.js +30 -0
  24. package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
  25. package/changesets/remark/plugins/contributor-footnotes.js +123 -0
  26. package/changesets/remark/plugins/deduplicate-items.js +30 -0
  27. package/changesets/remark/plugins/issue-link-refs.js +58 -0
  28. package/changesets/remark/plugins/merge-sections.js +43 -0
  29. package/changesets/remark/plugins/normalize-format.js +47 -0
  30. package/changesets/remark/plugins/reorder-sections.js +34 -0
  31. package/changesets/remark/presets.js +119 -0
  32. package/changesets/remark/rules/content-structure.js +22 -0
  33. package/changesets/remark/rules/dependency-table-format.js +40 -0
  34. package/changesets/remark/rules/heading-hierarchy.js +19 -0
  35. package/changesets/remark/rules/required-sections.js +17 -0
  36. package/changesets/remark/rules/uncategorized-content.js +31 -0
  37. package/changesets/schemas/changeset.js +146 -0
  38. package/changesets/schemas/dependency-table.js +189 -0
  39. package/changesets/schemas/git.js +69 -0
  40. package/changesets/schemas/github.js +175 -0
  41. package/changesets/schemas/options.js +182 -0
  42. package/changesets/schemas/package-scope.js +128 -0
  43. package/changesets/schemas/primitives.js +72 -0
  44. package/changesets/schemas/version-files.js +151 -0
  45. package/changesets/services/branch-analyzer.js +278 -0
  46. package/changesets/services/changelog.js +50 -0
  47. package/changesets/services/config-inspector.js +390 -0
  48. package/changesets/services/github.js +178 -0
  49. package/changesets/services/markdown.js +106 -0
  50. package/changesets/services/workspace-snapshot.js +182 -0
  51. package/changesets/utils/commit-parser.js +80 -0
  52. package/changesets/utils/dep-diff.js +77 -0
  53. package/changesets/utils/dependency-table.js +347 -0
  54. package/changesets/utils/issue-refs.js +101 -0
  55. package/changesets/utils/jsonpath.js +175 -0
  56. package/changesets/utils/logger.js +50 -0
  57. package/changesets/utils/markdown-link.js +57 -0
  58. package/changesets/utils/publishability.js +39 -0
  59. package/changesets/utils/remark-pipeline.js +79 -0
  60. package/changesets/utils/section-parser.js +94 -0
  61. package/changesets/utils/strip-frontmatter.js +46 -0
  62. package/changesets/utils/version-blocks.js +108 -0
  63. package/changesets/utils/version-files.js +336 -0
  64. package/changesets/utils/worktree-snapshot.js +142 -0
  65. package/changesets/vendor/github-info.js +55 -0
  66. package/commitlint/config/factory.js +69 -0
  67. package/commitlint/config/plugins.js +227 -0
  68. package/commitlint/config/rules.js +155 -0
  69. package/commitlint/config/schema.js +46 -0
  70. package/commitlint/detection/dco.js +53 -0
  71. package/commitlint/detection/scopes.js +45 -0
  72. package/commitlint/formatter/format.js +85 -0
  73. package/commitlint/formatter/messages.js +79 -0
  74. package/commitlint/hook/diagnostics/branch.js +36 -0
  75. package/commitlint/hook/diagnostics/cache.js +37 -0
  76. package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
  77. package/commitlint/hook/diagnostics/open-issues.js +56 -0
  78. package/commitlint/hook/diagnostics/package-manager.js +51 -0
  79. package/commitlint/hook/diagnostics/signing.js +107 -0
  80. package/commitlint/hook/envelope.js +46 -0
  81. package/commitlint/hook/output.js +45 -0
  82. package/commitlint/hook/parse-bash-command.js +105 -0
  83. package/commitlint/hook/rules/closes-trailer.js +31 -0
  84. package/commitlint/hook/rules/forbidden-content.js +32 -0
  85. package/commitlint/hook/rules/plan-leakage.js +36 -0
  86. package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
  87. package/commitlint/hook/rules/soft-wrap.js +37 -0
  88. package/commitlint/hook/rules/types.js +14 -0
  89. package/commitlint/hook/rules/verbosity.js +31 -0
  90. package/commitlint/hook/silence-logger.js +39 -0
  91. package/commitlint/index.js +146 -0
  92. package/commitlint/prompt/config.js +91 -0
  93. package/commitlint/prompt/emojis.js +74 -0
  94. package/commitlint/prompt/prompter.js +135 -0
  95. package/commitlint/static.js +73 -0
  96. package/errors/BiomeSyncError.js +21 -0
  97. package/errors/ChangesetConfigError.js +20 -0
  98. package/errors/ConfigNotFoundError.js +21 -0
  99. package/errors/SectionParseError.js +16 -0
  100. package/errors/SectionValidationError.js +16 -0
  101. package/errors/SectionWriteError.js +16 -0
  102. package/errors/TagFormatError.js +20 -0
  103. package/errors/ToolNotFoundError.js +11 -0
  104. package/errors/ToolResolutionError.js +11 -0
  105. package/errors/ToolVersionMismatchError.js +11 -0
  106. package/errors/VersioningDetectionError.js +20 -0
  107. package/errors/WorkspaceAnalysisError.js +21 -0
  108. package/index.d.ts +9743 -8380
  109. package/index.js +36 -6657
  110. package/lint/Handler.js +39 -0
  111. package/lint/cli/sections.js +65 -0
  112. package/lint/cli/templates/markdownlint.gen.js +183 -0
  113. package/lint/config/Preset.js +152 -0
  114. package/lint/config/createConfig.js +89 -0
  115. package/lint/handlers/Biome.js +179 -0
  116. package/lint/handlers/Markdown.js +139 -0
  117. package/lint/handlers/PackageJson.js +130 -0
  118. package/lint/handlers/PnpmWorkspace.js +141 -0
  119. package/lint/handlers/ShellScripts.js +58 -0
  120. package/lint/handlers/TypeScript.js +134 -0
  121. package/lint/handlers/Yaml.js +167 -0
  122. package/lint/index.js +52 -0
  123. package/lint/utils/Command.js +285 -0
  124. package/lint/utils/Filter.js +100 -0
  125. package/lint/utils/Workspace.js +86 -0
  126. package/package.json +52 -63
  127. package/schemas/CommentStyle.js +16 -0
  128. package/schemas/ResolvedTool.js +63 -0
  129. package/schemas/SavvySections.js +113 -0
  130. package/schemas/SectionBlock.js +70 -0
  131. package/schemas/SectionDefinition.js +121 -0
  132. package/schemas/SectionResults.js +12 -0
  133. package/schemas/TagStrategySchemas.js +18 -0
  134. package/schemas/ToolDefinition.js +39 -0
  135. package/schemas/ToolResults.js +14 -0
  136. package/schemas/VersioningSchemas.js +95 -0
  137. package/schemas/WorkspaceAnalysisSchemas.js +190 -0
  138. package/services/BiomeSchemaSync.js +133 -0
  139. package/services/ChangesetConfig.js +78 -0
  140. package/services/ChangesetConfigReader.js +106 -0
  141. package/services/ConfigDiscovery.js +71 -0
  142. package/services/ManagedSection.js +288 -0
  143. package/services/SilkPublishability.js +193 -0
  144. package/services/SilkWorkspaceAnalyzer.js +213 -0
  145. package/services/TagStrategy.js +54 -0
  146. package/services/ToolDiscovery.js +229 -0
  147. package/services/VersioningStrategy.js +67 -0
  148. package/tsdoc-metadata.json +11 -11
  149. package/turbo/digest.js +127 -0
  150. package/turbo/errors.js +48 -0
  151. package/turbo/index.js +32 -0
  152. package/turbo/schemas/DryRun.js +57 -0
  153. package/turbo/schemas/results.js +61 -0
  154. package/turbo/services/TurboInspector.js +100 -0
  155. package/utils/ToolCommand.js +40 -0
@@ -0,0 +1,151 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/changesets/schemas/version-files.ts
4
+ /**
5
+ * Schemas for `versionFiles` configuration entries.
6
+ *
7
+ * @remarks
8
+ * `versionFiles` lets \@savvy-web/changesets update version fields in
9
+ * arbitrary JSON files (beyond `package.json`) during the
10
+ * `changeset version` command. Each entry specifies a glob pattern to
11
+ * match files and optional JSONPath expressions to locate version fields
12
+ * within those files.
13
+ *
14
+ * Two related schemas live here:
15
+ *
16
+ * - {@link VersionFileConfigSchema} — the **new** per-package entry shape
17
+ * used inside `packages[*].versionFiles`. It has no `package` field
18
+ * because the parent record key already names the owning package.
19
+ * - {@link LegacyVersionFileConfigSchema} — the **deprecated** entry shape
20
+ * used by the top-level `versionFiles[]` array on the changelog options.
21
+ * It carries an optional `package` field so the owner can be named
22
+ * inline. Accepted in 0.9.0 with a deprecation warning; removed in
23
+ * 1.0.0.
24
+ *
25
+ * @see {@link ChangesetOptionsSchema} for the consuming options shape
26
+ * @see {@link PackageScopeSchema} for the new per-package container
27
+ * @see {@link https://effect.website/docs/schema/introduction | Effect Schema documentation}
28
+ *
29
+ * @packageDocumentation
30
+ */
31
+ /**
32
+ * Schema for a JSONPath expression starting with `$.`.
33
+ *
34
+ * @remarks
35
+ * Supports property access (`$.foo.bar`), array wildcard (`$.foo[*].bar`),
36
+ * and array index access (`$.foo[0].bar`). The expression must begin with
37
+ * `$.` followed by at least one property segment.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * import { Schema } from "effect";
42
+ * import { JsonPathSchema } from "@savvy-web/changesets";
43
+ *
44
+ * // Succeeds — simple property access
45
+ * Schema.decodeUnknownSync(JsonPathSchema)("$.version");
46
+ *
47
+ * // Succeeds — nested property access
48
+ * Schema.decodeUnknownSync(JsonPathSchema)("$.metadata.version");
49
+ *
50
+ * // Throws ParseError — missing "$." prefix
51
+ * Schema.decodeUnknownSync(JsonPathSchema)("version");
52
+ * ```
53
+ *
54
+ * @see {@link VersionFileConfigSchema} which uses this for the `paths` field
55
+ *
56
+ * @public
57
+ */
58
+ const JsonPathSchema = Schema.String.pipe(Schema.pattern(/^\$\.[^.]/, { message: () => "JSONPath must start with \"$.\" followed by a property (e.g., \"$.version\", \"$.metadata.version\")" }));
59
+ /**
60
+ * Schema for a single version file configuration entry — **new shape**.
61
+ *
62
+ * @remarks
63
+ * Used inside `packages[*].versionFiles` (i.e., underneath a parent
64
+ * record key that names the owning package). Has no `package` field
65
+ * because the parent key already supplies that information.
66
+ *
67
+ * @example
68
+ * ```typescript
69
+ * import { Schema } from "effect";
70
+ * import { VersionFileConfigSchema } from "@savvy-web/changesets";
71
+ *
72
+ * const entry = Schema.decodeUnknownSync(VersionFileConfigSchema)({
73
+ * glob: "plugin/.claude-plugin/plugin.json",
74
+ * paths: ["$.version"],
75
+ * });
76
+ * ```
77
+ *
78
+ * @see {@link VersionFileConfig} for the inferred TypeScript type
79
+ * @see {@link LegacyVersionFileConfigSchema} for the deprecated shape
80
+ * used by the top-level `versionFiles[]` array
81
+ * @see {@link JsonPathSchema} for JSONPath validation rules
82
+ *
83
+ * @public
84
+ */
85
+ const VersionFileConfigSchema = Schema.Struct({
86
+ /** Glob pattern to match JSON files. */
87
+ glob: Schema.String.pipe(Schema.minLength(1)),
88
+ /** JSONPath expressions to locate version fields. Defaults to `["$.version"]`. */
89
+ paths: Schema.optional(Schema.Array(JsonPathSchema))
90
+ });
91
+ /**
92
+ * Schema for an array of new-shape {@link VersionFileConfigSchema} entries.
93
+ *
94
+ * @public
95
+ */
96
+ const VersionFilesSchema = Schema.Array(VersionFileConfigSchema);
97
+ /**
98
+ * Schema for a single version file configuration entry — **legacy shape**.
99
+ *
100
+ * @remarks
101
+ * DEPRECATED. Used only for parsing the top-level `versionFiles[]` array on
102
+ * the changelog options block. Carries an optional `package` field so the
103
+ * owning package can be named inline (alternatively, the runtime falls back
104
+ * to longest-prefix path matching against workspace package directories).
105
+ *
106
+ * Accepted in 0.9.0 with a deprecation warning emitted by `ConfigInspector`
107
+ * at config-load time. Removed in 1.0.0 — migrate to the new
108
+ * {@link VersionFileConfigSchema} under
109
+ * `changelog[1].packages[<name>].versionFiles`.
110
+ *
111
+ * @example
112
+ * ```typescript
113
+ * import { Schema } from "effect";
114
+ * import { LegacyVersionFileConfigSchema } from "@savvy-web/changesets";
115
+ *
116
+ * // Owner named inline via the `package` field
117
+ * const entry = Schema.decodeUnknownSync(LegacyVersionFileConfigSchema)({
118
+ * glob: "plugin/.claude-plugin/plugin.json",
119
+ * paths: ["$.version"],
120
+ * package: "@savvy-web/changesets",
121
+ * });
122
+ * ```
123
+ *
124
+ * @see {@link VersionFileConfigSchema} for the replacement (new) shape
125
+ * @see {@link LegacyVersionFileConfig} for the inferred TypeScript type
126
+ *
127
+ * @deprecated 0.9.0 — migrate to {@link VersionFileConfigSchema} inside
128
+ * `packages[*].versionFiles`. Removed in 1.0.0.
129
+ * @public
130
+ */
131
+ const LegacyVersionFileConfigSchema = Schema.Struct({
132
+ /** Glob pattern to match JSON files. */
133
+ glob: Schema.String.pipe(Schema.minLength(1)),
134
+ /** JSONPath expressions to locate version fields. Defaults to `["$.version"]`. */
135
+ paths: Schema.optional(Schema.Array(JsonPathSchema)),
136
+ /** Workspace package name to source the version from, bypassing path-based resolution. */
137
+ package: Schema.optional(Schema.String.pipe(Schema.minLength(1)))
138
+ });
139
+ /**
140
+ * Schema for an array of legacy {@link LegacyVersionFileConfigSchema}
141
+ * entries — used by the deprecated top-level `versionFiles[]` array on
142
+ * `ChangesetOptionsSchema`.
143
+ *
144
+ * @deprecated 0.9.0 — migrate to {@link VersionFilesSchema} inside
145
+ * `packages[*].versionFiles`. Removed in 1.0.0.
146
+ * @public
147
+ */
148
+ const LegacyVersionFilesSchema = Schema.Array(LegacyVersionFileConfigSchema);
149
+
150
+ //#endregion
151
+ export { JsonPathSchema, LegacyVersionFileConfigSchema, LegacyVersionFilesSchema, VersionFileConfigSchema, VersionFilesSchema };
@@ -0,0 +1,278 @@
1
+ import { GitError } from "../errors.js";
2
+ import { ConfigInspector } from "./config-inspector.js";
3
+ import { Context, Effect, Layer } from "effect";
4
+ import { execFileSync } from "node:child_process";
5
+
6
+ //#region src/changesets/services/branch-analyzer.ts
7
+ /**
8
+ * `BranchAnalyzer` service — combine a git diff against the base branch with
9
+ * the per-file classification produced by {@link ConfigInspector}.
10
+ *
11
+ * @remarks
12
+ * This is the single call that the changeset-manager agent uses during its
13
+ * inventory step: one invocation returns the diff, the per-file package
14
+ * attribution, the set of packages affected by the branch, and the list of
15
+ * paths that did not map to any known release surface (so the agent can ask
16
+ * the user about them rather than silently excluding).
17
+ *
18
+ * Base-branch resolution order (highest priority first):
19
+ *
20
+ * 1. Explicit `opts.baseBranch` passed to {@link BranchAnalyzerShape.analyzeBranch}.
21
+ * 2. `baseBranch` from `.changeset/config.json` (surfaced via the inspector).
22
+ * 3. The branch `origin/HEAD` points to (`git symbolic-ref refs/remotes/origin/HEAD`).
23
+ * 4. `"main"` as a final fallback.
24
+ *
25
+ * Diff resolution covers everything the user might commit before merging:
26
+ *
27
+ * 1. `git merge-base <base> HEAD` finds the common ancestor.
28
+ * 2. `git diff --name-status <merge-base>` (working tree vs merge-base)
29
+ * returns every committed, staged, AND unstaged change since the
30
+ * branch diverged. The two-arg `<merge-base>...HEAD` form would miss
31
+ * work-in-progress — what the user has open in their editor right
32
+ * now is exactly the state the agent needs to document.
33
+ * 3. `git ls-files --others --exclude-standard` adds untracked files
34
+ * (entirely new files not yet `git add`-ed). Each is reported with
35
+ * status `"added"`.
36
+ *
37
+ * The two results are deduped by path before classification. Renames
38
+ * are reported as `"renamed"` with the new path; the old path is
39
+ * discarded since the classifier needs a single canonical path per
40
+ * file.
41
+ *
42
+ * @see {@link BranchAnalyzer} for the service tag
43
+ * @see {@link BranchAnalyzerLive} for the production layer
44
+ * @see {@link ConfigInspector} for the underlying classification service
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+ const _tag = Context.Tag("BranchAnalyzer");
49
+ /**
50
+ * Base class for {@link BranchAnalyzer}.
51
+ *
52
+ * @privateRemarks
53
+ * Effect's `Context.Tag` creates an anonymous base class that api-extractor
54
+ * cannot follow without an explicit export. Do not delete.
55
+ *
56
+ * @internal
57
+ */
58
+ const BranchAnalyzerBase = _tag();
59
+ /**
60
+ * Effect service tag for {@link BranchAnalyzerShape}.
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * import { Effect } from "effect";
65
+ * import { BranchAnalyzer, BranchAnalyzerLive, ConfigInspectorLive } from "@savvy-web/changesets";
66
+ *
67
+ * const program = Effect.gen(function* () {
68
+ * const analyzer = yield* BranchAnalyzer;
69
+ * const analysis = yield* analyzer.analyzeBranch(process.cwd());
70
+ * return analysis.packagesAffected;
71
+ * });
72
+ *
73
+ * Effect.runPromise(
74
+ * program.pipe(
75
+ * Effect.provide(BranchAnalyzerLive),
76
+ * Effect.provide(ConfigInspectorLive),
77
+ * // ... + ChangesetConfigReaderLive + WorkspacesLive + NodeContext.layer
78
+ * ),
79
+ * );
80
+ * ```
81
+ *
82
+ * @public
83
+ */
84
+ var BranchAnalyzer = class extends BranchAnalyzerBase {};
85
+ /**
86
+ * Invoke `git` with the given args under `cwd` and return stdout. On
87
+ * non-zero exit (or any throw), maps to a {@link GitError} carrying the
88
+ * command, cwd, and captured stderr.
89
+ */
90
+ function runGit(cwd, args) {
91
+ return Effect.try({
92
+ try: () => execFileSync("git", args, {
93
+ cwd,
94
+ encoding: "utf8",
95
+ stdio: [
96
+ "ignore",
97
+ "pipe",
98
+ "pipe"
99
+ ]
100
+ }),
101
+ catch: (error) => {
102
+ const e = error;
103
+ const stderr = typeof e.stderr === "string" ? e.stderr : e.stderr?.toString() ?? "";
104
+ return new GitError({
105
+ command: `git ${args.join(" ")}`,
106
+ cwd,
107
+ reason: stderr.trim() || e.message || String(error)
108
+ });
109
+ }
110
+ });
111
+ }
112
+ const STATUS_MAP = {
113
+ A: "added",
114
+ M: "modified",
115
+ D: "deleted",
116
+ R: "renamed",
117
+ C: "copied",
118
+ T: "typechange",
119
+ U: "unmerged"
120
+ };
121
+ function statusFromCode(code) {
122
+ return STATUS_MAP[code.charAt(0)] ?? "unknown";
123
+ }
124
+ /**
125
+ * Parse `git diff --name-status -z` output into one entry per changed file.
126
+ *
127
+ * `-z` separates fields with NUL bytes and avoids the per-record `\n`
128
+ * delimiter, so paths containing spaces or special characters round-trip
129
+ * cleanly. Rename and copy entries occupy three NUL-separated tokens
130
+ * (status, old path, new path) instead of two; everything else is two.
131
+ */
132
+ function parseNameStatus(output) {
133
+ if (output.length === 0) return [];
134
+ const tokens = output.split("\0");
135
+ if (tokens[tokens.length - 1] === "") tokens.pop();
136
+ const entries = [];
137
+ for (let i = 0; i < tokens.length;) {
138
+ const code = tokens[i] ?? "";
139
+ if (code.length === 0) {
140
+ /* v8 ignore next 2 -- defensive guard; trailing empties stripped at parse time */
141
+ i += 1;
142
+ continue;
143
+ }
144
+ const status = statusFromCode(code);
145
+ if (status === "renamed" || status === "copied") {
146
+ const newPath = tokens[i + 2] ?? "";
147
+ if (newPath.length > 0) entries.push({
148
+ path: newPath,
149
+ status
150
+ });
151
+ i += 3;
152
+ } else {
153
+ const path = tokens[i + 1] ?? "";
154
+ if (path.length > 0) entries.push({
155
+ path,
156
+ status
157
+ });
158
+ i += 2;
159
+ }
160
+ }
161
+ return entries;
162
+ }
163
+ /**
164
+ * Parse a NUL-separated list of paths (e.g., `git ls-files -z` output) into
165
+ * an array of strings, dropping any trailing empty entry left by the final
166
+ * NUL byte.
167
+ */
168
+ function parseNulSeparatedPaths(output) {
169
+ if (output.length === 0) return [];
170
+ const tokens = output.split("\0");
171
+ if (tokens[tokens.length - 1] === "") tokens.pop();
172
+ return tokens.filter((t) => t.length > 0);
173
+ }
174
+ /**
175
+ * Resolve the base branch using the documented priority order.
176
+ */
177
+ function resolveBaseBranch(opts) {
178
+ if (opts.explicit && opts.explicit.length > 0) return Effect.succeed(opts.explicit);
179
+ if (opts.configBaseBranch && opts.configBaseBranch !== "main") return Effect.succeed(opts.configBaseBranch);
180
+ return runGit(opts.cwd, [
181
+ "symbolic-ref",
182
+ "--quiet",
183
+ "--short",
184
+ "refs/remotes/origin/HEAD"
185
+ ]).pipe(
186
+ /* v8 ignore start -- callback only reachable with a remote that exposes origin/HEAD */
187
+ Effect.map((stdout) => {
188
+ const trimmed = stdout.trim();
189
+ return trimmed.length > 0 ? trimmed.replace(/^origin\//, "") : opts.configBaseBranch;
190
+ }),
191
+ /* v8 ignore stop */
192
+ Effect.catchAll(() => Effect.succeed(opts.configBaseBranch))
193
+ );
194
+ }
195
+ function makeShape(inspector) {
196
+ const analyzeBranch = (cwd, opts) => Effect.gen(function* () {
197
+ const inspected = yield* inspector.inspect(cwd);
198
+ const baseBranch = yield* resolveBaseBranch({
199
+ explicit: opts?.baseBranch,
200
+ configBaseBranch: inspected.baseBranch,
201
+ cwd
202
+ });
203
+ const mergeBaseSha = (yield* runGit(cwd, [
204
+ "merge-base",
205
+ baseBranch,
206
+ "HEAD"
207
+ ])).trim();
208
+ const diffEntries = parseNameStatus(yield* runGit(cwd, [
209
+ "diff",
210
+ "--name-status",
211
+ "-z",
212
+ mergeBaseSha
213
+ ]));
214
+ const untrackedEntries = parseNulSeparatedPaths(yield* runGit(cwd, [
215
+ "ls-files",
216
+ "-z",
217
+ "--others",
218
+ "--exclude-standard"
219
+ ])).map((path) => ({
220
+ path,
221
+ status: "added"
222
+ }));
223
+ const seen = /* @__PURE__ */ new Set();
224
+ const rawEntries = [];
225
+ for (const e of diffEntries) {
226
+ if (seen.has(e.path)) continue;
227
+ seen.add(e.path);
228
+ rawEntries.push(e);
229
+ }
230
+ for (const e of untrackedEntries) {
231
+ if (seen.has(e.path)) continue;
232
+ seen.add(e.path);
233
+ rawEntries.push(e);
234
+ }
235
+ const paths = rawEntries.map((e) => e.path);
236
+ const classifications = yield* inspector.classify(cwd, paths);
237
+ const files = rawEntries.map((entry, idx) => {
238
+ const c = classifications[idx];
239
+ return {
240
+ path: entry.path,
241
+ status: entry.status,
242
+ package: c?.package ?? null,
243
+ reason: c?.reason ?? null
244
+ };
245
+ });
246
+ return {
247
+ baseBranch,
248
+ mergeBaseSha,
249
+ files,
250
+ packagesAffected: Array.from(new Set(files.map((f) => f.package).filter((p) => p !== null))).sort(),
251
+ unmappedFiles: files.filter((f) => f.package === null).map((f) => f.path)
252
+ };
253
+ });
254
+ return { analyzeBranch };
255
+ }
256
+ /**
257
+ * Live layer for {@link BranchAnalyzer}.
258
+ *
259
+ * Requires {@link ConfigInspector} (which in turn requires
260
+ * `ChangesetConfigReader` and `WorkspaceDiscovery`).
261
+ *
262
+ * @public
263
+ */
264
+ const BranchAnalyzerLive = Layer.effect(BranchAnalyzer, Effect.gen(function* () {
265
+ return makeShape(yield* ConfigInspector);
266
+ }));
267
+ /**
268
+ * Test factory — build a {@link BranchAnalyzer} that returns a fixed
269
+ * {@link BranchAnalysis} for any input.
270
+ *
271
+ * @public
272
+ */
273
+ function makeBranchAnalyzerTest(fixed) {
274
+ return Layer.succeed(BranchAnalyzer, { analyzeBranch: () => Effect.succeed(fixed) });
275
+ }
276
+
277
+ //#endregion
278
+ export { BranchAnalyzer, BranchAnalyzerBase, BranchAnalyzerLive, makeBranchAnalyzerTest };
@@ -0,0 +1,50 @@
1
+ import { Context } from "effect";
2
+
3
+ //#region src/changesets/services/changelog.ts
4
+ const _tag = Context.Tag("ChangelogService");
5
+ /**
6
+ * Base class for ChangelogService.
7
+ *
8
+ * @privateRemarks
9
+ * This export is required for api-extractor documentation generation.
10
+ * Effect's Context.Tag creates an anonymous base class that must be
11
+ * explicitly exported to avoid "forgotten export" warnings. Do not delete.
12
+ *
13
+ * @internal
14
+ */
15
+ const ChangelogServiceBase = _tag();
16
+ /**
17
+ * Effect service tag for changelog formatting.
18
+ *
19
+ * Provides dependency-injected access to the two Changesets API formatter
20
+ * functions: `formatReleaseLine` and `formatDependencyReleaseLine`.
21
+ *
22
+ * @remarks
23
+ * This is an abstract service tag — it has no default `Layer`. The concrete
24
+ * implementation is the `\@savvy-web/changesets/changelog` subpath export,
25
+ * which wires the formatting logic through `Effect.runPromise` for the
26
+ * Changesets CLI. For direct Effect usage, build your own layer or use
27
+ * the class-based `Changelog` wrapper.
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * import { Effect } from "effect";
32
+ * import type { ChangesetOptions } from "\@savvy-web/changesets";
33
+ * import { ChangelogService, GitHubLive, MarkdownLive } from "\@savvy-web/changesets";
34
+ *
35
+ * const program = Effect.gen(function* () {
36
+ * const changelog = yield* ChangelogService;
37
+ * const line = yield* changelog.formatReleaseLine(changeset, "minor", options);
38
+ * return line;
39
+ * });
40
+ * ```
41
+ *
42
+ * @see {@link ChangelogServiceShape} for the service interface
43
+ * @see {@link ChangelogServiceBase} for the api-extractor base class
44
+ *
45
+ * @public
46
+ */
47
+ var ChangelogService = class extends ChangelogServiceBase {};
48
+
49
+ //#endregion
50
+ export { ChangelogService, ChangelogServiceBase };