@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.
- package/README.md +48 -17
- package/_virtual/_rolldown/runtime.js +18 -0
- package/changesets/api/categories.js +247 -0
- package/changesets/api/changelog.js +134 -0
- package/changesets/api/dependency-table.js +163 -0
- package/changesets/api/linter.js +168 -0
- package/changesets/api/transformer.js +140 -0
- package/changesets/categories/index.js +299 -0
- package/changesets/categories/types.js +66 -0
- package/changesets/changelog/formatting.js +119 -0
- package/changesets/changelog/getDependencyReleaseLine.js +114 -0
- package/changesets/changelog/getReleaseLine.js +122 -0
- package/changesets/changelog/index.js +99 -0
- package/changesets/constants.js +43 -0
- package/changesets/errors.js +305 -0
- package/changesets/index.js +146 -0
- package/changesets/markdownlint/index.js +29 -0
- package/changesets/markdownlint/rules/content-structure.js +98 -0
- package/changesets/markdownlint/rules/dependency-table-format.js +170 -0
- package/changesets/markdownlint/rules/heading-hierarchy.js +61 -0
- package/changesets/markdownlint/rules/required-sections.js +54 -0
- package/changesets/markdownlint/rules/uncategorized-content.js +54 -0
- package/changesets/markdownlint/rules/utils.js +30 -0
- package/changesets/remark/plugins/aggregate-dependency-tables.js +47 -0
- package/changesets/remark/plugins/contributor-footnotes.js +123 -0
- package/changesets/remark/plugins/deduplicate-items.js +30 -0
- package/changesets/remark/plugins/issue-link-refs.js +58 -0
- package/changesets/remark/plugins/merge-sections.js +43 -0
- package/changesets/remark/plugins/normalize-format.js +47 -0
- package/changesets/remark/plugins/reorder-sections.js +34 -0
- package/changesets/remark/presets.js +119 -0
- package/changesets/remark/rules/content-structure.js +22 -0
- package/changesets/remark/rules/dependency-table-format.js +40 -0
- package/changesets/remark/rules/heading-hierarchy.js +19 -0
- package/changesets/remark/rules/required-sections.js +17 -0
- package/changesets/remark/rules/uncategorized-content.js +31 -0
- package/changesets/schemas/changeset.js +146 -0
- package/changesets/schemas/dependency-table.js +189 -0
- package/changesets/schemas/git.js +69 -0
- package/changesets/schemas/github.js +175 -0
- package/changesets/schemas/options.js +182 -0
- package/changesets/schemas/package-scope.js +128 -0
- package/changesets/schemas/primitives.js +72 -0
- package/changesets/schemas/version-files.js +151 -0
- package/changesets/services/branch-analyzer.js +278 -0
- package/changesets/services/changelog.js +50 -0
- package/changesets/services/config-inspector.js +390 -0
- package/changesets/services/github.js +178 -0
- package/changesets/services/markdown.js +106 -0
- package/changesets/services/workspace-snapshot.js +182 -0
- package/changesets/utils/commit-parser.js +80 -0
- package/changesets/utils/dep-diff.js +77 -0
- package/changesets/utils/dependency-table.js +347 -0
- package/changesets/utils/issue-refs.js +101 -0
- package/changesets/utils/jsonpath.js +175 -0
- package/changesets/utils/logger.js +50 -0
- package/changesets/utils/markdown-link.js +57 -0
- package/changesets/utils/publishability.js +39 -0
- package/changesets/utils/remark-pipeline.js +79 -0
- package/changesets/utils/section-parser.js +94 -0
- package/changesets/utils/strip-frontmatter.js +46 -0
- package/changesets/utils/version-blocks.js +108 -0
- package/changesets/utils/version-files.js +336 -0
- package/changesets/utils/worktree-snapshot.js +142 -0
- package/changesets/vendor/github-info.js +55 -0
- package/commitlint/config/factory.js +69 -0
- package/commitlint/config/plugins.js +227 -0
- package/commitlint/config/rules.js +155 -0
- package/commitlint/config/schema.js +46 -0
- package/commitlint/detection/dco.js +53 -0
- package/commitlint/detection/scopes.js +45 -0
- package/commitlint/formatter/format.js +85 -0
- package/commitlint/formatter/messages.js +79 -0
- package/commitlint/hook/diagnostics/branch.js +36 -0
- package/commitlint/hook/diagnostics/cache.js +37 -0
- package/commitlint/hook/diagnostics/commitlint-config.js +36 -0
- package/commitlint/hook/diagnostics/open-issues.js +56 -0
- package/commitlint/hook/diagnostics/package-manager.js +51 -0
- package/commitlint/hook/diagnostics/signing.js +107 -0
- package/commitlint/hook/envelope.js +46 -0
- package/commitlint/hook/output.js +45 -0
- package/commitlint/hook/parse-bash-command.js +105 -0
- package/commitlint/hook/rules/closes-trailer.js +31 -0
- package/commitlint/hook/rules/forbidden-content.js +32 -0
- package/commitlint/hook/rules/plan-leakage.js +36 -0
- package/commitlint/hook/rules/signing-flag-conflict.js +25 -0
- package/commitlint/hook/rules/soft-wrap.js +37 -0
- package/commitlint/hook/rules/types.js +14 -0
- package/commitlint/hook/rules/verbosity.js +31 -0
- package/commitlint/hook/silence-logger.js +39 -0
- package/commitlint/index.js +146 -0
- package/commitlint/prompt/config.js +91 -0
- package/commitlint/prompt/emojis.js +74 -0
- package/commitlint/prompt/prompter.js +135 -0
- package/commitlint/static.js +73 -0
- package/errors/BiomeSyncError.js +21 -0
- package/errors/ChangesetConfigError.js +20 -0
- package/errors/ConfigNotFoundError.js +21 -0
- package/errors/SectionParseError.js +16 -0
- package/errors/SectionValidationError.js +16 -0
- package/errors/SectionWriteError.js +16 -0
- package/errors/TagFormatError.js +20 -0
- package/errors/ToolNotFoundError.js +11 -0
- package/errors/ToolResolutionError.js +11 -0
- package/errors/ToolVersionMismatchError.js +11 -0
- package/errors/VersioningDetectionError.js +20 -0
- package/errors/WorkspaceAnalysisError.js +21 -0
- package/index.d.ts +9743 -8380
- package/index.js +36 -6657
- package/lint/Handler.js +39 -0
- package/lint/cli/sections.js +65 -0
- package/lint/cli/templates/markdownlint.gen.js +183 -0
- package/lint/config/Preset.js +152 -0
- package/lint/config/createConfig.js +89 -0
- package/lint/handlers/Biome.js +179 -0
- package/lint/handlers/Markdown.js +139 -0
- package/lint/handlers/PackageJson.js +130 -0
- package/lint/handlers/PnpmWorkspace.js +141 -0
- package/lint/handlers/ShellScripts.js +58 -0
- package/lint/handlers/TypeScript.js +134 -0
- package/lint/handlers/Yaml.js +167 -0
- package/lint/index.js +52 -0
- package/lint/utils/Command.js +285 -0
- package/lint/utils/Filter.js +100 -0
- package/lint/utils/Workspace.js +86 -0
- package/package.json +52 -63
- package/schemas/CommentStyle.js +16 -0
- package/schemas/ResolvedTool.js +63 -0
- package/schemas/SavvySections.js +113 -0
- package/schemas/SectionBlock.js +70 -0
- package/schemas/SectionDefinition.js +121 -0
- package/schemas/SectionResults.js +12 -0
- package/schemas/TagStrategySchemas.js +18 -0
- package/schemas/ToolDefinition.js +39 -0
- package/schemas/ToolResults.js +14 -0
- package/schemas/VersioningSchemas.js +95 -0
- package/schemas/WorkspaceAnalysisSchemas.js +190 -0
- package/services/BiomeSchemaSync.js +133 -0
- package/services/ChangesetConfig.js +78 -0
- package/services/ChangesetConfigReader.js +106 -0
- package/services/ConfigDiscovery.js +71 -0
- package/services/ManagedSection.js +288 -0
- package/services/SilkPublishability.js +193 -0
- package/services/SilkWorkspaceAnalyzer.js +213 -0
- package/services/TagStrategy.js +54 -0
- package/services/ToolDiscovery.js +229 -0
- package/services/VersioningStrategy.js +67 -0
- package/tsdoc-metadata.json +11 -11
- package/turbo/digest.js +127 -0
- package/turbo/errors.js +48 -0
- package/turbo/index.js +32 -0
- package/turbo/schemas/DryRun.js +57 -0
- package/turbo/schemas/results.js +61 -0
- package/turbo/services/TurboInspector.js +100 -0
- package/utils/ToolCommand.js +40 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/commitlint/hook/rules/plan-leakage.ts
|
|
4
|
+
/**
|
|
5
|
+
* plan-leakage rule — advises when commit bodies reference plan files
|
|
6
|
+
* or contain planning-narrative language.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
const PATH_PATTERNS = [/\.claude\/plans\//i, /\.claude\/design\//i];
|
|
11
|
+
const PHRASE_PATTERNS = [
|
|
12
|
+
/\bas decided in the plan\b/i,
|
|
13
|
+
/\bpreviously documented\b/i,
|
|
14
|
+
/\bsee the design doc\b/i,
|
|
15
|
+
/\bper the (plan|design doc|spec)\b/i
|
|
16
|
+
];
|
|
17
|
+
const planLeakageRule = {
|
|
18
|
+
id: "plan-leakage",
|
|
19
|
+
severity: "advise",
|
|
20
|
+
check: (input) => Effect.sync(() => {
|
|
21
|
+
const matchedPaths = PATH_PATTERNS.filter((re) => re.test(input.message));
|
|
22
|
+
const matchedPhrases = PHRASE_PATTERNS.filter((re) => re.test(input.message));
|
|
23
|
+
if (matchedPaths.length === 0 && matchedPhrases.length === 0) return null;
|
|
24
|
+
const reasons = [];
|
|
25
|
+
if (matchedPaths.length > 0) reasons.push("references planning artifacts (.claude/plans/ or .claude/design/)");
|
|
26
|
+
if (matchedPhrases.length > 0) reasons.push("contains planning-narrative phrasing");
|
|
27
|
+
return {
|
|
28
|
+
ruleId: "plan-leakage",
|
|
29
|
+
severity: "advise",
|
|
30
|
+
message: `Body ${reasons.join(" and ")}. Plan-file paths and "as decided in the plan"-style narrative belong in the PR description, not the commit body. Remove those references and re-run git commit.`
|
|
31
|
+
};
|
|
32
|
+
})
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { planLeakageRule };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/commitlint/hook/rules/signing-flag-conflict.ts
|
|
4
|
+
/**
|
|
5
|
+
* signing-flag-conflict rule — denies when the command explicitly opts out
|
|
6
|
+
* of signing while the project is configured to require signing.
|
|
7
|
+
*
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
const signingFlagConflictRule = {
|
|
11
|
+
id: "signing-flag-conflict",
|
|
12
|
+
severity: "deny",
|
|
13
|
+
check: (input, ctx) => Effect.sync(() => {
|
|
14
|
+
if (input.flags.sign !== "force-off") return null;
|
|
15
|
+
if (!ctx.autoSignEnabled) return null;
|
|
16
|
+
return {
|
|
17
|
+
ruleId: "signing-flag-conflict",
|
|
18
|
+
severity: "deny",
|
|
19
|
+
message: "Command uses --no-gpg-sign while commit.gpgsign=true is configured. Either remove --no-gpg-sign, or update git config commit.gpgsign first if you intend to leave signing off."
|
|
20
|
+
};
|
|
21
|
+
})
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
//#endregion
|
|
25
|
+
export { signingFlagConflictRule };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/commitlint/hook/rules/soft-wrap.ts
|
|
4
|
+
/**
|
|
5
|
+
* soft-wrap rule — advises when a bullet point appears to have an
|
|
6
|
+
* artificial mid-bullet line wrap.
|
|
7
|
+
*
|
|
8
|
+
* Heuristic: a bullet line shorter than 80 chars, immediately followed
|
|
9
|
+
* by an indented continuation line (not a new bullet), suggests the
|
|
10
|
+
* agent wrapped at ~80 chars unnecessarily.
|
|
11
|
+
*
|
|
12
|
+
* @internal
|
|
13
|
+
*/
|
|
14
|
+
const BULLET = /^([ \t]*)[-*]\s/;
|
|
15
|
+
const CONTINUATION = /^[ \t]+\S/;
|
|
16
|
+
const softWrapRule = {
|
|
17
|
+
id: "soft-wrap",
|
|
18
|
+
severity: "advise",
|
|
19
|
+
check: (input) => Effect.sync(() => {
|
|
20
|
+
const lines = input.message.split("\n");
|
|
21
|
+
for (let i = 0; i < lines.length - 1; i++) {
|
|
22
|
+
const line = lines[i] ?? "";
|
|
23
|
+
const next = lines[i + 1] ?? "";
|
|
24
|
+
if (!line.match(BULLET)) continue;
|
|
25
|
+
if (line.length >= 80) continue;
|
|
26
|
+
if (CONTINUATION.test(next) && !BULLET.test(next)) return {
|
|
27
|
+
ruleId: "soft-wrap",
|
|
28
|
+
severity: "advise",
|
|
29
|
+
message: "A bullet point appears to have an artificial soft-wrap (a short bullet followed by an indented continuation). The body line limit is 300 chars; one bullet should fit on one line. Reflow the bullet onto a single line."
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
})
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
//#endregion
|
|
37
|
+
export { softWrapRule };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/commitlint/hook/rules/types.ts
|
|
2
|
+
function partitionHits(hits) {
|
|
3
|
+
const deny = [];
|
|
4
|
+
const advise = [];
|
|
5
|
+
for (const hit of hits) if (hit.severity === "deny") deny.push(hit);
|
|
6
|
+
else advise.push(hit);
|
|
7
|
+
return {
|
|
8
|
+
deny,
|
|
9
|
+
advise
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
//#endregion
|
|
14
|
+
export { partitionHits };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/commitlint/hook/rules/verbosity.ts
|
|
4
|
+
/**
|
|
5
|
+
* verbosity rule — advises when the body exceeds line or word thresholds.
|
|
6
|
+
*
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
const VERBOSITY_LINE_THRESHOLD = 25;
|
|
10
|
+
const VERBOSITY_WORD_THRESHOLD = 400;
|
|
11
|
+
const verbosityRule = {
|
|
12
|
+
id: "verbosity",
|
|
13
|
+
severity: "advise",
|
|
14
|
+
check: (input) => Effect.sync(() => {
|
|
15
|
+
const body = input.message.split("\n").slice(2).filter((l) => l.length > 0);
|
|
16
|
+
const lineCount = body.length;
|
|
17
|
+
const wordCount = body.join(" ").trim().split(/\s+/).filter(Boolean).length;
|
|
18
|
+
if (lineCount <= 25 && wordCount <= 400) return null;
|
|
19
|
+
const reasons = [];
|
|
20
|
+
if (lineCount > 25) reasons.push(`${lineCount} non-empty body lines`);
|
|
21
|
+
if (wordCount > 400) reasons.push(`${wordCount} words`);
|
|
22
|
+
return {
|
|
23
|
+
ruleId: "verbosity",
|
|
24
|
+
severity: "advise",
|
|
25
|
+
message: `Body has ${reasons.join(" / ")}; the project standard keeps commit bodies under ~${25} lines and ~${400} words. Move detail to the PR description and keep the commit body to the irreducible "what + why".`
|
|
26
|
+
};
|
|
27
|
+
})
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
//#endregion
|
|
31
|
+
export { verbosityRule };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { Layer, LogLevel, Logger } from "effect";
|
|
2
|
+
|
|
3
|
+
//#region src/commitlint/hook/silence-logger.ts
|
|
4
|
+
/**
|
|
5
|
+
* Logger configuration shared by all hook subcommands.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Hook subcommands reserve stdout for the JSON envelope they emit to Claude
|
|
9
|
+
* Code. Effect's default logger writes messages to stdout via `console.log`,
|
|
10
|
+
* which pollutes that contract (e.g., workspaces-effect emits "Workspace root
|
|
11
|
+
* found" on every CLI invocation). This Layer combines two protections:
|
|
12
|
+
*
|
|
13
|
+
* 1. {@link Logger.minimumLogLevel} at `Warning` silences routine Info/Debug
|
|
14
|
+
* traffic so the common case never reaches the logger at all.
|
|
15
|
+
* 2. A default-logger replacement that routes anything which *does* fire to
|
|
16
|
+
* stderr, so even Warning/Error output cannot corrupt the stdout envelope.
|
|
17
|
+
*
|
|
18
|
+
* Only the hook command wrappers provide this Layer, so regular `savvy`
|
|
19
|
+
* commands keep their stdout output intact.
|
|
20
|
+
*
|
|
21
|
+
* @internal
|
|
22
|
+
*/
|
|
23
|
+
/**
|
|
24
|
+
* Replacement for the default logger that writes every message to stderr,
|
|
25
|
+
* leaving stdout reserved for the hook's JSON envelope.
|
|
26
|
+
*/
|
|
27
|
+
const StderrLogger = Logger.replace(Logger.defaultLogger, Logger.make(({ message }) => {
|
|
28
|
+
process.stderr.write(`${JSON.stringify(message)}\n`);
|
|
29
|
+
}));
|
|
30
|
+
/** Minimum log level layer that suppresses Info and below. */
|
|
31
|
+
const MinLogLevel = Logger.minimumLogLevel(LogLevel.Warning);
|
|
32
|
+
/**
|
|
33
|
+
* Combined hook logger layer: stderr redirect merged with the Warning minimum
|
|
34
|
+
* log level. Provided by hook command wrappers to keep stdout corruption-proof.
|
|
35
|
+
*/
|
|
36
|
+
const HookSilencer = Layer.merge(StderrLogger, MinLogLevel);
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { HookSilencer };
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { detectDCO } from "./detection/dco.js";
|
|
3
|
+
import { COMMIT_TYPES, COMMIT_TYPE_DEFINITIONS, DCO_SIGNOFF_TEXT, TDD_SCOPE_PATTERN, TDD_STATES } from "./config/rules.js";
|
|
4
|
+
import { TYPE_EMOJIS, TYPE_EMOJIS_UNICODE, getTypeEmoji } from "./prompt/emojis.js";
|
|
5
|
+
import { createPromptConfig, createTypeEnum, defaultPromptConfig, emojiPromptConfig } from "./prompt/config.js";
|
|
6
|
+
import { createConfig } from "./config/factory.js";
|
|
7
|
+
import { decodeConfigOptions } from "./config/schema.js";
|
|
8
|
+
import staticConfig from "./static.js";
|
|
9
|
+
import { ERROR_EXPLANATIONS, ERROR_SUGGESTIONS, getExplanation, getSuggestion } from "./formatter/messages.js";
|
|
10
|
+
import { format } from "./formatter/format.js";
|
|
11
|
+
import { prompter } from "./prompt/prompter.js";
|
|
12
|
+
import { detectScopes } from "./detection/scopes.js";
|
|
13
|
+
import { PostToolUseEnvelope, PreToolUseEnvelope, SessionStartEnvelope, UserPromptSubmitEnvelope } from "./hook/envelope.js";
|
|
14
|
+
import { postToolUseAdvise, preToolUseAdvise, preToolUseAllow, preToolUseDeny, preToolUseSilent, sessionStartContext, userPromptSubmitContext } from "./hook/output.js";
|
|
15
|
+
import { parseBashCommand } from "./hook/parse-bash-command.js";
|
|
16
|
+
import { HookSilencer } from "./hook/silence-logger.js";
|
|
17
|
+
import { inferTicketId, readBranchInfo } from "./hook/diagnostics/branch.js";
|
|
18
|
+
import { readCache, writeCache } from "./hook/diagnostics/cache.js";
|
|
19
|
+
import { parseHuskyConfigPath, readCommitlintConfigPath } from "./hook/diagnostics/commitlint-config.js";
|
|
20
|
+
import { ISSUES_CACHE_RELATIVE_PATH, fetchAndCacheOpenIssues, readOpenIssuesFromCache, readOrFetchOpenIssues } from "./hook/diagnostics/open-issues.js";
|
|
21
|
+
import { detectFromLockfiles, detectPackageManager, parsePackageManagerField } from "./hook/diagnostics/package-manager.js";
|
|
22
|
+
import { buildSigningDiagnostic, parseGpgKeyExpiry, readSigningDiagnostic } from "./hook/diagnostics/signing.js";
|
|
23
|
+
import { closesTrailerRule, hasClosingTrailer } from "./hook/rules/closes-trailer.js";
|
|
24
|
+
import { forbiddenContentRule } from "./hook/rules/forbidden-content.js";
|
|
25
|
+
import { planLeakageRule } from "./hook/rules/plan-leakage.js";
|
|
26
|
+
import { signingFlagConflictRule } from "./hook/rules/signing-flag-conflict.js";
|
|
27
|
+
import { softWrapRule } from "./hook/rules/soft-wrap.js";
|
|
28
|
+
import { partitionHits } from "./hook/rules/types.js";
|
|
29
|
+
import { verbosityRule } from "./hook/rules/verbosity.js";
|
|
30
|
+
|
|
31
|
+
//#region src/commitlint/index.ts
|
|
32
|
+
/**
|
|
33
|
+
* Commitlint namespace — commitlint business logic extracted from \@savvy-web/commitlint.
|
|
34
|
+
*
|
|
35
|
+
* @remarks
|
|
36
|
+
* This barrel re-exports the complete commitlint public surface (minus CLI/bin) from
|
|
37
|
+
* the silk-effects package. Downstream consumers import via the Commitlint namespace:
|
|
38
|
+
*
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { Commitlint } from "@savvy-web/silk-effects";
|
|
41
|
+
* const config = Commitlint.CommitlintConfig.silk();
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* @packageDocumentation
|
|
45
|
+
*/
|
|
46
|
+
var commitlint_exports = /* @__PURE__ */ __exportAll({
|
|
47
|
+
COMMIT_TYPES: () => COMMIT_TYPES,
|
|
48
|
+
COMMIT_TYPE_DEFINITIONS: () => COMMIT_TYPE_DEFINITIONS,
|
|
49
|
+
CommitlintConfig: () => CommitlintConfig,
|
|
50
|
+
DCO_SIGNOFF_TEXT: () => DCO_SIGNOFF_TEXT,
|
|
51
|
+
DEFAULT_BODY_MAX_LINE_LENGTH: () => 300,
|
|
52
|
+
ERROR_EXPLANATIONS: () => ERROR_EXPLANATIONS,
|
|
53
|
+
ERROR_SUGGESTIONS: () => ERROR_SUGGESTIONS,
|
|
54
|
+
HookSilencer: () => HookSilencer,
|
|
55
|
+
ISSUES_CACHE_RELATIVE_PATH: () => ISSUES_CACHE_RELATIVE_PATH,
|
|
56
|
+
ISSUES_CACHE_TTL_SECONDS: () => 600,
|
|
57
|
+
PostToolUseEnvelope: () => PostToolUseEnvelope,
|
|
58
|
+
PreToolUseEnvelope: () => PreToolUseEnvelope,
|
|
59
|
+
SessionStartEnvelope: () => SessionStartEnvelope,
|
|
60
|
+
TDD_SCOPE_PATTERN: () => TDD_SCOPE_PATTERN,
|
|
61
|
+
TDD_STATES: () => TDD_STATES,
|
|
62
|
+
TYPE_EMOJIS: () => TYPE_EMOJIS,
|
|
63
|
+
TYPE_EMOJIS_UNICODE: () => TYPE_EMOJIS_UNICODE,
|
|
64
|
+
UserPromptSubmitEnvelope: () => UserPromptSubmitEnvelope,
|
|
65
|
+
VERBOSITY_LINE_THRESHOLD: () => 25,
|
|
66
|
+
VERBOSITY_WORD_THRESHOLD: () => 400,
|
|
67
|
+
buildSigningDiagnostic: () => buildSigningDiagnostic,
|
|
68
|
+
closesTrailerRule: () => closesTrailerRule,
|
|
69
|
+
createPromptConfig: () => createPromptConfig,
|
|
70
|
+
createTypeEnum: () => createTypeEnum,
|
|
71
|
+
default: () => CommitlintConfig,
|
|
72
|
+
defaultPromptConfig: () => defaultPromptConfig,
|
|
73
|
+
detectDCO: () => detectDCO,
|
|
74
|
+
detectFromLockfiles: () => detectFromLockfiles,
|
|
75
|
+
detectPackageManager: () => detectPackageManager,
|
|
76
|
+
detectScopes: () => detectScopes,
|
|
77
|
+
emojiPromptConfig: () => emojiPromptConfig,
|
|
78
|
+
fetchAndCacheOpenIssues: () => fetchAndCacheOpenIssues,
|
|
79
|
+
forbiddenContentRule: () => forbiddenContentRule,
|
|
80
|
+
format: () => format,
|
|
81
|
+
getExplanation: () => getExplanation,
|
|
82
|
+
getSuggestion: () => getSuggestion,
|
|
83
|
+
getTypeEmoji: () => getTypeEmoji,
|
|
84
|
+
hasClosingTrailer: () => hasClosingTrailer,
|
|
85
|
+
inferTicketId: () => inferTicketId,
|
|
86
|
+
parseBashCommand: () => parseBashCommand,
|
|
87
|
+
parseGpgKeyExpiry: () => parseGpgKeyExpiry,
|
|
88
|
+
parseHuskyConfigPath: () => parseHuskyConfigPath,
|
|
89
|
+
parsePackageManagerField: () => parsePackageManagerField,
|
|
90
|
+
partitionHits: () => partitionHits,
|
|
91
|
+
planLeakageRule: () => planLeakageRule,
|
|
92
|
+
postToolUseAdvise: () => postToolUseAdvise,
|
|
93
|
+
preToolUseAdvise: () => preToolUseAdvise,
|
|
94
|
+
preToolUseAllow: () => preToolUseAllow,
|
|
95
|
+
preToolUseDeny: () => preToolUseDeny,
|
|
96
|
+
preToolUseSilent: () => preToolUseSilent,
|
|
97
|
+
prompter: () => prompter,
|
|
98
|
+
readBranchInfo: () => readBranchInfo,
|
|
99
|
+
readCache: () => readCache,
|
|
100
|
+
readCommitlintConfigPath: () => readCommitlintConfigPath,
|
|
101
|
+
readOpenIssuesFromCache: () => readOpenIssuesFromCache,
|
|
102
|
+
readOrFetchOpenIssues: () => readOrFetchOpenIssues,
|
|
103
|
+
readSigningDiagnostic: () => readSigningDiagnostic,
|
|
104
|
+
sessionStartContext: () => sessionStartContext,
|
|
105
|
+
signingFlagConflictRule: () => signingFlagConflictRule,
|
|
106
|
+
softWrapRule: () => softWrapRule,
|
|
107
|
+
staticConfig: () => staticConfig,
|
|
108
|
+
userPromptSubmitContext: () => userPromptSubmitContext,
|
|
109
|
+
verbosityRule: () => verbosityRule,
|
|
110
|
+
writeCache: () => writeCache
|
|
111
|
+
});
|
|
112
|
+
/**
|
|
113
|
+
* Dynamic commitlint configuration factory.
|
|
114
|
+
*
|
|
115
|
+
* @remarks
|
|
116
|
+
* Provides static methods for creating commitlint configurations with automatic
|
|
117
|
+
* detection of repository characteristics. The class co-locates configuration
|
|
118
|
+
* creation with schema validation.
|
|
119
|
+
*
|
|
120
|
+
* @public
|
|
121
|
+
*
|
|
122
|
+
* @example
|
|
123
|
+
* ```typescript
|
|
124
|
+
* import { Commitlint } from "@savvy-web/silk-effects";
|
|
125
|
+
*
|
|
126
|
+
* // Auto-detect everything
|
|
127
|
+
* export default Commitlint.CommitlintConfig.silk();
|
|
128
|
+
*
|
|
129
|
+
* // With explicit overrides
|
|
130
|
+
* export default Commitlint.CommitlintConfig.silk({
|
|
131
|
+
* dco: true,
|
|
132
|
+
* scopes: ["api", "cli"],
|
|
133
|
+
* emojis: true,
|
|
134
|
+
* });
|
|
135
|
+
* ```
|
|
136
|
+
*/
|
|
137
|
+
var CommitlintConfig = class {
|
|
138
|
+
static silk(options = {}) {
|
|
139
|
+
return createConfig(decodeConfigOptions(options));
|
|
140
|
+
}
|
|
141
|
+
/* v8 ignore next 3 -- private constructor prevents direct instantiation */
|
|
142
|
+
constructor() {}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
//#endregion
|
|
146
|
+
export { commitlint_exports };
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { COMMIT_TYPE_DEFINITIONS } from "../config/rules.js";
|
|
2
|
+
import { TYPE_EMOJIS } from "./emojis.js";
|
|
3
|
+
|
|
4
|
+
//#region src/commitlint/prompt/config.ts
|
|
5
|
+
/**
|
|
6
|
+
* Create type enum configuration for prompts.
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Builds the type enum object used by commitizen adapters
|
|
10
|
+
* to display commit type options in the interactive prompt.
|
|
11
|
+
*
|
|
12
|
+
* @param emojis - Whether to include emoji shortcodes
|
|
13
|
+
* @returns Type enum configuration object
|
|
14
|
+
*
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
function createTypeEnum(emojis) {
|
|
18
|
+
const typeEnum = {};
|
|
19
|
+
for (const def of COMMIT_TYPE_DEFINITIONS) typeEnum[def.type] = {
|
|
20
|
+
description: def.description,
|
|
21
|
+
title: def.title,
|
|
22
|
+
emoji: emojis ? TYPE_EMOJIS[def.type] : ""
|
|
23
|
+
};
|
|
24
|
+
return typeEnum;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Create prompt configuration for commitizen adapters.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* Generates a complete prompt configuration object compatible with
|
|
31
|
+
* `@commitlint/cz-commitlint` and included in `CommitlintConfig.silk()` output.
|
|
32
|
+
*
|
|
33
|
+
* @param options - Prompt configuration options
|
|
34
|
+
* @returns Prompt configuration object
|
|
35
|
+
*
|
|
36
|
+
* @public
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```typescript
|
|
40
|
+
* import { createPromptConfig } from "@savvy-web/commitlint/prompt";
|
|
41
|
+
*
|
|
42
|
+
* const promptConfig = createPromptConfig({ emojis: true });
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
function createPromptConfig(options = {}) {
|
|
46
|
+
const { emojis = false, scopes } = options;
|
|
47
|
+
const scopeQuestion = { description: "What is the scope of this change (e.g., component name):" };
|
|
48
|
+
if (scopes && scopes.length > 0) scopeQuestion.enum = scopes;
|
|
49
|
+
return {
|
|
50
|
+
settings: {
|
|
51
|
+
enableMultipleScopes: true,
|
|
52
|
+
scopeEnumSeparator: ","
|
|
53
|
+
},
|
|
54
|
+
messages: {
|
|
55
|
+
skip: "(press enter to skip)",
|
|
56
|
+
max: "(max %d chars)",
|
|
57
|
+
min: "(min %d chars)",
|
|
58
|
+
emptyWarning: "cannot be empty",
|
|
59
|
+
upperLimitWarning: "over the limit",
|
|
60
|
+
lowerLimitWarning: "below the limit"
|
|
61
|
+
},
|
|
62
|
+
questions: {
|
|
63
|
+
type: {
|
|
64
|
+
description: "Select the type of change you're committing:",
|
|
65
|
+
enum: createTypeEnum(emojis)
|
|
66
|
+
},
|
|
67
|
+
scope: scopeQuestion,
|
|
68
|
+
subject: { description: "Write a short, imperative description of the change:" },
|
|
69
|
+
body: { description: "Provide a longer description of the change (optional):" },
|
|
70
|
+
isBreaking: { description: "Are there any breaking changes?" },
|
|
71
|
+
breakingBody: { description: "Describe the breaking changes:" },
|
|
72
|
+
isIssueAffected: { description: "Does this change affect any open issues?" },
|
|
73
|
+
issuesBody: { description: "Add issue references (e.g., 'fix #123', 'close #456'):" }
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Default prompt configuration (no emojis).
|
|
79
|
+
*
|
|
80
|
+
* @public
|
|
81
|
+
*/
|
|
82
|
+
const defaultPromptConfig = createPromptConfig();
|
|
83
|
+
/**
|
|
84
|
+
* Prompt configuration with emojis enabled.
|
|
85
|
+
*
|
|
86
|
+
* @public
|
|
87
|
+
*/
|
|
88
|
+
const emojiPromptConfig = createPromptConfig({ emojis: true });
|
|
89
|
+
|
|
90
|
+
//#endregion
|
|
91
|
+
export { createPromptConfig, createTypeEnum, defaultPromptConfig, emojiPromptConfig };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/commitlint/prompt/emojis.ts
|
|
2
|
+
/**
|
|
3
|
+
* Emoji shortcode mapping for commit types.
|
|
4
|
+
*
|
|
5
|
+
* @remarks
|
|
6
|
+
* Uses GitHub/GitLab compatible shortcodes (e.g., `:sparkles:`) that
|
|
7
|
+
* render as emojis in Markdown contexts and web UIs.
|
|
8
|
+
*
|
|
9
|
+
* @public
|
|
10
|
+
*/
|
|
11
|
+
const TYPE_EMOJIS = {
|
|
12
|
+
ai: ":robot:",
|
|
13
|
+
feat: ":sparkles:",
|
|
14
|
+
fix: ":bug:",
|
|
15
|
+
docs: ":memo:",
|
|
16
|
+
style: ":lipstick:",
|
|
17
|
+
refactor: ":recycle:",
|
|
18
|
+
perf: ":zap:",
|
|
19
|
+
test: ":white_check_mark:",
|
|
20
|
+
tdd: ":test_tube:",
|
|
21
|
+
build: ":package:",
|
|
22
|
+
ci: ":construction_worker:",
|
|
23
|
+
chore: ":wrench:",
|
|
24
|
+
revert: ":rewind:",
|
|
25
|
+
release: ":bookmark:"
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Unicode emoji mapping for commit types.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Uses actual Unicode emojis for terminals and contexts that
|
|
32
|
+
* support direct emoji rendering.
|
|
33
|
+
*
|
|
34
|
+
* @public
|
|
35
|
+
*/
|
|
36
|
+
const TYPE_EMOJIS_UNICODE = {
|
|
37
|
+
ai: "🤖",
|
|
38
|
+
feat: "✨",
|
|
39
|
+
fix: "🐛",
|
|
40
|
+
docs: "📝",
|
|
41
|
+
style: "💄",
|
|
42
|
+
refactor: "♻️",
|
|
43
|
+
perf: "⚡",
|
|
44
|
+
test: "✅",
|
|
45
|
+
tdd: "",
|
|
46
|
+
build: "📦",
|
|
47
|
+
ci: "👷",
|
|
48
|
+
chore: "🔧",
|
|
49
|
+
revert: "⏪",
|
|
50
|
+
release: "🔖"
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* Get emoji for a commit type.
|
|
54
|
+
*
|
|
55
|
+
* @param type - Commit type
|
|
56
|
+
* @param unicode - Use Unicode emojis instead of shortcodes
|
|
57
|
+
* @returns Emoji string (shortcode or Unicode)
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import { getTypeEmoji } from "@savvy-web/commitlint/prompt";
|
|
64
|
+
*
|
|
65
|
+
* getTypeEmoji("feat"); // ":sparkles:"
|
|
66
|
+
* getTypeEmoji("feat", true); // "✨"
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
function getTypeEmoji(type, unicode = false) {
|
|
70
|
+
return unicode ? TYPE_EMOJIS_UNICODE[type] : TYPE_EMOJIS[type];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { TYPE_EMOJIS, TYPE_EMOJIS_UNICODE, getTypeEmoji };
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { COMMIT_TYPE_DEFINITIONS } from "../config/rules.js";
|
|
2
|
+
import { TYPE_EMOJIS_UNICODE } from "./emojis.js";
|
|
3
|
+
|
|
4
|
+
//#region src/commitlint/prompt/prompter.ts
|
|
5
|
+
/** Default prompter options */
|
|
6
|
+
const DEFAULT_OPTIONS = {
|
|
7
|
+
emojis: true,
|
|
8
|
+
scopes: [],
|
|
9
|
+
maxSubjectLength: 100,
|
|
10
|
+
maxBodyLength: 300
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Build type choices for the prompt.
|
|
14
|
+
*/
|
|
15
|
+
function buildTypeChoices(emojis) {
|
|
16
|
+
return COMMIT_TYPE_DEFINITIONS.map((def) => {
|
|
17
|
+
const emoji = emojis ? TYPE_EMOJIS_UNICODE[def.type] : "";
|
|
18
|
+
return {
|
|
19
|
+
name: `${emoji ? `${emoji} ` : ""}${def.type}: ${def.description}`,
|
|
20
|
+
value: def.type
|
|
21
|
+
};
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build scope choices for the prompt.
|
|
26
|
+
*/
|
|
27
|
+
function buildScopeChoices(scopes) {
|
|
28
|
+
return [{
|
|
29
|
+
name: "(none)",
|
|
30
|
+
value: ""
|
|
31
|
+
}, ...scopes.map((scope) => ({
|
|
32
|
+
name: scope,
|
|
33
|
+
value: scope
|
|
34
|
+
}))];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Format the final commit message.
|
|
38
|
+
*/
|
|
39
|
+
function formatCommitMessage(answers) {
|
|
40
|
+
const { type, scope, subject, body, isBreaking, breakingBody, isIssueAffected, issues } = answers;
|
|
41
|
+
const parts = [`${type}${scope ? `(${scope})` : ""}${isBreaking ? "!" : ""}: ${subject}`];
|
|
42
|
+
if (body) parts.push("", body);
|
|
43
|
+
if (isBreaking && breakingBody) parts.push("", `BREAKING CHANGE: ${breakingBody}`);
|
|
44
|
+
if (isIssueAffected && issues) parts.push("", issues);
|
|
45
|
+
return parts.join("\n");
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Commitizen prompter function.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* This is the main entry point for the commitizen adapter.
|
|
52
|
+
* It prompts the user for commit information and calls the
|
|
53
|
+
* commit callback with the formatted message.
|
|
54
|
+
*
|
|
55
|
+
* @param cz - Inquirer instance provided by commitizen
|
|
56
|
+
* @param commit - Callback to execute the commit
|
|
57
|
+
* @param options - Prompter options
|
|
58
|
+
*
|
|
59
|
+
* @public
|
|
60
|
+
*/
|
|
61
|
+
function prompter(cz, commit, options = {}) {
|
|
62
|
+
const opts = {
|
|
63
|
+
...DEFAULT_OPTIONS,
|
|
64
|
+
...options
|
|
65
|
+
};
|
|
66
|
+
const scopeQuestion = opts.scopes.length > 0 ? {
|
|
67
|
+
type: "list",
|
|
68
|
+
name: "scope",
|
|
69
|
+
message: "Select the scope of this change:",
|
|
70
|
+
choices: buildScopeChoices(opts.scopes)
|
|
71
|
+
} : {
|
|
72
|
+
type: "input",
|
|
73
|
+
name: "scope",
|
|
74
|
+
message: "Scope of this change (press enter to skip):",
|
|
75
|
+
filter: (input) => input.trim().toLowerCase()
|
|
76
|
+
};
|
|
77
|
+
const questions = [
|
|
78
|
+
{
|
|
79
|
+
type: "list",
|
|
80
|
+
name: "type",
|
|
81
|
+
message: "Select the type of change you're committing:",
|
|
82
|
+
choices: buildTypeChoices(opts.emojis)
|
|
83
|
+
},
|
|
84
|
+
scopeQuestion,
|
|
85
|
+
{
|
|
86
|
+
type: "input",
|
|
87
|
+
name: "subject",
|
|
88
|
+
message: `Short description (max ${opts.maxSubjectLength} chars):`,
|
|
89
|
+
validate: (input) => {
|
|
90
|
+
if (!input.trim()) return "Subject is required";
|
|
91
|
+
if (input.length > opts.maxSubjectLength) return `Subject must be ${opts.maxSubjectLength} characters or less (currently ${input.length})`;
|
|
92
|
+
return true;
|
|
93
|
+
},
|
|
94
|
+
filter: (input) => {
|
|
95
|
+
const trimmed = input.trim();
|
|
96
|
+
return trimmed.charAt(0).toLowerCase() + trimmed.slice(1);
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
type: "input",
|
|
101
|
+
name: "body",
|
|
102
|
+
message: "Longer description (press enter to skip):"
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: "confirm",
|
|
106
|
+
name: "isBreaking",
|
|
107
|
+
message: "Are there any breaking changes?",
|
|
108
|
+
default: false
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
type: "input",
|
|
112
|
+
name: "breakingBody",
|
|
113
|
+
message: "Describe the breaking changes:",
|
|
114
|
+
when: (answers) => answers.isBreaking === true
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
type: "confirm",
|
|
118
|
+
name: "isIssueAffected",
|
|
119
|
+
message: "Does this change affect any open issues?",
|
|
120
|
+
default: false
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: "input",
|
|
124
|
+
name: "issues",
|
|
125
|
+
message: "Issue references (e.g., 'fix #123', 'closes #456'):",
|
|
126
|
+
when: (answers) => answers.isIssueAffected === true
|
|
127
|
+
}
|
|
128
|
+
];
|
|
129
|
+
cz.prompt(questions).then((answers) => {
|
|
130
|
+
commit(formatCommitMessage(answers));
|
|
131
|
+
}).catch(() => {});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
//#endregion
|
|
135
|
+
export { prompter };
|