@workit-dev/core 0.0.1 → 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/LICENSE +21 -0
- package/README.md +22 -43
- package/package.json +56 -6
- package/schemas/config.json +74 -0
- package/schemas/vcs.json +79 -0
- package/schemas/workspaces.json +1029 -0
- package/schemas/youtrack.json +84 -0
- package/scripts/analyze-release-scope.ts +215 -0
- package/scripts/bundled-deps.json +131 -0
- package/scripts/fixtures/sample-plan.md +15 -0
- package/scripts/fixtures/sample-sdd/progress.md +2 -0
- package/scripts/install-codex-plugin.sh +28 -0
- package/scripts/install-cursor-plugin.sh +146 -0
- package/scripts/install-opencode-plugin.sh +79 -0
- package/scripts/install-pi-package.sh +56 -0
- package/scripts/lib/config-dir.sh +28 -0
- package/scripts/publish-changed-packages.ts +395 -0
- package/scripts/rewrite-workspace-deps.ts +53 -0
- package/scripts/run-cursor-mcp.sh +6 -0
- package/scripts/sync-release-manifests.ts +82 -0
- package/scripts/sync-runtime.sh +142 -0
- package/scripts/validate-cursor-marketplace.ts +171 -0
- package/scripts/verify-bundle-sources.ts +134 -0
- package/scripts/write-schemas.ts +16 -0
- package/skills/workit-architecture/SKILL.md +72 -0
- package/skills/workit-architecture/agents/openai.yaml +3 -0
- package/skills/workit-architecture/references/instruction-files.md +52 -0
- package/skills/workit-architecture/references/vocabulary.md +53 -0
- package/skills/workit-bdd/SKILL.md +53 -0
- package/skills/workit-continue/SKILL.md +54 -0
- package/skills/workit-debug/SKILL.md +45 -0
- package/skills/workit-deslop/SKILL.md +58 -0
- package/skills/workit-fanout/SKILL.md +71 -0
- package/skills/workit-fanout/references/brief.md +141 -0
- package/skills/workit-fanout/references/orchestration.md +169 -0
- package/skills/workit-implement/SKILL.md +71 -0
- package/skills/workit-retro/SKILL.md +72 -0
- package/skills/workit-retro/agents/openai.yaml +3 -0
- package/skills/workit-retro/references/sources.md +35 -0
- package/skills/workit-retro/references/steering.md +39 -0
- package/skills/workit-review/SKILL.md +66 -0
- package/skills/workit-review/references/impact.md +24 -0
- package/skills/workit-shape/SKILL.md +71 -0
- package/skills/workit-shape/references/diagrams.md +17 -0
- package/skills/workit-shape/references/knowledge.md +61 -0
- package/skills/workit-shape/references/mockups.md +15 -0
- package/skills/workit-shape/references/slicing.md +44 -0
- package/skills/workit-ship/SKILL.md +71 -0
- package/skills/workit-ship/references/changelog.md +44 -0
- package/skills/workit-ship/references/pr-body.md +32 -0
- package/skills/workit-ship/references/release.md +39 -0
- package/skills/workit-test-audit/SKILL.md +54 -0
- package/skills/workit-verify-app/SKILL.md +68 -0
- package/skills/workit-verify-app/references/template.md +51 -0
- package/src/autonomy.ts +910 -0
- package/src/check-config.ts +613 -0
- package/src/checks.ts +534 -0
- package/src/ci-waiter.ts +159 -0
- package/src/core/boundary.ts +55 -0
- package/src/core/branch-policy.ts +112 -0
- package/src/core/branch.ts +1192 -0
- package/src/core/changelog-gate.ts +349 -0
- package/src/core/changelog-merge.ts +214 -0
- package/src/core/changelog.ts +392 -0
- package/src/core/cleanup.ts +638 -0
- package/src/core/cli-locate.ts +173 -0
- package/src/core/commit-flavors.ts +65 -0
- package/src/core/config-schemas.ts +653 -0
- package/src/core/config.ts +351 -0
- package/src/core/context-read.ts +144 -0
- package/src/core/docs-gate.ts +69 -0
- package/src/core/finding-evidence.ts +89 -0
- package/src/core/finding-queue.ts +36 -0
- package/src/core/git.ts +73 -0
- package/src/core/gitignore.ts +52 -0
- package/src/core/heavy-eta.ts +170 -0
- package/src/core/heavy-lease.ts +290 -0
- package/src/core/heavy-rules.ts +373 -0
- package/src/core/heavy-scope.ts +131 -0
- package/src/core/heavy-words.ts +83 -0
- package/src/core/heavy.ts +535 -0
- package/src/core/hygiene.ts +149 -0
- package/src/core/init.ts +218 -0
- package/src/core/live-session.ts +333 -0
- package/src/core/logger.ts +310 -0
- package/src/core/methods.ts +185 -0
- package/src/core/model-fallback.ts +208 -0
- package/src/core/node-resolve.mjs +249 -0
- package/src/core/operation-input.ts +632 -0
- package/src/core/package-root.ts +28 -0
- package/src/core/policy/attributes.ts +97 -0
- package/src/core/policy/bookkeeping.ts +119 -0
- package/src/core/policy/carry.ts +122 -0
- package/src/core/policy/check-evidence.ts +115 -0
- package/src/core/policy/current-judgment.ts +20 -0
- package/src/core/policy/derive.ts +387 -0
- package/src/core/policy/judgment.ts +246 -0
- package/src/core/policy/lane.ts +562 -0
- package/src/core/policy/pr-lane.ts +32 -0
- package/src/core/policy/substance.ts +365 -0
- package/src/core/policy-resolver.ts +94 -0
- package/src/core/pr-create.ts +448 -0
- package/src/core/pr-template.ts +162 -0
- package/src/core/release-notes.ts +197 -0
- package/src/core/release-tracks.ts +779 -0
- package/src/core/remotes.ts +436 -0
- package/src/core/repo-context.ts +542 -0
- package/src/core/repo-paths.ts +29 -0
- package/src/core/resources.ts +1299 -0
- package/src/core/role-models.ts +603 -0
- package/src/core/route-intent.ts +305 -0
- package/src/core/rules.ts +65 -0
- package/src/core/runtime-identity.ts +63 -0
- package/src/core/safe-write.ts +87 -0
- package/src/core/scope-glob.ts +56 -0
- package/src/core/scripts.ts +6 -0
- package/src/core/skill-manifests.ts +173 -0
- package/src/core/store-lock.ts +374 -0
- package/src/core/support-matrix.ts +24 -0
- package/src/core/task-context.ts +247 -0
- package/src/core/task-contract.ts +1279 -0
- package/src/core/task-delivery.ts +109 -0
- package/src/core/task-engine.ts +2248 -0
- package/src/core/task-evaluation.ts +1147 -0
- package/src/core/task-store.ts +2649 -0
- package/src/core/task-view.ts +180 -0
- package/src/core/templates.ts +22 -0
- package/src/core/tracker-issues.ts +174 -0
- package/src/core/vcs-config.ts +540 -0
- package/src/core/workers.ts +62 -0
- package/src/core/workit-paths.mjs +242 -0
- package/src/core/workspace-branch-policy.ts +123 -0
- package/src/core/workspaces.ts +1745 -0
- package/src/core/youtrack-backend.ts +288 -0
- package/src/core/youtrack-tools.ts +260 -0
- package/src/core/youtrack-writes.ts +73 -0
- package/src/core/youtrack-ytrack.ts +217 -0
- package/src/core/youtrack.ts +454 -0
- package/src/core/ytrack-skill.ts +297 -0
- package/src/core.ts +171 -0
- package/src/deslop/lex.ts +271 -0
- package/src/deslop/scan.ts +232 -0
- package/src/duration.ts +27 -0
- package/src/fanout-brief.ts +486 -0
- package/src/fanout-check.ts +455 -0
- package/src/fanout-landed.ts +370 -0
- package/src/fanout-stack.ts +108 -0
- package/src/fanout-status.ts +362 -0
- package/src/fanout-worktree.ts +893 -0
- package/src/fanout.ts +1473 -0
- package/src/forge/exec.ts +385 -0
- package/src/forge/github.ts +831 -0
- package/src/forge/gitlab.ts +906 -0
- package/src/forge/issue-forge.ts +209 -0
- package/src/forge/issue-link.ts +468 -0
- package/src/forge/issue-ops.ts +314 -0
- package/src/forge/merge-gate-base-red.ts +72 -0
- package/src/forge/pr-body.ts +100 -0
- package/src/forge/pr-edit.ts +292 -0
- package/src/forge/pr-ops.ts +1109 -0
- package/src/forge/pr-status.ts +10 -0
- package/src/forge/redact.ts +195 -0
- package/src/forge/report.ts +994 -0
- package/src/forge/reruns.ts +132 -0
- package/src/forge/resolve.ts +509 -0
- package/src/forge/types.ts +242 -0
- package/src/forge/verify.ts +398 -0
- package/src/git/default-base.ts +130 -0
- package/src/git/env-redirect.ts +28 -0
- package/src/git/facts.ts +122 -0
- package/src/git/ops.ts +1823 -0
- package/src/git/refs-read.ts +332 -0
- package/src/git/release.ts +1020 -0
- package/src/git/rev.ts +1175 -0
- package/src/git/session-hook.ts +597 -0
- package/src/git/stage.ts +63 -0
- package/src/hooks/agent-model.ts +149 -0
- package/src/hooks/budget-signal.ts +284 -0
- package/src/hooks/context.ts +461 -0
- package/src/hooks/cwd-normalize.ts +79 -0
- package/src/hooks/deadline.ts +228 -0
- package/src/hooks/delivery-verbs.ts +182 -0
- package/src/hooks/descriptor.ts +124 -0
- package/src/hooks/handle.ts +433 -0
- package/src/hooks/heavy-gate.ts +99 -0
- package/src/hooks/hook-runs.ts +130 -0
- package/src/hooks/hosts/claude-code.ts +380 -0
- package/src/hooks/hosts/codex.ts +372 -0
- package/src/hooks/hosts/cursor.ts +419 -0
- package/src/hooks/hosts/fields.ts +98 -0
- package/src/hooks/hosts/opencode.ts +79 -0
- package/src/hooks/hosts/pi.ts +78 -0
- package/src/hooks/index.ts +60 -0
- package/src/hooks/model-limit.ts +378 -0
- package/src/hooks/model-switch.ts +35 -0
- package/src/hooks/policy.ts +29 -0
- package/src/hooks/process.ts +48 -0
- package/src/hooks/protocol.ts +135 -0
- package/src/hooks/raw-git.ts +2289 -0
- package/src/hooks/repeat-failure.ts +115 -0
- package/src/hooks/run.ts +54 -0
- package/src/hooks/session-marker.ts +151 -0
- package/src/hooks/shell-words.ts +532 -0
- package/src/hooks/skill-nudge.ts +295 -0
- package/src/hooks/spawn-gate.ts +72 -0
- package/src/hooks/standing-signal.ts +99 -0
- package/src/hooks/standing.ts +225 -0
- package/src/hooks/steering.ts +74 -0
- package/src/hooks/stop.ts +408 -0
- package/src/hooks/worktree-confine.ts +415 -0
- package/src/hooks/write-gate.ts +1174 -0
- package/src/host-session.ts +459 -0
- package/src/knowledge.ts +368 -0
- package/src/ledger.ts +2913 -0
- package/src/stack.ts +2570 -0
- package/src/store/event-log.ts +217 -0
- package/src/store/notes.ts +29 -0
- package/src/store/patch.ts +135 -0
- package/src/store/paths.ts +368 -0
- package/src/store/reduce.ts +203 -0
- package/src/test-audit-mocks.ts +309 -0
- package/src/verdict-cover.ts +151 -0
- package/src/worktree-deps.ts +142 -0
- package/templates/headers.md +3 -0
- package/templates/hygiene/.editorconfig +8 -0
- package/templates/hygiene/.gitattributes +3 -0
- package/templates/hygiene/CHANGELOG.md +14 -0
- package/templates/hygiene/CONTRIBUTING.md +3 -0
- package/templates/hygiene/LICENSE +21 -0
- package/templates/hygiene/README.md +3 -0
- package/templates/issue-update.md +5 -0
- package/templates/plan-template.md +26 -0
- package/templates/spec-template.md +64 -0
- package/templates/workit-contract.md +13 -0
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
// Which `workit` CLI a host launcher hands its agent (#648). The host
|
|
2
|
+
// launchers (Codex hooks and MCP, Cursor hooks, the OpenCode plugin, the Pi
|
|
3
|
+
// extension) run bundled code, but the agent's shell runs `workit` from PATH,
|
|
4
|
+
// where a stale global install can sit ahead of the Workit-owned launcher.
|
|
5
|
+
// Order: the owned launcher (<install root>/bin/workit), else the owned
|
|
6
|
+
// `current` entry, else a bundled CLI the caller names, else plain `workit`
|
|
7
|
+
// from PATH (setups older than the owned install; `workit doctor` row
|
|
8
|
+
// cli_installs names the fix).
|
|
9
|
+
//
|
|
10
|
+
// Dependency-light on purpose (node: builtins only): the Codex hook launcher
|
|
11
|
+
// loads it on every tool call before the runtime. installRoot/launcherDir are
|
|
12
|
+
// the one copy: packages/workit-cli/src/install/paths.ts (the writer) re-exports them.
|
|
13
|
+
import { closeSync, existsSync, openSync, readSync } from "node:fs";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { dataDir, userHome } from "./workit-paths.mjs";
|
|
16
|
+
|
|
17
|
+
/** The first line after the shebang of every launcher Workit writes. */
|
|
18
|
+
export const LAUNCHER_MARKER = "workit-owned-launcher";
|
|
19
|
+
const CLI_PACKAGE_PATH = ["node_modules", "@workit-dev", "cli"];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The Workit-owned install root: `WORKIT_INSTALL_ROOT`, else the data dir
|
|
23
|
+
* (core/workit-paths.mjs: `$XDG_DATA_HOME/workit` or `~/.local/share/workit`),
|
|
24
|
+
* and `<data dir>\\install` on Windows (`%LOCALAPPDATA%\\workit\\install`).
|
|
25
|
+
*/
|
|
26
|
+
export const installRoot = (
|
|
27
|
+
env: NodeJS.ProcessEnv,
|
|
28
|
+
platform: NodeJS.Platform = process.platform,
|
|
29
|
+
): string => {
|
|
30
|
+
if (env.WORKIT_INSTALL_ROOT) return env.WORKIT_INSTALL_ROOT;
|
|
31
|
+
const data = dataDir(env, platform);
|
|
32
|
+
return platform === "win32" ? path.join(data, "install") : data;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/** The directory on PATH that holds the `workit` link to the owned launcher. */
|
|
36
|
+
export const launcherDir = (
|
|
37
|
+
env: NodeJS.ProcessEnv,
|
|
38
|
+
platform: NodeJS.Platform = process.platform,
|
|
39
|
+
): string => {
|
|
40
|
+
if (env.WORKIT_BIN_DIR) return env.WORKIT_BIN_DIR;
|
|
41
|
+
if (platform === "win32") return path.join(installRoot(env, platform), "bin");
|
|
42
|
+
return env.XDG_BIN_HOME || path.join(userHome(env), ".local", "bin");
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** The launcher Workit writes and owns: <install root>/bin/workit (workit.cmd on Windows). */
|
|
46
|
+
export const ownedLauncherPath = (
|
|
47
|
+
env: NodeJS.ProcessEnv,
|
|
48
|
+
platform: NodeJS.Platform = process.platform,
|
|
49
|
+
): string =>
|
|
50
|
+
path.join(installRoot(env, platform), "bin", platform === "win32" ? "workit.cmd" : "workit");
|
|
51
|
+
|
|
52
|
+
/** The CLI entry inside the owned `current` install. */
|
|
53
|
+
export const ownedCurrentEntry = (
|
|
54
|
+
env: NodeJS.ProcessEnv,
|
|
55
|
+
platform: NodeJS.Platform = process.platform,
|
|
56
|
+
): string =>
|
|
57
|
+
path.join(installRoot(env, platform), "current", ...CLI_PACKAGE_PATH, "dist", "index.js");
|
|
58
|
+
|
|
59
|
+
/** The first bytes of a file, or "" when it cannot be read. */
|
|
60
|
+
const head = (file: string): string => {
|
|
61
|
+
let fd: number | undefined;
|
|
62
|
+
try {
|
|
63
|
+
fd = openSync(file, "r");
|
|
64
|
+
const buffer = Buffer.alloc(256);
|
|
65
|
+
const read = readSync(fd, buffer, 0, buffer.length, 0);
|
|
66
|
+
return buffer.subarray(0, read).toString("utf8");
|
|
67
|
+
} catch {
|
|
68
|
+
return "";
|
|
69
|
+
} finally {
|
|
70
|
+
if (fd !== undefined) closeSync(fd);
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
export type CliSource = "owned-launcher" | "owned-current" | "bundled" | "path";
|
|
75
|
+
|
|
76
|
+
export interface LocatedCli {
|
|
77
|
+
source: CliSource;
|
|
78
|
+
/** What to spawn. */
|
|
79
|
+
command: string;
|
|
80
|
+
/** Arguments before the workit verb (the entry script when command is a node). */
|
|
81
|
+
args: string[];
|
|
82
|
+
/** A directory whose `workit` is this CLI, to put first on PATH; null when none. */
|
|
83
|
+
binDir: string | null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface LocateOptions {
|
|
87
|
+
env?: NodeJS.ProcessEnv;
|
|
88
|
+
platform?: NodeJS.Platform;
|
|
89
|
+
/** A CLI entry script the host package ships, tried after the owned install. */
|
|
90
|
+
bundled?: string | null;
|
|
91
|
+
/** The node that runs an entry script (default: this process's). */
|
|
92
|
+
node?: string;
|
|
93
|
+
exists?: (file: string) => boolean;
|
|
94
|
+
read?: (file: string) => string;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/** The workit CLI a host launcher should hand its agent; never a PATH lookup unless nothing owned or bundled exists. */
|
|
98
|
+
export const locateWorkitCli = (options: LocateOptions = {}): LocatedCli => {
|
|
99
|
+
const env = options.env ?? process.env;
|
|
100
|
+
const platform = options.platform ?? process.platform;
|
|
101
|
+
const exists = options.exists ?? existsSync;
|
|
102
|
+
const read = options.read ?? head;
|
|
103
|
+
const node = options.node ?? process.execPath;
|
|
104
|
+
const launcher = ownedLauncherPath(env, platform);
|
|
105
|
+
if (exists(launcher) && read(launcher).includes(LAUNCHER_MARKER))
|
|
106
|
+
return {
|
|
107
|
+
source: "owned-launcher",
|
|
108
|
+
command: launcher,
|
|
109
|
+
args: [],
|
|
110
|
+
binDir: path.dirname(launcher),
|
|
111
|
+
};
|
|
112
|
+
const current = ownedCurrentEntry(env, platform);
|
|
113
|
+
if (exists(current))
|
|
114
|
+
return {
|
|
115
|
+
source: "owned-current",
|
|
116
|
+
command: node,
|
|
117
|
+
args: [current],
|
|
118
|
+
binDir: null,
|
|
119
|
+
};
|
|
120
|
+
if (options.bundled && exists(options.bundled))
|
|
121
|
+
return {
|
|
122
|
+
source: "bundled",
|
|
123
|
+
command: node,
|
|
124
|
+
args: [options.bundled],
|
|
125
|
+
binDir: null,
|
|
126
|
+
};
|
|
127
|
+
return { source: "path", command: "workit", args: [], binDir: null };
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/** The env key that holds PATH (Windows keeps `Path`). */
|
|
131
|
+
const pathKey = (env: NodeJS.ProcessEnv): string =>
|
|
132
|
+
Object.keys(env).find((key) => key.toUpperCase() === "PATH") ?? "PATH";
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The env changes that make a shell started from `env` run the located CLI
|
|
136
|
+
* as `workit`: its bin dir first on PATH (moved there when already present)
|
|
137
|
+
* and WORKIT_CLI naming it. Empty when the CLI comes from PATH anyway.
|
|
138
|
+
*/
|
|
139
|
+
export const workitCliEnv = (
|
|
140
|
+
located: LocatedCli,
|
|
141
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
142
|
+
platform: NodeJS.Platform = process.platform,
|
|
143
|
+
): Record<string, string> => {
|
|
144
|
+
if (located.source === "path") return {};
|
|
145
|
+
const patch: Record<string, string> = {
|
|
146
|
+
WORKIT_CLI: [located.command, ...located.args].join(" "),
|
|
147
|
+
};
|
|
148
|
+
if (located.binDir) {
|
|
149
|
+
const key = pathKey(env);
|
|
150
|
+
const delimiter = platform === "win32" ? ";" : ":";
|
|
151
|
+
const same = (dir: string) =>
|
|
152
|
+
platform === "win32"
|
|
153
|
+
? path.win32.normalize(dir).toLowerCase() ===
|
|
154
|
+
path.win32.normalize(located.binDir!).toLowerCase()
|
|
155
|
+
: path.posix.normalize(dir) === path.posix.normalize(located.binDir!);
|
|
156
|
+
const rest = (env[key] ?? "").split(delimiter).filter((dir) => dir && !same(dir));
|
|
157
|
+
patch[key] = [located.binDir, ...rest].join(delimiter);
|
|
158
|
+
}
|
|
159
|
+
return patch;
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Locate the CLI and apply its env changes to `env` (process.env by default),
|
|
164
|
+
* so this process and every shell it starts runs the owned `workit`.
|
|
165
|
+
*/
|
|
166
|
+
export const useOwnedWorkitCli = (
|
|
167
|
+
options: LocateOptions & { env?: NodeJS.ProcessEnv } = {},
|
|
168
|
+
): LocatedCli => {
|
|
169
|
+
const env = options.env ?? process.env;
|
|
170
|
+
const located = locateWorkitCli({ ...options, env });
|
|
171
|
+
Object.assign(env, workitCliEnv(located, env, options.platform));
|
|
172
|
+
return located;
|
|
173
|
+
};
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Commit message flavors: matchers plus history detection for the git.commit
|
|
2
|
+
// gate. Detection is deterministic (majority vote, no LLM): sample recent
|
|
3
|
+
// subjects, classify each, take the majority above threshold, else null so the
|
|
4
|
+
// caller falls back to the configured preset.
|
|
5
|
+
|
|
6
|
+
export type CommitFlavor = "conventional" | "gitmoji" | "ticket-prefix" | "freeform";
|
|
7
|
+
export type CommitFlavorPreset = CommitFlavor | "custom" | "auto";
|
|
8
|
+
|
|
9
|
+
const CONVENTIONAL_RE =
|
|
10
|
+
/^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\(.+\))?(!)?: .+/;
|
|
11
|
+
const TICKET_RE = /^[A-Z]{2,}-\d+\b/;
|
|
12
|
+
const GITMOJI_RE = /^\p{Extended_Pictographic}/u;
|
|
13
|
+
|
|
14
|
+
export const matchCommitFlavor = (
|
|
15
|
+
message: string,
|
|
16
|
+
flavor: CommitFlavorPreset,
|
|
17
|
+
pattern?: string,
|
|
18
|
+
): boolean => {
|
|
19
|
+
const subject = message.split("\n", 1)[0] ?? "";
|
|
20
|
+
switch (flavor) {
|
|
21
|
+
case "conventional":
|
|
22
|
+
return CONVENTIONAL_RE.test(subject);
|
|
23
|
+
case "gitmoji":
|
|
24
|
+
return GITMOJI_RE.test(subject);
|
|
25
|
+
case "ticket-prefix":
|
|
26
|
+
return TICKET_RE.test(subject);
|
|
27
|
+
case "freeform":
|
|
28
|
+
return subject.trim().length > 0;
|
|
29
|
+
case "custom":
|
|
30
|
+
if (!pattern) return false;
|
|
31
|
+
try {
|
|
32
|
+
return new RegExp(pattern).test(subject);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
case "auto":
|
|
37
|
+
return subject.trim().length > 0;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const DETECTABLE: CommitFlavor[] = ["conventional", "gitmoji", "ticket-prefix"];
|
|
42
|
+
|
|
43
|
+
export const detectCommitFlavor = (
|
|
44
|
+
subjects: string[],
|
|
45
|
+
threshold = 0.7,
|
|
46
|
+
): { flavor: CommitFlavor | null; confidence: number } => {
|
|
47
|
+
if (subjects.length === 0) return { flavor: null, confidence: 0 };
|
|
48
|
+
const votes = new Map<CommitFlavor, number>();
|
|
49
|
+
for (const subject of subjects) {
|
|
50
|
+
const hit = DETECTABLE.find((flavor) => matchCommitFlavor(subject, flavor));
|
|
51
|
+
if (hit) votes.set(hit, (votes.get(hit) ?? 0) + 1);
|
|
52
|
+
}
|
|
53
|
+
let best: CommitFlavor | null = null;
|
|
54
|
+
let count = 0;
|
|
55
|
+
for (const [flavor, votesFor] of votes) {
|
|
56
|
+
if (votesFor > count) {
|
|
57
|
+
best = flavor;
|
|
58
|
+
count = votesFor;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const confidence = count / subjects.length;
|
|
62
|
+
return best !== null && confidence >= threshold
|
|
63
|
+
? { flavor: best, confidence }
|
|
64
|
+
: { flavor: null, confidence };
|
|
65
|
+
};
|