@savvy-web/silk-effects 0.6.0 → 1.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 (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,69 @@
1
+ import { detectDCO } from "../detection/dco.js";
2
+ import { COMMIT_TYPES } from "./rules.js";
3
+ import { createPromptConfig } from "../prompt/config.js";
4
+ import { createScopeEnumRule, silkPlugin } from "./plugins.js";
5
+
6
+ //#region src/commitlint/config/factory.ts
7
+ /**
8
+ * Configuration factory implementation.
9
+ *
10
+ * @internal
11
+ */
12
+ /**
13
+ * Create a commitlint configuration with auto-detection.
14
+ *
15
+ * @remarks
16
+ * This function is the internal implementation for {@link CommitlintConfig.silk}.
17
+ * It receives already-validated options and performs the actual configuration
18
+ * assembly with auto-detection of repository settings.
19
+ *
20
+ * @param options - Resolved configuration options (after Zod parsing)
21
+ * @returns Commitlint UserConfig object
22
+ *
23
+ * @internal
24
+ */
25
+ function createConfig(options) {
26
+ const cwd = options.cwd ?? process.cwd();
27
+ const dco = process.env.COMMITLINT_SKIP_DCO === "1" || process.env.COMMITLINT_SKIP_DCO === "true" ? false : options.dco ?? detectDCO(cwd);
28
+ const scopes = options.scopes ?? [];
29
+ const allScopes = [...new Set([...scopes, ...options.additionalScopes ?? []])].sort();
30
+ const rules = {
31
+ "body-max-line-length": [
32
+ 2,
33
+ "always",
34
+ options.bodyMaxLineLength
35
+ ],
36
+ "type-enum": [
37
+ 2,
38
+ "always",
39
+ [...COMMIT_TYPES]
40
+ ],
41
+ "subject-case": [0]
42
+ };
43
+ if (allScopes.length > 0) {
44
+ rules["scope-enum"] = [0];
45
+ rules["silk/tdd-scope"] = [0];
46
+ rules["silk/scope-enum"] = [2, "always"];
47
+ } else rules["silk/tdd-scope"] = [2, "always"];
48
+ const plugins = [allScopes.length > 0 ? { rules: {
49
+ ...silkPlugin.rules,
50
+ "silk/scope-enum": createScopeEnumRule(allScopes)
51
+ } } : silkPlugin];
52
+ if (dco) rules["silk/signed-off-by"] = [2, "always"];
53
+ if (options.noMarkdown) {
54
+ rules["silk/body-no-markdown"] = [2, "always"];
55
+ rules["silk/subject-no-markdown"] = [2, "always"];
56
+ }
57
+ return {
58
+ extends: ["@commitlint/config-conventional"],
59
+ plugins,
60
+ rules,
61
+ prompt: createPromptConfig({
62
+ emojis: options.emojis,
63
+ ...allScopes.length > 0 && { scopes: allScopes }
64
+ })
65
+ };
66
+ }
67
+
68
+ //#endregion
69
+ export { createConfig };
@@ -0,0 +1,227 @@
1
+ import { TDD_SCOPE_PATTERN } from "./rules.js";
2
+
3
+ //#region src/commitlint/config/plugins.ts
4
+ /**
5
+ * Custom commitlint plugin rules.
6
+ *
7
+ * @remarks
8
+ * These rules help enforce plain-text commit messages by rejecting
9
+ * common markdown formatting patterns that AI agents tend to add.
10
+ *
11
+ * @internal
12
+ */
13
+ /**
14
+ * Patterns that indicate markdown formatting in commit messages.
15
+ *
16
+ * @internal
17
+ */
18
+ const MARKDOWN_PATTERNS = {
19
+ /** Markdown headers (# Header, ## Header, etc.) */
20
+ headers: /^#{1,6}\s/m,
21
+ /** Markdown bullet lists (- item, * item) */
22
+ bullets: /^[\t ]*[-*]\s/m,
23
+ /** Markdown numbered lists (1. item, 2. item) */
24
+ numberedLists: /^[\t ]*\d+\.\s/m,
25
+ /** Markdown code fences (triple backticks) */
26
+ codeFences: /```/,
27
+ /** Markdown inline code (`code`) - only flag if excessive */
28
+ inlineCode: /`[^`]+`/g,
29
+ /** Markdown bold (**text** or __text__) */
30
+ bold: /(\*\*|__)[^*_]+(\*\*|__)/,
31
+ /** Markdown italic (*text* or _text_) - be careful not to match normal underscores */
32
+ italic: /(?<!\w)\*[^*]+\*(?!\w)/,
33
+ /** Markdown links [text](url) */
34
+ links: /\[.+?\]\(.+?\)/,
35
+ /** Markdown horizontal rules (---, ***, ___) */
36
+ horizontalRules: /^[-*_]{3,}$/m
37
+ };
38
+ /**
39
+ * Check if text contains markdown formatting.
40
+ *
41
+ * @param text - Text to check
42
+ * @returns Object with detected patterns
43
+ */
44
+ function detectMarkdown(text) {
45
+ const detected = [];
46
+ if (MARKDOWN_PATTERNS.headers.test(text)) detected.push("headers (#)");
47
+ if (MARKDOWN_PATTERNS.numberedLists.test(text)) detected.push("numbered lists (1.)");
48
+ if (MARKDOWN_PATTERNS.codeFences.test(text)) detected.push("code fences (```)");
49
+ if (MARKDOWN_PATTERNS.bold.test(text)) detected.push("bold (**text**)");
50
+ if (MARKDOWN_PATTERNS.links.test(text)) detected.push("links ([text](url))");
51
+ if (MARKDOWN_PATTERNS.horizontalRules.test(text)) detected.push("horizontal rules (---)");
52
+ const inlineCodeMatches = text.match(MARKDOWN_PATTERNS.inlineCode);
53
+ if (inlineCodeMatches && inlineCodeMatches.length > 2) detected.push("excessive inline code (`code`)");
54
+ return {
55
+ hasMarkdown: detected.length > 0,
56
+ patterns: detected
57
+ };
58
+ }
59
+ /**
60
+ * Rule: body-no-markdown
61
+ *
62
+ * @remarks
63
+ * Rejects commit message bodies that contain markdown formatting.
64
+ * This helps ensure commit messages are plain text and readable
65
+ * in terminals, git log, and other tools that don't render markdown.
66
+ *
67
+ * Simple unordered lists (`-` or `*`) are allowed for readability.
68
+ *
69
+ * @example
70
+ * ```
71
+ * // Invalid - contains markdown headers
72
+ * feat: add feature
73
+ *
74
+ * ## Summary
75
+ * This adds a new feature.
76
+ *
77
+ * // Valid - plain text with lists
78
+ * feat: add feature
79
+ *
80
+ * Added new feature and fixed related bug:
81
+ * - Implemented user authentication
82
+ * - Fixed session timeout issue
83
+ * ```
84
+ */
85
+ const bodyNoMarkdown = (parsed) => {
86
+ const body = parsed.body;
87
+ if (!body) return [true, ""];
88
+ const { hasMarkdown, patterns } = detectMarkdown(body);
89
+ if (hasMarkdown) return [false, `body contains markdown formatting: ${patterns.join(", ")}`];
90
+ return [true, ""];
91
+ };
92
+ /**
93
+ * Rule: subject-no-markdown
94
+ *
95
+ * @remarks
96
+ * Rejects commit message subjects (first line) that contain markdown.
97
+ * Subjects should be plain text without any formatting.
98
+ */
99
+ const subjectNoMarkdown = (parsed) => {
100
+ const subject = parsed.subject;
101
+ if (!subject) return [true, ""];
102
+ const { hasMarkdown, patterns } = detectMarkdown(subject);
103
+ if (hasMarkdown) return [false, `subject contains markdown formatting: ${patterns.join(", ")}`];
104
+ return [true, ""];
105
+ };
106
+ /**
107
+ * Rule: body-prose-only
108
+ *
109
+ * @remarks
110
+ * A stricter rule that requires commit bodies to be prose paragraphs,
111
+ * rejecting any list-like structures even without markdown markers.
112
+ * Checks for lines that look like list items.
113
+ */
114
+ const bodyProseOnly = (parsed) => {
115
+ const body = parsed.body;
116
+ if (!body) return [true, ""];
117
+ if (/^[\t ]*(?:[-*•]|\d+[.):])\s/m.test(body)) return [false, "body should be prose paragraphs, not lists"];
118
+ return [true, ""];
119
+ };
120
+ /**
121
+ * Rule: signed-off-by
122
+ *
123
+ * @remarks
124
+ * Case-insensitive check for DCO signoff trailer. Accepts both
125
+ * "Signed-off-by:" and "signed-off-by:" (and any other casing).
126
+ *
127
+ * This replaces the built-in commitlint signed-off-by rule which
128
+ * is case-sensitive.
129
+ */
130
+ const signedOffBy = (parsed) => {
131
+ const raw = parsed.raw;
132
+ if (!raw) return [false, "message must be signed off"];
133
+ if (/^signed-off-by:\s*.+$/im.test(raw)) return [true, ""];
134
+ return [false, "message must be signed off"];
135
+ };
136
+ /**
137
+ * Rule: tdd-scope
138
+ *
139
+ * @remarks
140
+ * Enforces scope format for TDD commits. Non-TDD commits pass unconditionally.
141
+ * TDD commits require a scope in the format `<goalId>:<state>` where:
142
+ * - goalId is a numeric ID (one or more digits)
143
+ * - state is one of: spike, red, green, refactor
144
+ *
145
+ * @example
146
+ * ```
147
+ * // Valid - numeric goalId with valid state
148
+ * tdd(7:green): implement feature
149
+ * tdd(12:spike): research approach
150
+ *
151
+ * // Invalid - no scope
152
+ * tdd: implement feature
153
+ *
154
+ * // Invalid - missing state
155
+ * tdd(7): implement feature
156
+ *
157
+ * // Invalid - non-numeric goalId
158
+ * tdd(feature:green): implement feature
159
+ *
160
+ * // Invalid - invalid state
161
+ * tdd(7:done): implement feature
162
+ * ```
163
+ */
164
+ function checkTddScope(scope) {
165
+ if (!scope) return [false, "tdd commits require a scope in the format <goalId>:<state>"];
166
+ if (!TDD_SCOPE_PATTERN.test(scope)) return [false, `tdd scope must match <digits>:(spike|red|green|refactor), got: ${scope}`];
167
+ return [true, ""];
168
+ }
169
+ const tddScope = (parsed) => {
170
+ if (parsed.type !== "tdd") return [true, ""];
171
+ return checkTddScope(parsed.scope);
172
+ };
173
+ /**
174
+ * Custom commitlint plugin with markdown prevention rules.
175
+ *
176
+ * @remarks
177
+ * This plugin provides rules to enforce plain-text commit messages.
178
+ * Rules are prefixed with `silk/` to namespace them.
179
+ *
180
+ * Available rules:
181
+ * - `silk/body-no-markdown`: Reject markdown in commit body
182
+ * - `silk/subject-no-markdown`: Reject markdown in commit subject
183
+ * - `silk/body-prose-only`: Require prose paragraphs (no lists)
184
+ * - `silk/signed-off-by`: Require DCO signoff
185
+ * - `silk/tdd-scope`: Enforce TDD scope format
186
+ *
187
+ * @internal
188
+ */
189
+ const silkPlugin = { rules: {
190
+ "silk/body-no-markdown": bodyNoMarkdown,
191
+ "silk/subject-no-markdown": subjectNoMarkdown,
192
+ "silk/body-prose-only": bodyProseOnly,
193
+ "silk/signed-off-by": signedOffBy,
194
+ "silk/tdd-scope": tddScope
195
+ } };
196
+ /**
197
+ * Factory function to create a scope enum rule.
198
+ *
199
+ * @remarks
200
+ * Creates a rule that validates commit scopes, with special handling for TDD commits.
201
+ * - For TDD commits: enforces scope format `<goalId>:<state>` where state is one of spike, red, green, refactor
202
+ * - For non-TDD commits: enforces scope is one of the provided project scopes
203
+ *
204
+ * This replaces the built-in commitlint `scope-enum` rule, avoiding duplicate error
205
+ * messages when a TDD commit has an invalid scope.
206
+ *
207
+ * @param scopes - Array of valid project scope strings (e.g. ["api", "cli"])
208
+ * @returns A Rule function that validates scopes
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * const rule = createScopeEnumRule(["api", "cli"]);
213
+ * const [valid, msg] = rule(parsedCommit);
214
+ * ```
215
+ *
216
+ * @internal
217
+ */
218
+ function createScopeEnumRule(scopes) {
219
+ return (parsed) => {
220
+ if (parsed.type === "tdd") return checkTddScope(parsed.scope);
221
+ if (!parsed.scope || !scopes.includes(parsed.scope)) return [false, `scope must be one of: ${scopes.join(", ")}`];
222
+ return [true, ""];
223
+ };
224
+ }
225
+
226
+ //#endregion
227
+ export { createScopeEnumRule, silkPlugin };
@@ -0,0 +1,155 @@
1
+ //#region src/commitlint/config/rules.ts
2
+ /**
3
+ * Allowed commit types for conventional commits.
4
+ *
5
+ * @remarks
6
+ * Extends the standard conventional commit types with:
7
+ * - `ai`: For AI/LLM agent document updates (CLAUDE.md, context files)
8
+ * - `release`: For release commits (version bumps, changelogs)
9
+ *
10
+ * Types are sorted alphabetically for consistent ordering.
11
+ *
12
+ * @public
13
+ */
14
+ const COMMIT_TYPES = [
15
+ "ai",
16
+ "build",
17
+ "chore",
18
+ "ci",
19
+ "docs",
20
+ "feat",
21
+ "fix",
22
+ "perf",
23
+ "refactor",
24
+ "release",
25
+ "revert",
26
+ "style",
27
+ "tdd",
28
+ "test"
29
+ ];
30
+ /**
31
+ * Pattern for valid TDD commit scope.
32
+ *
33
+ * @remarks
34
+ * TDD commits require a scope in the format: `<goalId>:<state>`
35
+ * where goalId is a numeric ID and state is one of: spike, red, green, refactor.
36
+ *
37
+ * Example: `tdd(42:red)` or `tdd(1:green)`
38
+ *
39
+ * @public
40
+ */
41
+ const TDD_SCOPE_PATTERN = /^\d+:(spike|red|green|refactor)$/;
42
+ /**
43
+ * Valid TDD states for commit scope.
44
+ *
45
+ * @remarks
46
+ * These states correspond to the phases of test-driven development:
47
+ * - spike: Research/exploration phase
48
+ * - red: Write failing test
49
+ * - green: Make test pass
50
+ * - refactor: Clean up code
51
+ *
52
+ * @public
53
+ */
54
+ const TDD_STATES = [
55
+ "spike",
56
+ "red",
57
+ "green",
58
+ "refactor"
59
+ ];
60
+ /**
61
+ * Commit type definitions with metadata for prompts and changelogs.
62
+ *
63
+ * @remarks
64
+ * Each definition provides human-readable text for interactive commit
65
+ * tools and changelog generation. The order here determines display
66
+ * order in prompts (most common types first).
67
+ *
68
+ * @public
69
+ */
70
+ const COMMIT_TYPE_DEFINITIONS = [
71
+ {
72
+ type: "ai",
73
+ description: "AI/LLM agent document updates (CLAUDE.md, context files)",
74
+ title: "AI Context"
75
+ },
76
+ {
77
+ type: "feat",
78
+ description: "A new feature",
79
+ title: "Features"
80
+ },
81
+ {
82
+ type: "fix",
83
+ description: "A bug fix",
84
+ title: "Bug Fixes"
85
+ },
86
+ {
87
+ type: "docs",
88
+ description: "Documentation only changes",
89
+ title: "Documentation"
90
+ },
91
+ {
92
+ type: "style",
93
+ description: "Code style changes (formatting, semicolons, etc)",
94
+ title: "Styles"
95
+ },
96
+ {
97
+ type: "refactor",
98
+ description: "Code change that neither fixes a bug nor adds a feature",
99
+ title: "Code Refactoring"
100
+ },
101
+ {
102
+ type: "perf",
103
+ description: "A code change that improves performance",
104
+ title: "Performance Improvements"
105
+ },
106
+ {
107
+ type: "test",
108
+ description: "Adding missing tests or correcting existing tests",
109
+ title: "Tests"
110
+ },
111
+ {
112
+ type: "tdd",
113
+ description: "TDD agent commit (goalId:state scope required)",
114
+ title: "TDD"
115
+ },
116
+ {
117
+ type: "build",
118
+ description: "Changes to build system or external dependencies",
119
+ title: "Builds"
120
+ },
121
+ {
122
+ type: "ci",
123
+ description: "Changes to CI configuration files and scripts",
124
+ title: "Continuous Integration"
125
+ },
126
+ {
127
+ type: "chore",
128
+ description: "Other changes that don't modify src or test files",
129
+ title: "Chores"
130
+ },
131
+ {
132
+ type: "revert",
133
+ description: "Reverts a previous commit",
134
+ title: "Reverts"
135
+ },
136
+ {
137
+ type: "release",
138
+ description: "Release commits (version bumps, changelogs)",
139
+ title: "Releases"
140
+ }
141
+ ];
142
+ /**
143
+ * DCO (Developer Certificate of Origin) signoff text.
144
+ *
145
+ * @remarks
146
+ * This is the standard DCO signoff prefix. When DCO is required,
147
+ * commit messages must include a trailer in the format:
148
+ * `Signed-off-by: Name <email>`
149
+ *
150
+ * @public
151
+ */
152
+ const DCO_SIGNOFF_TEXT = "Signed-off-by:";
153
+
154
+ //#endregion
155
+ export { COMMIT_TYPES, COMMIT_TYPE_DEFINITIONS, DCO_SIGNOFF_TEXT, TDD_SCOPE_PATTERN, TDD_STATES };
@@ -0,0 +1,46 @@
1
+ import { Schema } from "effect";
2
+
3
+ //#region src/commitlint/config/schema.ts
4
+ /**
5
+ * Effect Schemas for commitlint configuration options.
6
+ *
7
+ * @internal
8
+ */
9
+ /**
10
+ * Effect Schema for {@link ReleaseFormat}.
11
+ *
12
+ * @internal
13
+ */
14
+ const ReleaseFormatSchema = Schema.Literal("semver", "packages", "scoped");
15
+ /**
16
+ * Effect Schema for validating {@link ConfigOptions}.
17
+ *
18
+ * @remarks
19
+ * This schema validates and applies defaults to user-provided configuration.
20
+ * Used internally by {@link CommitlintConfig.silk} via {@link decodeConfigOptions}.
21
+ *
22
+ * @internal
23
+ */
24
+ const ConfigOptionsSchema = Schema.Struct({
25
+ dco: Schema.optional(Schema.Boolean),
26
+ scopes: Schema.optional(Schema.Array(Schema.String)),
27
+ additionalScopes: Schema.optional(Schema.Array(Schema.String)),
28
+ releaseFormat: Schema.optional(ReleaseFormatSchema),
29
+ emojis: Schema.optionalWith(Schema.Boolean, { default: () => false }),
30
+ bodyMaxLineLength: Schema.optionalWith(Schema.Number.pipe(Schema.positive()), { default: () => 300 }),
31
+ noMarkdown: Schema.optionalWith(Schema.Boolean, { default: () => true }),
32
+ cwd: Schema.optional(Schema.String)
33
+ });
34
+ /**
35
+ * Decode unknown input into {@link ResolvedConfigOptions}, applying defaults.
36
+ *
37
+ * @remarks
38
+ * Synchronous decoder that throws a `ParseError` on invalid input, mirroring
39
+ * the previous Zod `.parse()` contract used by {@link CommitlintConfig.silk}.
40
+ *
41
+ * @internal
42
+ */
43
+ const decodeConfigOptions = Schema.decodeUnknownSync(ConfigOptionsSchema);
44
+
45
+ //#endregion
46
+ export { decodeConfigOptions };
@@ -0,0 +1,53 @@
1
+ import { existsSync } from "node:fs";
2
+ import { dirname, join, resolve } from "node:path";
3
+
4
+ //#region src/commitlint/detection/dco.ts
5
+ /**
6
+ * DCO (Developer Certificate of Origin) detection module.
7
+ *
8
+ * @internal
9
+ */
10
+ /** Filename for the DCO file that indicates signoff is required. */
11
+ const DCO_FILENAME = "DCO";
12
+ /** Markers that indicate a project root directory. */
13
+ const ROOT_MARKERS = [
14
+ "pnpm-workspace.yaml",
15
+ ".git",
16
+ "package.json"
17
+ ];
18
+ /**
19
+ * Walk up the directory tree to find the project root.
20
+ *
21
+ * @param cwd - Starting directory
22
+ * @returns Project root path or null if not found
23
+ *
24
+ * @internal
25
+ */
26
+ function findProjectRoot(cwd) {
27
+ let dir = resolve(cwd);
28
+ while (true) {
29
+ for (const marker of ROOT_MARKERS) if (existsSync(join(dir, marker))) return dir;
30
+ const parent = dirname(dir);
31
+ if (parent === dir) return null;
32
+ dir = parent;
33
+ }
34
+ }
35
+ /**
36
+ * Detect if DCO signoff should be required.
37
+ *
38
+ * @remarks
39
+ * Checks for the presence of a DCO file at the repository root.
40
+ * Walks up the directory tree to find the project root by looking
41
+ * for workspace markers (pnpm-workspace.yaml, .git, package.json).
42
+ *
43
+ * @param cwd - Working directory (defaults to process.cwd())
44
+ * @returns `true` if DCO file exists at repo root, `false` otherwise
45
+ *
46
+ * @public
47
+ */
48
+ function detectDCO(cwd = process.cwd()) {
49
+ return existsSync(join(findProjectRoot(cwd) ?? cwd, DCO_FILENAME));
50
+ }
51
+
52
+ //#endregion
53
+ export { detectDCO };
@@ -0,0 +1,45 @@
1
+ import { Effect } from "effect";
2
+ import { WorkspaceDiscovery } from "workspaces-effect";
3
+
4
+ //#region src/commitlint/detection/scopes.ts
5
+ /**
6
+ * Workspace scope detection module.
7
+ *
8
+ * @internal
9
+ */
10
+ /**
11
+ * Extract scope-friendly name from a package name.
12
+ *
13
+ * @param name - Package name (e.g., `@scope/package-name` or `package-name`)
14
+ * @returns Package name without scope prefix
15
+ *
16
+ * @internal
17
+ */
18
+ function extractScopeName(name) {
19
+ if (name.startsWith("@")) return name.split("/")[1];
20
+ return name;
21
+ }
22
+ /**
23
+ * Detect package scopes from workspace configuration.
24
+ *
25
+ * @remarks
26
+ * Uses workspaces-effect to find all packages in the workspace and extracts
27
+ * their names as potential commit scopes. For scoped packages like
28
+ * `@scope/package-name`, only the package name portion is used as the scope.
29
+ *
30
+ * @returns Effect yielding sorted array of scope names, requires WorkspaceDiscovery
31
+ *
32
+ * @public
33
+ */
34
+ const detectScopes = Effect.gen(function* () {
35
+ const packages = yield* (yield* WorkspaceDiscovery).listPackages();
36
+ const scopes = [];
37
+ for (const pkg of packages) {
38
+ const scopeName = extractScopeName(pkg.name);
39
+ if (scopeName) scopes.push(scopeName);
40
+ }
41
+ return scopes.sort();
42
+ });
43
+
44
+ //#endregion
45
+ export { detectScopes };
@@ -0,0 +1,85 @@
1
+ import { getExplanation, getSuggestion } from "./messages.js";
2
+
3
+ //#region src/commitlint/formatter/format.ts
4
+ /**
5
+ * Custom formatter implementation for commitlint.
6
+ *
7
+ * @internal
8
+ */
9
+ /** Unicode symbol for error indicator. */
10
+ const ERROR_ICON = "✗";
11
+ /** Unicode symbol for warning indicator. */
12
+ const WARNING_ICON = "⚠";
13
+ /**
14
+ * Format a single rule result with explanation and suggestion.
15
+ *
16
+ * @param result - Rule result to format
17
+ * @param level - "error" or "warning"
18
+ * @returns Formatted string
19
+ *
20
+ * @internal
21
+ */
22
+ function formatRuleResult(result, level) {
23
+ const lines = [` ${level === "error" ? ERROR_ICON : WARNING_ICON} ${result.name}: ${result.message}`];
24
+ const explanation = getExplanation(result.name);
25
+ if (explanation) lines.push(` ${explanation}`);
26
+ const suggestion = getSuggestion(result.name);
27
+ if (suggestion) lines.push(` Suggestion: ${suggestion}`);
28
+ return lines.join("\n");
29
+ }
30
+ /**
31
+ * Build summary line for error/warning counts.
32
+ *
33
+ * @param errorCount - Number of errors
34
+ * @param warningCount - Number of warnings
35
+ * @returns Summary string
36
+ *
37
+ * @internal
38
+ */
39
+ function buildSummary(errorCount, warningCount) {
40
+ const parts = [];
41
+ if (errorCount > 0) parts.push(`${errorCount} error${errorCount === 1 ? "" : "s"}`);
42
+ if (warningCount > 0) parts.push(`${warningCount} warning${warningCount === 1 ? "" : "s"}`);
43
+ return `Found ${parts.join(", ")}.`;
44
+ }
45
+ /**
46
+ * Format lint results for display.
47
+ *
48
+ * @remarks
49
+ * This is the main formatter function exported for commitlint.
50
+ * It formats errors and warnings with helpful explanations and suggestions.
51
+ *
52
+ * @param formatterResult - Results from commitlint
53
+ * @returns Formatted output string
54
+ *
55
+ * @public
56
+ */
57
+ function format(formatterResult) {
58
+ const { results, options } = formatterResult;
59
+ const output = [];
60
+ let totalErrors = 0;
61
+ let totalWarnings = 0;
62
+ for (const result of results) {
63
+ if (!(!result.valid || result.errors.length > 0 || result.warnings.length > 0)) continue;
64
+ const firstLine = result.input.split("\n")[0];
65
+ output.push(`\nInput: "${firstLine}..."`);
66
+ output.push("");
67
+ for (const error of result.errors) {
68
+ output.push(formatRuleResult(error, "error"));
69
+ totalErrors++;
70
+ }
71
+ for (const warning of result.warnings) {
72
+ output.push(formatRuleResult(warning, "warning"));
73
+ totalWarnings++;
74
+ }
75
+ }
76
+ if (totalErrors > 0 || totalWarnings > 0) {
77
+ output.push("");
78
+ output.push(buildSummary(totalErrors, totalWarnings));
79
+ if (options?.helpUrl) output.push(`\nFor more information, see: ${options.helpUrl}`);
80
+ }
81
+ return output.join("\n");
82
+ }
83
+
84
+ //#endregion
85
+ export { format };