@shxd/mmit 0.1.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 (47) hide show
  1. package/README.md +84 -0
  2. package/bin/mmit +8 -0
  3. package/dist/config.d.ts +14 -0
  4. package/dist/config.d.ts.map +1 -0
  5. package/dist/config.js +64 -0
  6. package/dist/config.js.map +1 -0
  7. package/dist/engine.d.ts +8 -0
  8. package/dist/engine.d.ts.map +1 -0
  9. package/dist/engine.js +116 -0
  10. package/dist/engine.js.map +1 -0
  11. package/dist/git.d.ts +16 -0
  12. package/dist/git.d.ts.map +1 -0
  13. package/dist/git.js +90 -0
  14. package/dist/git.js.map +1 -0
  15. package/dist/index.d.ts +3 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +270 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/provider.d.ts +11 -0
  20. package/dist/provider.d.ts.map +1 -0
  21. package/dist/provider.js +96 -0
  22. package/dist/provider.js.map +1 -0
  23. package/dist/providers/anthropic.d.ts +3 -0
  24. package/dist/providers/anthropic.d.ts.map +1 -0
  25. package/dist/providers/anthropic.js +17 -0
  26. package/dist/providers/anthropic.js.map +1 -0
  27. package/dist/providers/gemini.d.ts +3 -0
  28. package/dist/providers/gemini.d.ts.map +1 -0
  29. package/dist/providers/gemini.js +20 -0
  30. package/dist/providers/gemini.js.map +1 -0
  31. package/dist/providers/index.d.ts +4 -0
  32. package/dist/providers/index.d.ts.map +1 -0
  33. package/dist/providers/index.js +11 -0
  34. package/dist/providers/index.js.map +1 -0
  35. package/dist/providers/openai.d.ts +3 -0
  36. package/dist/providers/openai.d.ts.map +1 -0
  37. package/dist/providers/openai.js +18 -0
  38. package/dist/providers/openai.js.map +1 -0
  39. package/dist/providers/openrouter.d.ts +3 -0
  40. package/dist/providers/openrouter.d.ts.map +1 -0
  41. package/dist/providers/openrouter.js +21 -0
  42. package/dist/providers/openrouter.js.map +1 -0
  43. package/dist/providers/types.d.ts +10 -0
  44. package/dist/providers/types.d.ts.map +1 -0
  45. package/dist/providers/types.js +2 -0
  46. package/dist/providers/types.js.map +1 -0
  47. package/package.json +30 -0
package/README.md ADDED
@@ -0,0 +1,84 @@
1
+ ```
2
+ █▓ █▓▄
3
+ ██▀██▀▓▄ ██▀██▀▓▄ ▄▄▄ ██
4
+ ██ ██ ██ ██ ██ ██ ██ ▀██▄
5
+ ```
6
+
7
+ # mmit
8
+
9
+ AI-powered conventional commit message generator. Works with OpenAI, Anthropic, Gemini, and OpenRouter.
10
+
11
+ ```
12
+ mmit
13
+ ```
14
+
15
+ ## Install
16
+
17
+ ```bash
18
+ npm install -g @shxd/mmit
19
+ ```
20
+
21
+ ## Setup
22
+
23
+ ```bash
24
+ mmit init
25
+ ```
26
+
27
+ This walks you through selecting a provider and API key. Alternatively, set an environment variable:
28
+
29
+ - **OpenAI** — `OPENAI_API_KEY`
30
+ - **Anthropic** — `ANTHROPIC_API_KEY`
31
+ - **Gemini** — `GEMINI_API_KEY`
32
+ - **OpenRouter** — `OPENROUTER_API_KEY`
33
+
34
+ ## Usage
35
+
36
+ Stage your changes and run:
37
+
38
+ ```bash
39
+ git add .
40
+ mmit
41
+ ```
42
+
43
+ mmit analyzes the diff, generates a conventional commit message, and lets you review, edit, or regenerate before committing.
44
+
45
+ ### Multi-line bodies
46
+
47
+ When the AI generates a body explaining the change, you can choose to commit with the subject only or include the body.
48
+
49
+ ### Non-interactive
50
+
51
+ ```bash
52
+ mmit --dry-run # preview without committing
53
+ mmit --auto # skip the interactive prompt
54
+ ```
55
+
56
+ ## Options
57
+
58
+ - `-p, --provider <name>` — Use a specific provider (openai, anthropic, gemini, openrouter)
59
+ - `-m, --model <name>` — Override the default model
60
+ - `--dry-run` — Generate the message without committing
61
+ - `--auto` — Skip the interactive review prompt
62
+ - `--diff-only` — Print the staged diff and exit
63
+ - `--config` — Print the current config
64
+
65
+ ## Configuration
66
+
67
+ Global: `~/.mmit.json`
68
+ Project: `.mmit.json` in the project root (overrides global)
69
+
70
+ ```json
71
+ {
72
+ "provider": "gemini",
73
+ "model": "gemini-3.1-flash-lite",
74
+ "commitTypes": ["feat", "fix", "chore", "refactor", "docs", "style", "test", "perf", "ci", "build", "revert"]
75
+ }
76
+ ```
77
+
78
+ ## How it works
79
+
80
+ 1. mmit reads your staged git diff
81
+ 2. Sends it to the configured AI provider with a conventional commits prompt
82
+ 3. Parses the response into a subject and optional body
83
+ 4. Presents the message for review, editing, or regeneration
84
+ 5. Commits when you confirm
package/bin/mmit ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from '../dist/index.js';
4
+
5
+ run().catch((err) => {
6
+ console.error(err);
7
+ process.exit(1);
8
+ });
@@ -0,0 +1,14 @@
1
+ export interface Config {
2
+ provider?: string;
3
+ model?: string;
4
+ apiKey?: string;
5
+ maxDiffTokens?: number;
6
+ commitTypes?: string[];
7
+ autoConfirm?: boolean;
8
+ }
9
+ export declare function loadConfig(): Config;
10
+ export declare function saveGlobalConfig(config: Config): void;
11
+ declare const ENV_KEY_MAP: Record<string, string>;
12
+ export declare function detectProviderFromEnv(preferred?: string): string | null;
13
+ export { ENV_KEY_MAP as envKeyMap };
14
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,MAAM;IAErB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IAEvB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAgCD,wBAAgB,UAAU,IAAI,MAAM,CAWnC;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAIrD;AAED,QAAA,MAAM,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAKvC,CAAC;AAEF,wBAAgB,qBAAqB,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CASvE;AAED,OAAO,EAAE,WAAW,IAAI,SAAS,EAAE,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,64 @@
1
+ import { readFileSync, existsSync, writeFileSync, mkdirSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join, resolve } from 'node:path';
4
+ const GLOBAL_CONFIG_PATH = join(homedir(), '.mmit.json');
5
+ const DEFAULT_CONFIG = {
6
+ maxDiffTokens: 8000,
7
+ commitTypes: [
8
+ 'feat', 'fix', 'chore', 'docs', 'refactor',
9
+ 'test', 'style', 'perf', 'ci', 'build', 'revert',
10
+ ],
11
+ autoConfirm: false,
12
+ };
13
+ function findLocalConfig() {
14
+ let dir = process.cwd();
15
+ while (true) {
16
+ const file = join(dir, '.mmit.json');
17
+ if (existsSync(file)) {
18
+ try {
19
+ return JSON.parse(readFileSync(file, 'utf-8'));
20
+ }
21
+ catch {
22
+ return null;
23
+ }
24
+ }
25
+ const parent = resolve(dir, '..');
26
+ if (parent === dir)
27
+ return null;
28
+ dir = parent;
29
+ }
30
+ }
31
+ let cached = null;
32
+ export function loadConfig() {
33
+ if (cached)
34
+ return cached;
35
+ const global = existsSync(GLOBAL_CONFIG_PATH)
36
+ ? JSON.parse(readFileSync(GLOBAL_CONFIG_PATH, 'utf-8'))
37
+ : {};
38
+ const local = findLocalConfig();
39
+ cached = { ...DEFAULT_CONFIG, ...global, ...local };
40
+ return cached;
41
+ }
42
+ export function saveGlobalConfig(config) {
43
+ mkdirSync(homedir(), { recursive: true });
44
+ writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(config, null, 2), 'utf-8');
45
+ cached = config;
46
+ }
47
+ const ENV_KEY_MAP = {
48
+ openai: 'OPENAI_API_KEY',
49
+ anthropic: 'ANTHROPIC_API_KEY',
50
+ gemini: 'GEMINI_API_KEY',
51
+ openrouter: 'OPENROUTER_API_KEY',
52
+ };
53
+ export function detectProviderFromEnv(preferred) {
54
+ const order = preferred && ENV_KEY_MAP[preferred]
55
+ ? [preferred, ...Object.keys(ENV_KEY_MAP).filter(k => k !== preferred)]
56
+ : Object.keys(ENV_KEY_MAP);
57
+ for (const name of order) {
58
+ if (process.env[ENV_KEY_MAP[name]])
59
+ return name;
60
+ }
61
+ return null;
62
+ }
63
+ export { ENV_KEY_MAP as envKeyMap };
64
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ export interface GeneratedMessage {
2
+ subject: string;
3
+ body?: string;
4
+ provider: string;
5
+ model: string;
6
+ }
7
+ export declare function generateCommitMessage(diff: string, truncated: boolean, overrideProvider?: string, overrideModel?: string): Promise<GeneratedMessage>;
8
+ //# sourceMappingURL=engine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf;AAyED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,OAAO,EAClB,gBAAgB,CAAC,EAAE,MAAM,EACzB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,gBAAgB,CAAC,CA4D3B"}
package/dist/engine.js ADDED
@@ -0,0 +1,116 @@
1
+ import { providers } from './provider.js';
2
+ import { loadConfig, detectProviderFromEnv, envKeyMap } from './config.js';
3
+ const COMMIT_PATTERN = /^[a-zA-Z]+(\([a-zA-Z0-9_.\-,/]+\))?!?:\s.+/;
4
+ function isValidCommitMessage(msg) {
5
+ return COMMIT_PATTERN.test(msg) && msg.length <= 100;
6
+ }
7
+ function splitSubjectBody(raw) {
8
+ const cleaned = raw
9
+ .replace(/^```[\w]*\n?/gm, '')
10
+ .replace(/```$/gm, '')
11
+ .replace(/^['"]|['"]$/g, '')
12
+ .trim();
13
+ const lines = cleaned.split('\n');
14
+ const subject = lines[0].trim();
15
+ const rest = lines.slice(1).map(l => l.trim()).filter(Boolean).join('\n');
16
+ return { subject, body: rest || undefined };
17
+ }
18
+ function buildPrompt(diff, commitTypes, truncated, strict = false) {
19
+ const types = commitTypes.join(', ');
20
+ const strictRule = strict
21
+ ? '\nCRITICAL: Respond with ONLY the first line of the commit message. No body, no explanations.'
22
+ : '\nYou may optionally include a body paragraph explaining the change.';
23
+ return `Generate a conventional commit message for the following git diff.
24
+
25
+ Commit types available: ${types}
26
+
27
+ Format:
28
+ <type>(<scope>): <description>
29
+
30
+ <optional body — explain why, not how>
31
+
32
+ <optional footers>
33
+
34
+ Examples:
35
+
36
+ - feat(api): add user authentication endpoint
37
+
38
+ - Required for mobile clients that need long-lived sessions
39
+ - Prevents abuse of the auth endpoint under heavy load
40
+ Closes #142
41
+
42
+ - fix(parser): handle null input gracefully
43
+
44
+ - Null values from the upstream API were crashing the parser
45
+ Co-authored-by: Alice <alice@example.com>
46
+
47
+ - feat(api)!: remove deprecated /v2/users endpoint
48
+
49
+ BREAKING CHANGE: The /v2/users endpoint has been removed. Use /v3/users instead.
50
+
51
+ Rules:
52
+
53
+ - Use the imperative mood ("add" not "added" / "adds")
54
+ - Subject line max 72 characters
55
+ - Scope is optional — infer from the files changed
56
+ - Always separate the subject and body with a blank line
57
+ - Write the body as bullet points (each prefixed with "-")
58
+ - Use the body to explain WHY the change was made, not WHAT (the diff already shows the what)
59
+ - Use footers for issue references (Closes, Refs), breaking changes (BREAKING CHANGE:), and co-authors
60
+ - For breaking changes, add "!" after the type/scope AND optionally a BREAKING CHANGE footer
61
+ - Respond with only the commit message — no intro, no explanation${strictRule}
62
+
63
+ ${truncated ? '(Note: the diff was truncated due to size. Generate a message for what is visible.)\n' : ''}
64
+ Diff:
65
+ ${diff}`;
66
+ }
67
+ export async function generateCommitMessage(diff, truncated, overrideProvider, overrideModel) {
68
+ const config = loadConfig();
69
+ // Determine provider and model
70
+ const providerName = overrideProvider
71
+ || config.provider
72
+ || detectProviderFromEnv(config.provider)
73
+ || 'openai';
74
+ const provider = providers[providerName];
75
+ if (!provider) {
76
+ throw new Error(`Unknown provider "${providerName}". Available: ${Object.keys(providers).join(', ')}`);
77
+ }
78
+ const apiKey = process.env[envKeyMap[providerName]] || config.apiKey;
79
+ if (!apiKey) {
80
+ throw new Error(`Missing ${envKeyMap[providerName]} for provider "${providerName}". Run \`mmit init\` to set one up.`);
81
+ }
82
+ // Determine model with good defaults
83
+ const defaultModels = {
84
+ openai: 'gpt-4o-mini',
85
+ anthropic: 'claude-sonnet-4-20250514',
86
+ gemini: 'gemini-3.1-flash-lite',
87
+ openrouter: 'openrouter/free',
88
+ };
89
+ const model = overrideModel || config.model || defaultModels[providerName];
90
+ const providerConfig = {
91
+ apiKey,
92
+ model,
93
+ maxTokens: 500,
94
+ };
95
+ // Attempt 1: normal prompt
96
+ let prompt = buildPrompt(diff, config.commitTypes ?? [], truncated);
97
+ let raw = await provider.generate(prompt, providerConfig);
98
+ let parsed = splitSubjectBody(raw);
99
+ // Attempt 2: stricter prompt if model rambled
100
+ if (!isValidCommitMessage(parsed.subject)) {
101
+ prompt = buildPrompt(diff, config.commitTypes ?? [], truncated, true);
102
+ raw = await provider.generate(prompt, providerConfig);
103
+ parsed = splitSubjectBody(raw);
104
+ }
105
+ // Final check
106
+ if (!isValidCommitMessage(parsed.subject)) {
107
+ throw new Error(`Model returned an invalid response. Try a different model.\n Got: ${parsed.subject}`);
108
+ }
109
+ return {
110
+ subject: parsed.subject,
111
+ body: parsed.body,
112
+ provider: providerName,
113
+ model,
114
+ };
115
+ }
116
+ //# sourceMappingURL=engine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine.js","sourceRoot":"","sources":["../src/engine.ts"],"names":[],"mappings":"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"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ export interface DiffResult {
2
+ staged: boolean;
3
+ diff: string;
4
+ truncated: boolean;
5
+ }
6
+ export interface DiffStats {
7
+ files: number;
8
+ insertions: number;
9
+ deletions: number;
10
+ }
11
+ export declare function isGitRepo(): boolean;
12
+ export declare function getGitDiff(maxTokens?: number): DiffResult;
13
+ export declare function stageAllAndDiff(): DiffResult;
14
+ export declare function getDiffStats(): DiffStats;
15
+ export declare function createCommit(subject: string, body?: string): string;
16
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;CACnB;AAID,wBAAgB,SAAS,IAAI,OAAO,CAOnC;AAUD,wBAAgB,UAAU,CAAC,SAAS,GAAE,MAAa,GAAG,UAAU,CA6B/D;AAoBD,wBAAgB,eAAe,IAAI,UAAU,CAG5C;AAED,wBAAgB,YAAY,IAAI,SAAS,CAcxC;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,MAAM,CAUnE"}
package/dist/git.js ADDED
@@ -0,0 +1,90 @@
1
+ import { execSync } from 'node:child_process';
2
+ const GIT_OPTS = { encoding: 'utf-8', maxBuffer: 10 * 1024 * 1024, stdio: 'pipe' };
3
+ export function isGitRepo() {
4
+ try {
5
+ execSync('git rev-parse --git-dir', { stdio: 'pipe', encoding: 'utf-8' });
6
+ return true;
7
+ }
8
+ catch {
9
+ return false;
10
+ }
11
+ }
12
+ function git(args) {
13
+ try {
14
+ return execSync(`git ${args}`, GIT_OPTS).trim();
15
+ }
16
+ catch {
17
+ return '';
18
+ }
19
+ }
20
+ export function getGitDiff(maxTokens = 8000) {
21
+ if (!isGitRepo()) {
22
+ return { staged: false, diff: '', truncated: false };
23
+ }
24
+ let diff = git('diff --cached');
25
+ let staged = !!diff;
26
+ if (!diff) {
27
+ diff = git('diff HEAD');
28
+ }
29
+ if (!diff) {
30
+ diff = git('diff');
31
+ }
32
+ if (!diff) {
33
+ return { staged: false, diff: '', truncated: false };
34
+ }
35
+ const estTokens = Math.ceil(diff.length / 4);
36
+ let truncated = false;
37
+ if (estTokens > maxTokens) {
38
+ diff = truncateDiff(diff, maxTokens);
39
+ truncated = true;
40
+ }
41
+ return { staged, diff, truncated };
42
+ }
43
+ function truncateDiff(diff, maxTokens) {
44
+ const chars = maxTokens * 4;
45
+ const lines = diff.split('\n');
46
+ const kept = [];
47
+ let length = 0;
48
+ for (const line of lines) {
49
+ const next = length + line.length + 1;
50
+ if (next > chars)
51
+ break;
52
+ kept.push(line);
53
+ length = next;
54
+ }
55
+ kept.push('# ... diff truncated due to size ...');
56
+ return kept.join('\n');
57
+ }
58
+ export function stageAllAndDiff() {
59
+ git('add -A');
60
+ return getGitDiff();
61
+ }
62
+ export function getDiffStats() {
63
+ const staged = !!git('diff --cached --stat');
64
+ const numstat = staged ? git('diff --cached --numstat') : git('diff HEAD --numstat') || git('diff --numstat');
65
+ if (!numstat)
66
+ return { files: 0, insertions: 0, deletions: 0 };
67
+ const lines = numstat.split('\n').filter(Boolean);
68
+ let files = 0, insertions = 0, deletions = 0;
69
+ for (const line of lines) {
70
+ const [add, del] = line.split('\t');
71
+ if (add && add !== '-')
72
+ insertions += parseInt(add, 10);
73
+ if (del && del !== '-')
74
+ deletions += parseInt(del, 10);
75
+ files++;
76
+ }
77
+ return { files, insertions, deletions };
78
+ }
79
+ export function createCommit(subject, body) {
80
+ const esc = (s) => s.replace(/["`$\\]/g, '\\$&');
81
+ const cmd = body
82
+ ? `git commit -m "${esc(subject)}" -m "${esc(body)}"`
83
+ : `git commit -m "${esc(subject)}"`;
84
+ execSync(cmd, {
85
+ stdio: 'inherit',
86
+ encoding: 'utf-8',
87
+ });
88
+ return git('rev-parse --short HEAD');
89
+ }
90
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export declare function run(): Promise<void>;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA0BA,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CA4NzC"}
package/dist/index.js ADDED
@@ -0,0 +1,270 @@
1
+ #!/usr/bin/env node
2
+ import { program } from 'commander';
3
+ import * as p from '@clack/prompts';
4
+ import pico from 'picocolors';
5
+ import { loadConfig, saveGlobalConfig, detectProviderFromEnv } from './config.js';
6
+ import { generateCommitMessage } from './engine.js';
7
+ import { getGitDiff, stageAllAndDiff, createCommit, getDiffStats, isGitRepo } from './git.js';
8
+ const LOGO = `\
9
+ █▓ █▓▄
10
+ ██▀██▀▓▄ ██▀██▀▓▄ ▄▄▄ ██
11
+ ██ ██ ██ ██ ██ ██ ██ ▀██▄`;
12
+ function renderHeader(version) {
13
+ const lines = LOGO.split('\n');
14
+ const maxWidth = Math.max(...lines.map(l => l.length));
15
+ const v = pico.dim(`v${version}`);
16
+ return pico.dim(lines.map((l, i) => {
17
+ if (i === 0)
18
+ return l + ' '.repeat(maxWidth - l.length + 2) + v;
19
+ return l;
20
+ }).join('\n'));
21
+ }
22
+ const VERSION = '0.1.0';
23
+ export async function run() {
24
+ // Handle `mmit init` before commander parsing
25
+ if (process.argv.includes('init')) {
26
+ await handleInit();
27
+ return;
28
+ }
29
+ if (!isGitRepo()) {
30
+ process.stderr.write(renderHeader(VERSION) + '\n');
31
+ p.intro('');
32
+ p.outro(pico.red('Not a git repository'));
33
+ process.exit(1);
34
+ }
35
+ program
36
+ .name('mmit')
37
+ .description('AI-powered git commit message generator')
38
+ .version(VERSION)
39
+ .option('-p, --provider <name>', 'AI provider (openai, anthropic, gemini, openrouter)')
40
+ .option('-m, --model <name>', 'Model name override')
41
+ .option('-d, --diff-only', 'Print diff and exit')
42
+ .option('--dry-run', 'Generate message but do not commit')
43
+ .option('--config', 'Open global config for editing')
44
+ .option('--auto', 'Auto-confirm without interactive prompt');
45
+ program.parse(process.argv);
46
+ const opts = program.opts();
47
+ // Handle --config
48
+ if (opts.config) {
49
+ const config = loadConfig();
50
+ if (config.apiKey) {
51
+ const key = config.apiKey;
52
+ const prefix = key.slice(0, 4);
53
+ const suffix = key.slice(-4);
54
+ config.apiKey = `${prefix}...${suffix}`;
55
+ }
56
+ console.log(JSON.stringify(config, null, 2));
57
+ console.error('\n Global config: ~/.mmit.json');
58
+ console.error(' Local config: .mmit.json in project root (overrides global)');
59
+ return;
60
+ }
61
+ // Handle --diff-only
62
+ if (opts.diffOnly) {
63
+ const { diff } = getGitDiff();
64
+ console.log(diff || '(no diff)');
65
+ return;
66
+ }
67
+ const provider = opts.provider || loadConfig().provider || detectProviderFromEnv();
68
+ const model = opts.model;
69
+ process.stderr.write(renderHeader(VERSION) + '\n');
70
+ p.intro('');
71
+ const diffResult = getGitDiff();
72
+ let diff = diffResult.diff;
73
+ let staged = diffResult.staged;
74
+ if (!diff) {
75
+ const shouldStage = await p.confirm({
76
+ message: 'No staged changes. Stage all and proceed?',
77
+ initialValue: true,
78
+ });
79
+ if (p.isCancel(shouldStage) || !shouldStage) {
80
+ p.outro('Cancelled.');
81
+ return;
82
+ }
83
+ const result = stageAllAndDiff();
84
+ diff = result.diff;
85
+ staged = result.staged;
86
+ if (!diff) {
87
+ p.outro(pico.red('No diff to commit.'));
88
+ return;
89
+ }
90
+ }
91
+ // Detect if we need a provider setup
92
+ if (!provider) {
93
+ p.log.warn('No API key found.');
94
+ const runInit = await p.confirm({
95
+ message: 'Run `mmit init` to set one up?',
96
+ initialValue: true,
97
+ });
98
+ if (p.isCancel(runInit) || !runInit) {
99
+ p.outro('Cancelled.');
100
+ return;
101
+ }
102
+ await handleInit();
103
+ return;
104
+ }
105
+ const diffStats = getDiffStats();
106
+ const statsLine = `${diffStats.files} file${diffStats.files !== 1 ? 's' : ''} changed`;
107
+ p.log.step(`${pico.dim(statsLine)}, ${pico.green(`+${diffStats.insertions}`)} ${pico.red(`-${diffStats.deletions}`)}`);
108
+ const spin = p.spinner();
109
+ spin.start('Generating commit message...');
110
+ let msg;
111
+ try {
112
+ msg = await generateCommitMessage(diff, diffResult.truncated, provider, model);
113
+ }
114
+ catch (err) {
115
+ spin.stop('Error');
116
+ const message = err instanceof Error ? err.message : String(err);
117
+ p.outro(pico.red(`Generation failed: ${message}`));
118
+ process.exit(1);
119
+ return;
120
+ }
121
+ spin.stop('Done');
122
+ p.log.step(pico.dim(`${msg.provider} · ${msg.model}`));
123
+ // Interactive loop
124
+ while (true) {
125
+ p.log.step(msg.subject);
126
+ if (msg.body) {
127
+ const bullets = msg.body
128
+ .split('\n')
129
+ .map(l => ` ${l.replace(/^-\s*/, '• ')}`)
130
+ .join('\n');
131
+ p.log.message(bullets);
132
+ }
133
+ if (opts.dryRun || opts.auto || loadConfig().autoConfirm) {
134
+ if (opts.dryRun) {
135
+ console.log(msg.body ? `${msg.subject}\n\n${msg.body}` : msg.subject);
136
+ p.outro('Dry-run — not committing.');
137
+ }
138
+ else {
139
+ const hash = createCommit(msg.subject, msg.body);
140
+ p.outro(pico.green(`Committed as ${hash} (${statsLine}, ${pico.green(`+${diffStats.insertions}`)} ${pico.red(`-${diffStats.deletions}`)})`));
141
+ }
142
+ break;
143
+ }
144
+ const action = await p.select({
145
+ message: 'What do you want to do?',
146
+ options: [
147
+ { value: 'subject', label: 'Commit (subject only)', hint: 'first line only' },
148
+ ...(msg.body ? [{ value: 'body', label: 'Commit (subject + body)', hint: 'includes body' }] : []),
149
+ { value: 'edit', label: 'Edit', hint: 'edit the message manually' },
150
+ { value: 'regenerate', label: 'Regenerate', hint: 'generate a new message' },
151
+ { value: 'cancel', label: 'Cancel' },
152
+ ],
153
+ });
154
+ if (p.isCancel(action) || action === 'cancel') {
155
+ p.outro('Cancelled.');
156
+ break;
157
+ }
158
+ if (action === 'subject') {
159
+ const hash = createCommit(msg.subject, undefined);
160
+ p.outro(pico.green(`Committed as ${hash} (${statsLine}, ${pico.green(`+${diffStats.insertions}`)} ${pico.red(`-${diffStats.deletions}`)})`));
161
+ break;
162
+ }
163
+ if (action === 'body') {
164
+ const hash = createCommit(msg.subject, msg.body);
165
+ p.outro(pico.green(`Committed as ${hash} (${statsLine}, ${pico.green(`+${diffStats.insertions}`)} ${pico.red(`-${diffStats.deletions}`)})`));
166
+ break;
167
+ }
168
+ if (action === 'regenerate') {
169
+ spin.start('Regenerating...');
170
+ try {
171
+ msg = await generateCommitMessage(diff, diffResult.truncated, provider, model);
172
+ }
173
+ catch (err) {
174
+ spin.stop('Error');
175
+ const message = err instanceof Error ? err.message : String(err);
176
+ p.outro(pico.red(`Generation failed: ${message}`));
177
+ process.exit(1);
178
+ }
179
+ spin.stop('Done');
180
+ continue;
181
+ }
182
+ if (action === 'edit') {
183
+ const edited = await p.text({
184
+ message: 'Edit the commit message',
185
+ initialValue: msg.subject,
186
+ validate: (val) => {
187
+ if (!val.trim())
188
+ return 'Message cannot be empty';
189
+ },
190
+ });
191
+ if (p.isCancel(edited)) {
192
+ continue;
193
+ }
194
+ msg.subject = edited.trim();
195
+ p.log.step(msg.subject);
196
+ const confirmEdit = await p.confirm({
197
+ message: 'Commit with this message?',
198
+ initialValue: true,
199
+ });
200
+ if (p.isCancel(confirmEdit)) {
201
+ continue;
202
+ }
203
+ if (confirmEdit) {
204
+ const hash = createCommit(msg.subject, msg.body);
205
+ p.outro(pico.green(`Committed as ${hash} (${statsLine}, ${pico.green(`+${diffStats.insertions}`)} ${pico.red(`-${diffStats.deletions}`)})`));
206
+ break;
207
+ }
208
+ continue;
209
+ }
210
+ }
211
+ }
212
+ const PROVIDER_INFO = {
213
+ openai: { env: 'OPENAI_API_KEY', defaultModel: 'gpt-4o-mini' },
214
+ anthropic: { env: 'ANTHROPIC_API_KEY', defaultModel: 'claude-sonnet-4-20250514' },
215
+ gemini: { env: 'GEMINI_API_KEY', defaultModel: 'gemini-3.1-flash-lite' },
216
+ openrouter: { env: 'OPENROUTER_API_KEY', defaultModel: 'openrouter/free' },
217
+ };
218
+ async function handleInit() {
219
+ process.stderr.write(renderHeader(VERSION) + '\n');
220
+ p.intro('');
221
+ const provider = await p.select({
222
+ message: 'AI provider',
223
+ options: Object.entries(PROVIDER_INFO).map(([name, info]) => ({
224
+ value: name,
225
+ label: name,
226
+ hint: process.env[info.env] ? pico.green('✓ env var set') : `env: ${info.env}`,
227
+ })),
228
+ });
229
+ if (p.isCancel(provider)) {
230
+ p.outro('Cancelled.');
231
+ return;
232
+ }
233
+ const info = PROVIDER_INFO[provider];
234
+ if (!info)
235
+ return;
236
+ let apiKey = process.env[info.env] || '';
237
+ if (!apiKey) {
238
+ const input = await p.password({
239
+ message: `Paste your ${info.env} API key`,
240
+ validate: (val) => {
241
+ if (!val.trim())
242
+ return 'API key cannot be empty';
243
+ },
244
+ });
245
+ if (p.isCancel(input)) {
246
+ p.outro('Cancelled.');
247
+ return;
248
+ }
249
+ apiKey = input.trim();
250
+ }
251
+ const model = await p.text({
252
+ message: `Default model for ${provider}`,
253
+ initialValue: info.defaultModel,
254
+ validate: (val) => {
255
+ if (!val.trim())
256
+ return 'Model name cannot be empty';
257
+ },
258
+ });
259
+ if (p.isCancel(model))
260
+ return;
261
+ const config = loadConfig();
262
+ config.provider = provider;
263
+ config.model = model.trim();
264
+ if (apiKey && !process.env[info.env]) {
265
+ config.apiKey = apiKey;
266
+ }
267
+ saveGlobalConfig(config);
268
+ p.outro(pico.green(`Config saved to ~/.mmit.json`));
269
+ }
270
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export interface ProviderConfig {
2
+ apiKey: string;
3
+ model: string;
4
+ maxTokens?: number;
5
+ }
6
+ export interface Provider {
7
+ name: string;
8
+ generate(prompt: string, config: ProviderConfig): Promise<string>;
9
+ }
10
+ export declare const providers: Record<string, Provider>;
11
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnE;AAsGD,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAK9C,CAAC"}
@@ -0,0 +1,96 @@
1
+ import OpenAI from 'openai';
2
+ import Anthropic from '@anthropic-ai/sdk';
3
+ import { GoogleGenerativeAI } from '@google/generative-ai';
4
+ const systemPrompt = 'You are a git commit message generator. Respond only with the commit message, no extra text.';
5
+ const openaiProvider = {
6
+ name: 'openai',
7
+ async generate(prompt, config) {
8
+ const client = new OpenAI({ apiKey: config.apiKey });
9
+ const res = await client.chat.completions.create({
10
+ model: config.model,
11
+ messages: [
12
+ { role: 'system', content: systemPrompt },
13
+ { role: 'user', content: prompt },
14
+ ],
15
+ temperature: 0.3,
16
+ max_tokens: config.maxTokens ?? 300,
17
+ });
18
+ return res.choices[0]?.message?.content?.trim() ?? '';
19
+ },
20
+ };
21
+ const anthropicProvider = {
22
+ name: 'anthropic',
23
+ async generate(prompt, config) {
24
+ const client = new Anthropic({ apiKey: config.apiKey });
25
+ const res = await client.messages.create({
26
+ model: config.model,
27
+ system: systemPrompt,
28
+ messages: [{ role: 'user', content: prompt }],
29
+ temperature: 0.3,
30
+ max_tokens: config.maxTokens ?? 300,
31
+ });
32
+ const block = res.content[0];
33
+ return block?.type === 'text' ? block.text.trim() : '';
34
+ },
35
+ };
36
+ const geminiProvider = {
37
+ name: 'gemini',
38
+ async generate(prompt, config) {
39
+ const genAI = new GoogleGenerativeAI(config.apiKey);
40
+ const model = genAI.getGenerativeModel({
41
+ model: config.model,
42
+ systemInstruction: systemPrompt,
43
+ });
44
+ const res = await model.generateContent({
45
+ contents: [{ role: 'user', parts: [{ text: prompt }] }],
46
+ generationConfig: {
47
+ temperature: 0.3,
48
+ maxOutputTokens: config.maxTokens ?? 300,
49
+ },
50
+ });
51
+ return res.response.text().trim();
52
+ },
53
+ };
54
+ const openrouterProvider = {
55
+ name: 'openrouter',
56
+ async generate(prompt, config) {
57
+ const res = await fetch('https://openrouter.ai/api/v1/chat/completions', {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Authorization': `Bearer ${config.apiKey}`,
61
+ 'Content-Type': 'application/json',
62
+ 'HTTP-Referer': 'https://github.com/mmit',
63
+ 'X-OpenRouter-Title': 'mmit',
64
+ },
65
+ body: JSON.stringify({
66
+ model: config.model,
67
+ messages: [
68
+ { role: 'system', content: systemPrompt },
69
+ { role: 'user', content: prompt },
70
+ ],
71
+ temperature: 0.3,
72
+ max_tokens: config.maxTokens ?? 300,
73
+ }),
74
+ });
75
+ if (!res.ok) {
76
+ const body = await res.text().catch(() => '');
77
+ throw new Error(`OpenRouter returned ${res.status}: ${body || res.statusText}`);
78
+ }
79
+ const data = await res.json();
80
+ if (data.error) {
81
+ throw new Error(`OpenRouter error: ${data.error.message || JSON.stringify(data.error)}`);
82
+ }
83
+ const choice = data.choices?.[0];
84
+ if (choice?.finish_reason === 'error') {
85
+ return '';
86
+ }
87
+ return choice?.message?.content?.trim() ?? '';
88
+ },
89
+ };
90
+ export const providers = {
91
+ openai: openaiProvider,
92
+ anthropic: anthropicProvider,
93
+ gemini: geminiProvider,
94
+ openrouter: openrouterProvider,
95
+ };
96
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { Provider } from './types.js';
2
+ export declare const anthropicProvider: Provider;
3
+ //# sourceMappingURL=anthropic.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,iBAAiB,EAAE,QAiB/B,CAAC"}
@@ -0,0 +1,17 @@
1
+ import Anthropic from '@anthropic-ai/sdk';
2
+ export const anthropicProvider = {
3
+ name: 'anthropic',
4
+ async generate(prompt, config) {
5
+ const client = new Anthropic({ apiKey: config.apiKey });
6
+ const res = await client.messages.create({
7
+ model: config.model,
8
+ system: 'You are a git commit message generator. Respond only with the commit message, no extra text.',
9
+ messages: [{ role: 'user', content: prompt }],
10
+ temperature: 0.3,
11
+ max_tokens: config.maxTokens ?? 300,
12
+ });
13
+ const block = res.content[0];
14
+ return block?.type === 'text' ? block.text.trim() : '';
15
+ },
16
+ };
17
+ //# sourceMappingURL=anthropic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"anthropic.js","sourceRoot":"","sources":["../../src/providers/anthropic.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAG1C,MAAM,CAAC,MAAM,iBAAiB,GAAa;IACzC,IAAI,EAAE,WAAW;IAEjB,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,MAAsB;QACnD,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAExD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC;YACvC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,MAAM,EAAE,8FAA8F;YACtG,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,MAAM,CAAC,SAAS,IAAI,GAAG;SACpC,CAAC,CAAC;QAEH,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7B,OAAO,KAAK,EAAE,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;IACzD,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Provider } from './types.js';
2
+ export declare const geminiProvider: Provider;
3
+ //# sourceMappingURL=gemini.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.d.ts","sourceRoot":"","sources":["../../src/providers/gemini.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,cAAc,EAAE,QAoB5B,CAAC"}
@@ -0,0 +1,20 @@
1
+ import { GoogleGenerativeAI } from '@google/generative-ai';
2
+ export const geminiProvider = {
3
+ name: 'gemini',
4
+ async generate(prompt, config) {
5
+ const genAI = new GoogleGenerativeAI(config.apiKey);
6
+ const model = genAI.getGenerativeModel({
7
+ model: config.model,
8
+ systemInstruction: 'You are a git commit message generator. Respond only with the commit message, no extra text.',
9
+ });
10
+ const res = await model.generateContent({
11
+ contents: [{ role: 'user', parts: [{ text: prompt }] }],
12
+ generationConfig: {
13
+ temperature: 0.3,
14
+ maxOutputTokens: config.maxTokens ?? 300,
15
+ },
16
+ });
17
+ return res.response.text().trim();
18
+ },
19
+ };
20
+ //# sourceMappingURL=gemini.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gemini.js","sourceRoot":"","sources":["../../src/providers/gemini.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAG3D,MAAM,CAAC,MAAM,cAAc,GAAa;IACtC,IAAI,EAAE,QAAQ;IAEd,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,MAAsB;QACnD,MAAM,KAAK,GAAG,IAAI,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACpD,MAAM,KAAK,GAAG,KAAK,CAAC,kBAAkB,CAAC;YACrC,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,iBAAiB,EAAE,8FAA8F;SAClH,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,eAAe,CAAC;YACtC,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;YACvD,gBAAgB,EAAE;gBAChB,WAAW,EAAE,GAAG;gBAChB,eAAe,EAAE,MAAM,CAAC,SAAS,IAAI,GAAG;aACzC;SACF,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,CAAC;IACpC,CAAC;CACF,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { Provider } from './types.js';
2
+ export declare const providers: Record<string, Provider>;
3
+ export type { Provider, ProviderConfig } from './types.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAM3C,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,QAAQ,CAK9C,CAAC;AAEF,YAAY,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,11 @@
1
+ import { openaiProvider } from './openai.js';
2
+ import { anthropicProvider } from './anthropic.js';
3
+ import { geminiProvider } from './gemini.js';
4
+ import { openrouterProvider } from './openrouter.js';
5
+ export const providers = {
6
+ openai: openaiProvider,
7
+ anthropic: anthropicProvider,
8
+ gemini: geminiProvider,
9
+ openrouter: openrouterProvider,
10
+ };
11
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,MAAM,CAAC,MAAM,SAAS,GAA6B;IACjD,MAAM,EAAE,cAAc;IACtB,SAAS,EAAE,iBAAiB;IAC5B,MAAM,EAAE,cAAc;IACtB,UAAU,EAAE,kBAAkB;CAC/B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Provider } from './types.js';
2
+ export declare const openaiProvider: Provider;
3
+ //# sourceMappingURL=openai.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,cAAc,EAAE,QAkB5B,CAAC"}
@@ -0,0 +1,18 @@
1
+ import OpenAI from 'openai';
2
+ export const openaiProvider = {
3
+ name: 'openai',
4
+ async generate(prompt, config) {
5
+ const client = new OpenAI({ apiKey: config.apiKey });
6
+ const res = await client.chat.completions.create({
7
+ model: config.model,
8
+ messages: [
9
+ { role: 'system', content: 'You are a git commit message generator. Respond only with the commit message, no extra text.' },
10
+ { role: 'user', content: prompt },
11
+ ],
12
+ temperature: 0.3,
13
+ max_tokens: config.maxTokens ?? 300,
14
+ });
15
+ return res.choices[0]?.message?.content?.trim() ?? '';
16
+ },
17
+ };
18
+ //# sourceMappingURL=openai.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai.js","sourceRoot":"","sources":["../../src/providers/openai.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,CAAC,MAAM,cAAc,GAAa;IACtC,IAAI,EAAE,QAAQ;IAEd,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,MAAsB;QACnD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;QAErD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YAC/C,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,8FAA8F,EAAE;gBAC3H,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;aAClC;YACD,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,MAAM,CAAC,SAAS,IAAI,GAAG;SACpC,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;CACF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Provider } from './types.js';
2
+ export declare const openrouterProvider: Provider;
3
+ //# sourceMappingURL=openrouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter.d.ts","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAkB,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,kBAAkB,EAAE,QAqBhC,CAAC"}
@@ -0,0 +1,21 @@
1
+ import OpenAI from 'openai';
2
+ export const openrouterProvider = {
3
+ name: 'openrouter',
4
+ async generate(prompt, config) {
5
+ const client = new OpenAI({
6
+ apiKey: config.apiKey,
7
+ baseURL: 'https://openrouter.ai/api/v1',
8
+ });
9
+ const res = await client.chat.completions.create({
10
+ model: config.model,
11
+ messages: [
12
+ { role: 'system', content: 'You are a git commit message generator. Respond only with the commit message, no extra text.' },
13
+ { role: 'user', content: prompt },
14
+ ],
15
+ temperature: 0.3,
16
+ max_tokens: config.maxTokens ?? 300,
17
+ });
18
+ return res.choices[0]?.message?.content?.trim() ?? '';
19
+ },
20
+ };
21
+ //# sourceMappingURL=openrouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openrouter.js","sourceRoot":"","sources":["../../src/providers/openrouter.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,CAAC,MAAM,kBAAkB,GAAa;IAC1C,IAAI,EAAE,YAAY;IAElB,KAAK,CAAC,QAAQ,CAAC,MAAc,EAAE,MAAsB;QACnD,MAAM,MAAM,GAAG,IAAI,MAAM,CAAC;YACxB,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,8BAA8B;SACxC,CAAC,CAAC;QAEH,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YAC/C,KAAK,EAAE,MAAM,CAAC,KAAK;YACnB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,8FAA8F,EAAE;gBAC3H,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE;aAClC;YACD,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,MAAM,CAAC,SAAS,IAAI,GAAG;SACpC,CAAC,CAAC;QAEH,OAAO,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;IACxD,CAAC;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ export interface ProviderConfig {
2
+ apiKey: string;
3
+ model: string;
4
+ maxTokens?: number;
5
+ }
6
+ export interface Provider {
7
+ name: string;
8
+ generate(prompt: string, config: ProviderConfig): Promise<string>;
9
+ }
10
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CACnE"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/providers/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@shxd/mmit",
3
+ "version": "0.1.0",
4
+ "description": "AI-powered git commit message generator",
5
+ "bin": {
6
+ "mmit": "./bin/mmit"
7
+ },
8
+ "files": [
9
+ "dist",
10
+ "bin"
11
+ ],
12
+ "type": "module",
13
+ "dependencies": {
14
+ "@anthropic-ai/sdk": "^0.32.0",
15
+ "@clack/prompts": "^0.7.0",
16
+ "@google/generative-ai": "^0.21.0",
17
+ "commander": "^12.0.0",
18
+ "openai": "^4.73.0",
19
+ "picocolors": "^1.1.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^22.0.0",
23
+ "typescript": "^5.6.0"
24
+ },
25
+ "scripts": {
26
+ "build": "tsc",
27
+ "dev": "tsc --watch",
28
+ "start": "node bin/mmit"
29
+ }
30
+ }