diffscribe 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,161 @@
1
+ # diffscribe
2
+
3
+ AI-powered commit message generator for Conventional Commits.
4
+
5
+ ## Features
6
+
7
+ - ✅ Conventional Commits compliance
8
+ - ✅ Concise and detailed modes
9
+ - ✅ Preview before committing
10
+ - ✅ Automatic clipboard copy
11
+ - ✅ Regeneration loop
12
+ - ✅ Smart model selection (free draft + paid refinement for long diffs)
13
+ - ✅ Mock mode for testing
14
+ - ✅ Error handling with clear messages
15
+
16
+ ## How It Works
17
+
18
+ 1. Reads your staged Git diffs
19
+ 2. Generates a Conventional Commit message using AI
20
+ 3. Preview and accept/reject
21
+ 4. Copies to clipboard on accept
22
+ 5. Run `git commit` and paste message
23
+
24
+ ## Installation
25
+
26
+ ### Global Installation (Recommended)
27
+
28
+ ```bash
29
+ npm install -g diffscribe
30
+ ```
31
+
32
+ After installation, you can run the `dcs` command from anywhere:
33
+
34
+ ```bash
35
+ dcs # If installed globally
36
+ ```
37
+
38
+ ### Zero-install via npx
39
+
40
+ ```bash
41
+ npx diffscribe
42
+ ```
43
+
44
+ ## Setup
45
+
46
+ ### 1. Get OpenRouter API Key
47
+
48
+ Visit https://openrouter.ai/keys to get your API key.
49
+
50
+ ### 2. Set Environment Variable
51
+
52
+ ```bash
53
+ export OPENROUTER_API_KEY=your-api-key-here
54
+ ```
55
+
56
+ For permanent setup, add to your shell profile:
57
+ ```bash
58
+ echo 'export OPENROUTER_API_KEY=your-api-key-here' >> ~/.bashrc # or ~/.zshrc
59
+ source ~/.bashrc
60
+ ```
61
+
62
+ ### 3. Verify Setup
63
+
64
+ ```bash
65
+ echo $OPENROUTER_API_KEY
66
+ ```
67
+
68
+ ## Usage
69
+
70
+ ### Basic Usage
71
+
72
+ ```bash
73
+ # Stage your changes
74
+ git add .
75
+
76
+ # Generate commit message (if installed globally)
77
+ dcs
78
+
79
+ # Or using npx
80
+ npx diffscribe
81
+ ```
82
+
83
+ ### Mock Mode (Testing)
84
+
85
+ For testing without API calls:
86
+
87
+ ```bash
88
+ dcs --mock # If installed globally
89
+ npx diffscribe --mock # Using npx
90
+ ```
91
+
92
+ ## Model Strategy
93
+
94
+ The tool uses a smart two-stage approach for optimal cost and quality:
95
+
96
+ ### Draft Stage (Free Models)
97
+ - **Primary**: `mistralai/devstral-2512:free` — Fast free model for initial commit message
98
+ - **Backup**: `qwen/qwen3-coder:free` — Falls back if primary hits rate limits
99
+
100
+ ### Refinement Stage (Paid Model)
101
+ - **Refinement**: `google/gemini-2.5-flash-lite` — Polishes messages for large diffs (300+ lines or 12KB+)
102
+
103
+ ### When Does Refinement Run?
104
+ Only for larger changes to improve clarity and structure. Smaller diffs skip refinement to save cost.
105
+
106
+ ## Options
107
+
108
+ ```
109
+ ### dcs
110
+
111
+ Generate AI-powered commit messages for your staged changes.
112
+
113
+ ```bash
114
+ dcs [options]
115
+ ```
116
+
117
+ Options:
118
+ -V, --version output the version number
119
+ --mock Use mock generation instead of LLM (for testing)
120
+ -h, --help display help for command
121
+
122
+ ### Examples
123
+
124
+ ```bash
125
+ # Generate commit message with AI
126
+ dcs
127
+
128
+ # Test without API calls
129
+ dcs --mock
130
+
131
+ # Using npx (no installation needed)
132
+ npx diffscribe
133
+ npx diffscribe --mock
134
+ ```
135
+ ```
136
+
137
+ ## Requirements
138
+
139
+ - Node.js >= 20
140
+ - Git repository
141
+ - OpenRouter API key (unless using --mock mode)
142
+
143
+ ## Development
144
+
145
+ ```bash
146
+ # Install dependencies
147
+ npm install
148
+
149
+ # Build
150
+ npm run build
151
+
152
+ # Watch mode
153
+ npm run dev
154
+
155
+ # Type check
156
+ npm run typecheck
157
+ ```
158
+
159
+ ## License
160
+
161
+ MIT
@@ -0,0 +1,19 @@
1
+ export type CommitStyle = 'concise' | 'detailed';
2
+ export interface CommitMessage {
3
+ header: string;
4
+ body?: string;
5
+ }
6
+ export interface GitResult {
7
+ success: boolean;
8
+ diff?: string;
9
+ error?: string;
10
+ }
11
+ export interface OpenRouterConfig {
12
+ apiKey: string;
13
+ siteUrl?: string;
14
+ siteName?: string;
15
+ }
16
+ export declare const DRAFT_MODEL_PRIMARY = "google/gemini-2.5-flash-lite";
17
+ export declare const DRAFT_MODEL_BACKUP = "mistralai/devstral-2512";
18
+ export declare const REFINEMENT_MODEL = "google/gemini-2.5-flash";
19
+ export declare function getOpenRouterConfig(): OpenRouterConfig;
package/dist/config.js ADDED
@@ -0,0 +1,23 @@
1
+ import { config } from 'dotenv';
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ const __filename = fileURLToPath(import.meta.url);
5
+ const __dirname = dirname(__filename);
6
+ config({ path: join(__dirname, '../.env') });
7
+ export const DRAFT_MODEL_PRIMARY = 'google/gemini-2.5-flash-lite';
8
+ export const DRAFT_MODEL_BACKUP = 'mistralai/devstral-2512';
9
+ export const REFINEMENT_MODEL = 'google/gemini-2.5-flash';
10
+ export function getOpenRouterConfig() {
11
+ const apiKey = process.env.OPENROUTER_API_KEY;
12
+ if (!apiKey) {
13
+ throw new Error('OPENROUTER_API_KEY environment variable is not set.\n' +
14
+ 'Get your API key from: https://openrouter.ai/keys\n' +
15
+ 'Set it with: export OPENROUTER_API_KEY=your-key-here');
16
+ }
17
+ return {
18
+ apiKey,
19
+ siteUrl: 'https://github.com/yourusername/diffscribe',
20
+ siteName: 'diffscribe'
21
+ };
22
+ }
23
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAChC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAErC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;AAEtC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;AAqB7C,MAAM,CAAC,MAAM,mBAAmB,GAAG,8BAA8B,CAAC;AAElE,MAAM,CAAC,MAAM,kBAAkB,GAAG,yBAAyB,CAAC;AAE5D,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAC;AAE1D,MAAM,UAAU,mBAAmB;IACjC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAE9C,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,uDAAuD;YACvD,qDAAqD;YACrD,sDAAsD,CACvD,CAAC;IACJ,CAAC;IAED,OAAO;QACL,MAAM;QACN,OAAO,EAAE,4CAA4C;QACrD,QAAQ,EAAE,YAAY;KACvB,CAAC;AACJ,CAAC"}
package/dist/git.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { GitResult } from './config.js';
2
+ export declare class GitHelper {
3
+ private cwd?;
4
+ constructor(cwd?: string);
5
+ isInGitRepo(): Promise<boolean>;
6
+ getStagedDiff(): Promise<GitResult>;
7
+ hasStagedChanges(): Promise<boolean>;
8
+ }
package/dist/git.js ADDED
@@ -0,0 +1,56 @@
1
+ // Git operations and repository management
2
+ import { execa } from 'execa';
3
+ export class GitHelper {
4
+ cwd;
5
+ constructor(cwd) {
6
+ this.cwd = cwd;
7
+ }
8
+ async isInGitRepo() {
9
+ try {
10
+ await execa('git', ['rev-parse', '--is-inside-work-tree'], {
11
+ cwd: this.cwd,
12
+ stdio: 'ignore'
13
+ });
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ async getStagedDiff() {
21
+ try {
22
+ const { stdout } = await execa('git', ['diff', '--cached'], {
23
+ cwd: this.cwd,
24
+ maxBuffer: 10 * 1024 * 1024
25
+ });
26
+ if (!stdout.trim()) {
27
+ return {
28
+ success: false,
29
+ error: 'No staged changes found'
30
+ };
31
+ }
32
+ return {
33
+ success: true,
34
+ diff: stdout
35
+ };
36
+ }
37
+ catch (error) {
38
+ return {
39
+ success: false,
40
+ error: error.message || 'Failed to get staged diff'
41
+ };
42
+ }
43
+ }
44
+ async hasStagedChanges() {
45
+ try {
46
+ await execa('git', ['diff', '--cached', '--quiet', '--exit-code'], {
47
+ cwd: this.cwd
48
+ });
49
+ return false;
50
+ }
51
+ catch (error) {
52
+ return error.exitCode === 1;
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../src/git.ts"],"names":[],"mappings":"AAAA,2CAA2C;AAC3C,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAG9B,MAAM,OAAO,SAAS;IACZ,GAAG,CAAU;IAErB,YAAY,GAAY;QACtB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,uBAAuB,CAAC,EAAE;gBACzD,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,KAAK,EAAE,QAAQ;aAChB,CAAC,CAAC;YACH,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa;QACjB,IAAI,CAAC;YACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,CAAC,EAAE;gBAC1D,GAAG,EAAE,IAAI,CAAC,GAAG;gBACb,SAAS,EAAE,EAAE,GAAG,IAAI,GAAG,IAAI;aAC5B,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC;gBACnB,OAAO;oBACL,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,yBAAyB;iBACjC,CAAC;YACJ,CAAC;YAED,OAAO;gBACL,OAAO,EAAE,IAAI;gBACb,IAAI,EAAE,MAAM;aACb,CAAC;QACJ,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,KAAK,EAAE,KAAK,CAAC,OAAO,IAAI,2BAA2B;aACpD,CAAC;QACJ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB;QACpB,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,CAAC,EAAE;gBACjE,GAAG,EAAE,IAAI,CAAC,GAAG;aACd,CAAC,CAAC;YACH,OAAO,KAAK,CAAC;QACf,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,KAAK,CAAC,QAAQ,KAAK,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,121 @@
1
+ #!/usr/bin/env node
2
+ import chalk from 'chalk';
3
+ import { Command } from 'commander';
4
+ import { GitHelper } from './git.js';
5
+ import { CommitService } from './service.js';
6
+ import { promptCommitStyle, promptAction, promptContinueRegeneration } from './ui.js';
7
+ import { getOpenRouterConfig, DRAFT_MODEL_PRIMARY, DRAFT_MODEL_BACKUP, REFINEMENT_MODEL } from './config.js';
8
+ const program = new Command()
9
+ .name('diffscribe')
10
+ .description('AI-powered commit message generator')
11
+ .version('1.0.0')
12
+ .option('--mock', 'Use mock generation instead of LLM (for testing)');
13
+ async function generateMockCommitMessage(style, diff) {
14
+ const hasTest = diff.includes('test') || diff.includes('spec');
15
+ const hasFix = diff.includes('fix') || diff.includes('bug');
16
+ const hasFeature = diff.includes('add') || diff.includes('new');
17
+ let type = 'chore';
18
+ let description = 'update code';
19
+ if (hasTest) {
20
+ type = 'test';
21
+ description = 'add test coverage';
22
+ }
23
+ else if (hasFix) {
24
+ type = 'fix';
25
+ description = 'fix bugs';
26
+ }
27
+ else if (hasFeature) {
28
+ type = 'feat';
29
+ description = 'add new feature';
30
+ }
31
+ const header = `${type}(): ${description}`;
32
+ if (style === 'detailed') {
33
+ return {
34
+ header,
35
+ body: '- update implementation\n- improve code quality'
36
+ };
37
+ }
38
+ return { header };
39
+ }
40
+ async function main() {
41
+ const options = program.opts();
42
+ const useMock = options.mock;
43
+ console.log(chalk.cyan.bold('diffscribe — AI-powered commit message generator\n'));
44
+ console.log(chalk.blue('ℹ Starting...\n'));
45
+ if (!useMock) {
46
+ try {
47
+ getOpenRouterConfig();
48
+ console.log(chalk.dim(`Draft model: ${DRAFT_MODEL_PRIMARY} (backup: ${DRAFT_MODEL_BACKUP})`));
49
+ console.log(chalk.dim(`Refinement model: ${REFINEMENT_MODEL}\n`));
50
+ }
51
+ catch (error) {
52
+ console.error(`${chalk.bold.red('✖')} ${error.message}`);
53
+ console.log(chalk.dim('To use mock mode for testing, run: diffscribe --mock'));
54
+ process.exit(1);
55
+ }
56
+ }
57
+ else {
58
+ console.log(chalk.dim('Running in mock mode (no API calls)\n'));
59
+ }
60
+ const git = new GitHelper();
61
+ if (!(await git.isInGitRepo())) {
62
+ console.error(`${chalk.bold.red('✖')} Not a Git repository.`);
63
+ console.log(chalk.dim('Run diffscribe inside a Git project.'));
64
+ process.exit(1);
65
+ }
66
+ const diffResult = await git.getStagedDiff();
67
+ if (!diffResult.success || !diffResult.diff) {
68
+ console.error(`${chalk.bold.red('✖')} ${diffResult.error || 'No staged changes found.'}`);
69
+ console.log(chalk.dim('Stage files using `git add` before running diffscribe.'));
70
+ process.exit(1);
71
+ }
72
+ console.log(`${chalk.bold.green('✓')} Found staged changes (${diffResult.diff.split('\n').length} lines)`);
73
+ const style = await promptCommitStyle();
74
+ console.log(chalk.blue(`ℹ Selected style: ${style}\n`));
75
+ let shouldContinue = true;
76
+ let attempt = 0;
77
+ const service = new CommitService();
78
+ while (shouldContinue) {
79
+ attempt++;
80
+ console.log(chalk.dim(`Generating commit message (attempt ${attempt})...`));
81
+ let message;
82
+ if (useMock) {
83
+ message = await generateMockCommitMessage(style, diffResult.diff);
84
+ service.displayCommitMessage(message);
85
+ }
86
+ else {
87
+ const result = await service.generateCommitMessageWithLLM(diffResult.diff, style);
88
+ if (!result.success || !result.message) {
89
+ console.error(`${chalk.bold.red('✖')} ${result.error || 'Failed to generate commit message'}`);
90
+ process.exit(1);
91
+ }
92
+ message = service.parseCommitMessage(result.message);
93
+ service.displayCommitMessage(message, result.model);
94
+ }
95
+ const action = await promptAction();
96
+ if (action === 'accept') {
97
+ await service.copyCommitMessage(message);
98
+ console.log(chalk.blue('ℹ Run `git commit` and paste the message.'));
99
+ process.exit(0);
100
+ }
101
+ else if (action === 'reject') {
102
+ console.log(chalk.blue('ℹ Cancelled.'));
103
+ process.exit(0);
104
+ }
105
+ else if (action === 'regenerate') {
106
+ shouldContinue = await promptContinueRegeneration();
107
+ if (!shouldContinue) {
108
+ console.log(chalk.blue('ℹ Cancelled.'));
109
+ process.exit(0);
110
+ }
111
+ }
112
+ }
113
+ }
114
+ program.action(() => {
115
+ main().catch((error) => {
116
+ console.error(`${chalk.bold.red('✖')} ${error.message}`);
117
+ process.exit(1);
118
+ });
119
+ });
120
+ program.parse(process.argv);
121
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AACtF,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,gBAAgB,EAA8B,MAAM,aAAa,CAAC;AAEzI,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,YAAY,CAAC;KAClB,WAAW,CAAC,qCAAqC,CAAC;KAClD,OAAO,CAAC,OAAO,CAAC;KAChB,MAAM,CAAC,QAAQ,EAAE,kDAAkD,CAAC,CAAC;AAExE,KAAK,UAAU,yBAAyB,CAAC,KAAkB,EAAE,IAAY;IACvE,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC/D,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAEhE,IAAI,IAAI,GAAG,OAAO,CAAC;IACnB,IAAI,WAAW,GAAG,aAAa,CAAC;IAEhC,IAAI,OAAO,EAAE,CAAC;QACZ,IAAI,GAAG,MAAM,CAAC;QACd,WAAW,GAAG,mBAAmB,CAAC;IACpC,CAAC;SAAM,IAAI,MAAM,EAAE,CAAC;QAClB,IAAI,GAAG,KAAK,CAAC;QACb,WAAW,GAAG,UAAU,CAAC;IAC3B,CAAC;SAAM,IAAI,UAAU,EAAE,CAAC;QACtB,IAAI,GAAG,MAAM,CAAC;QACd,WAAW,GAAG,iBAAiB,CAAC;IAClC,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,IAAI,OAAO,WAAW,EAAE,CAAC;IAE3C,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;QACzB,OAAO;YACL,MAAM;YACN,IAAI,EAAE,iDAAiD;SACxD,CAAC;IACJ,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAE7B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,oDAAoD,CAAC,CAAC,CAAC;IACnF,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAE3C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,IAAI,CAAC;YACH,mBAAmB,EAAE,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,mBAAmB,aAAa,kBAAkB,GAAG,CAAC,CAAC,CAAC;YAC9F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,qBAAqB,gBAAgB,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAU,EAAE,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sDAAsD,CAAC,CAAC,CAAC;YAC/E,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,uCAAuC,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,SAAS,EAAE,CAAC;IAE5B,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QAC9D,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sCAAsC,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,UAAU,GAAG,MAAM,GAAG,CAAC,aAAa,EAAE,CAAC;IAE7C,IAAI,CAAC,UAAU,CAAC,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;QAC5C,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,UAAU,CAAC,KAAK,IAAI,0BAA0B,EAAE,CAAC,CAAC;QAC1F,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,wDAAwD,CAAC,CAAC,CAAC;QACjF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,0BAA0B,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,SAAS,CAAC,CAAC;IAE3G,MAAM,KAAK,GAAG,MAAM,iBAAiB,EAAE,CAAC;IACxC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,qBAAqB,KAAK,IAAI,CAAC,CAAC,CAAC;IAExD,IAAI,cAAc,GAAG,IAAI,CAAC;IAC1B,IAAI,OAAO,GAAG,CAAC,CAAC;IAEhB,MAAM,OAAO,GAAG,IAAI,aAAa,EAAE,CAAC;IAEpC,OAAO,cAAc,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;QACV,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,sCAAsC,OAAO,MAAM,CAAC,CAAC,CAAC;QAE5E,IAAI,OAAsB,CAAC;QAE3B,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,GAAG,MAAM,yBAAyB,CAAC,KAAK,EAAE,UAAU,CAAC,IAAI,CAAC,CAAC;YAClE,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QACxC,CAAC;aAAM,CAAC;YACN,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,4BAA4B,CAAC,UAAU,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;YAElF,IAAI,CAAC,MAAM,CAAC,OAAO,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACvC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,MAAM,CAAC,KAAK,IAAI,mCAAmC,EAAE,CAAC,CAAC;gBAC/F,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;YAED,OAAO,GAAG,OAAO,CAAC,kBAAkB,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACrD,OAAO,CAAC,oBAAoB,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,EAAE,CAAC;QAEpC,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YACxB,MAAM,OAAO,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;YACzC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC,CAAC;YACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;aAAM,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;YACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;aAAM,IAAI,MAAM,KAAK,YAAY,EAAE,CAAC;YACnC,cAAc,GAAG,MAAM,0BAA0B,EAAE,CAAC;YACpD,IAAI,CAAC,cAAc,EAAE,CAAC;gBACpB,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,CAAC;gBACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,OAAO,CAAC,MAAM,CAAC,GAAG,EAAE;IAClB,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE;QACrB,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACzD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { CommitStyle } from './config.js';
2
+ export declare function promptCommitStyle(): Promise<CommitStyle>;
3
+ export declare function promptAction(): Promise<'accept' | 'regenerate' | 'reject'>;
4
+ export declare function promptContinueRegeneration(): Promise<boolean>;
@@ -0,0 +1,29 @@
1
+ import { select, confirm } from '@inquirer/prompts';
2
+ export async function promptCommitStyle() {
3
+ const choices = [
4
+ { name: 'Concise — single-line, clean history', value: 'concise' },
5
+ { name: 'Detailed — header + body with context', value: 'detailed' }
6
+ ];
7
+ return await select({
8
+ message: 'Choose commit message style:',
9
+ choices
10
+ });
11
+ }
12
+ export async function promptAction() {
13
+ const choices = [
14
+ { name: 'Accept & copy to clipboard', value: 'accept' },
15
+ { name: 'Regenerate', value: 'regenerate' },
16
+ { name: 'Reject & exit', value: 'reject' }
17
+ ];
18
+ return await select({
19
+ message: 'What would you like to do?',
20
+ choices
21
+ });
22
+ }
23
+ export async function promptContinueRegeneration() {
24
+ return await confirm({
25
+ message: 'Continue regenerating?',
26
+ default: true
27
+ });
28
+ }
29
+ //# sourceMappingURL=prompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompts.js","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,uCAAuC,EAAE,KAAK,EAAE,SAAwB,EAAE;QAClF,EAAE,IAAI,EAAE,uCAAuC,EAAE,KAAK,EAAE,UAAyB,EAAE;KACpF,CAAC;IAEF,OAAO,MAAM,MAAM,CAAC;QAClB,OAAO,EAAE,8BAA8B;QACvC,OAAO;KACR,CAAgB,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,4BAA4B,EAAE,KAAK,EAAE,QAAQ,EAAE;QACvD,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE;QAC3C,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE;KAC3C,CAAC;IAEF,OAAO,MAAM,MAAM,CAAC;QAClB,OAAO,EAAE,4BAA4B;QACrC,OAAO;KACR,CAAuC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAC9C,OAAO,MAAM,OAAO,CAAC;QACnB,OAAO,EAAE,wBAAwB;QACjC,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { CommitMessage } from './config.js';
2
+ export interface CommitMessageGenerationResult {
3
+ success: boolean;
4
+ message?: string;
5
+ error?: string;
6
+ model?: string;
7
+ }
8
+ export declare class CommitService {
9
+ generateCommitMessageWithLLM(diff: string, style: 'concise' | 'detailed'): Promise<CommitMessageGenerationResult>;
10
+ parseCommitMessage(llmOutput: string): CommitMessage;
11
+ displayCommitMessage(message: CommitMessage, model?: string): void;
12
+ copyCommitMessage(message: CommitMessage): Promise<void>;
13
+ }
@@ -0,0 +1,180 @@
1
+ import chalk from 'chalk';
2
+ import clipboardy from 'clipboardy';
3
+ import { OpenRouter } from '@openrouter/sdk';
4
+ import { getOpenRouterConfig, DRAFT_MODEL_PRIMARY, DRAFT_MODEL_BACKUP, REFINEMENT_MODEL } from './config.js';
5
+ const output = {
6
+ error: (message) => {
7
+ console.error(`${chalk.bold.red('✖')} ${message}`);
8
+ },
9
+ success: (message) => {
10
+ console.log(`${chalk.bold.green('✓')} ${message}`);
11
+ },
12
+ info: (message) => {
13
+ console.log(`${chalk.blue('ℹ')} ${message}`);
14
+ },
15
+ warning: (message) => {
16
+ console.log(`${chalk.yellow('⚠')} ${message}`);
17
+ },
18
+ dim: (message) => {
19
+ console.log(chalk.dim(message));
20
+ },
21
+ divider: () => {
22
+ console.log(chalk.dim('─'.repeat(50)));
23
+ },
24
+ header: (title) => {
25
+ console.log(`\n${chalk.bold.cyan(title)}\n`);
26
+ }
27
+ };
28
+ function calculateDiffStats(diff) {
29
+ const lines = diff.split('\n').length;
30
+ const chars = diff.length;
31
+ return { lines, chars };
32
+ }
33
+ function isLongDiff(stats) {
34
+ return stats.lines > 300 || stats.chars > 12000;
35
+ }
36
+ async function callModel(model, systemPrompt, userPrompt, maxTokens = 500) {
37
+ try {
38
+ const config = getOpenRouterConfig();
39
+ const openRouter = new OpenRouter({
40
+ apiKey: config.apiKey
41
+ });
42
+ const result = openRouter.callModel({
43
+ model,
44
+ instructions: systemPrompt,
45
+ input: [
46
+ {
47
+ role: 'user',
48
+ content: userPrompt
49
+ }
50
+ ],
51
+ temperature: 0.1,
52
+ maxOutputTokens: maxTokens
53
+ });
54
+ const message = await result.getText();
55
+ if (!message || message.trim() === '') {
56
+ return {
57
+ success: false,
58
+ error: 'LLM returned empty message'
59
+ };
60
+ }
61
+ return {
62
+ success: true,
63
+ message: message.trim(),
64
+ model
65
+ };
66
+ }
67
+ catch (error) {
68
+ if (error.statusCode === 401) {
69
+ return {
70
+ success: false,
71
+ error: 'Invalid API key. Please check your OPENROUTER_API_KEY environment variable.'
72
+ };
73
+ }
74
+ if (error.statusCode === 429) {
75
+ return {
76
+ success: false,
77
+ error: 'Rate limit exceeded. Please wait and try again.'
78
+ };
79
+ }
80
+ return {
81
+ success: false,
82
+ error: error.message || 'Failed to generate commit message'
83
+ };
84
+ }
85
+ }
86
+ async function generateCommitMessage(diff, style, useFallback) {
87
+ const styleInstruction = style === 'concise'
88
+ ? 'Return ONLY a single-line commit message in Conventional Commits format: <type>(<scope>): <description>'
89
+ : 'Return a commit message in Conventional Commits format with:\n- Header: <type>(<scope>): <description>\n- Body: 2-3 bullet points explaining what was changed and why';
90
+ const systemPrompt = `You are an expert developer who writes perfect Conventional Commits.
91
+ Your task is to analyze git diffs and generate clear, meaningful commit messages.
92
+
93
+ Rules:
94
+ - Use valid Conventional Commit types: feat, fix, docs, style, refactor, test, chore, perf
95
+ - Scope should be the affected module or component (e.g., auth, api, ui, db)
96
+ - Description must be in imperative mood (e.g., "add" not "added" or "adds")
97
+ - Keep header under 72 characters
98
+ - No period at the end of header
99
+ - Focus on WHAT and WHY, not HOW`;
100
+ const userPrompt = `${styleInstruction}\n\nGit Diff:\n\`\`\`diff\n${diff}\n\`\`\``;
101
+ const result = await callModel(DRAFT_MODEL_PRIMARY, systemPrompt, userPrompt);
102
+ if (result.success) {
103
+ return result;
104
+ }
105
+ if (useFallback) {
106
+ const backupResult = await callModel(DRAFT_MODEL_BACKUP, systemPrompt, userPrompt);
107
+ return backupResult;
108
+ }
109
+ return result;
110
+ }
111
+ async function refineCommitMessage(draftMessage) {
112
+ const systemPrompt = `You refine git commit messages.
113
+ Improve clarity, grammar, and structure.
114
+ Do NOT invent new changes or alter meaning.
115
+ Maintain Conventional Commits format.`;
116
+ const userPrompt = `Refine this commit message:\n\n${draftMessage}\n\nReturn the final commit message.`;
117
+ return await callModel(REFINEMENT_MODEL, systemPrompt, userPrompt, 300);
118
+ }
119
+ function parseCommitMessage(llmOutput) {
120
+ const lines = llmOutput.split('\n').filter(line => line.trim() !== '');
121
+ if (lines.length === 0) {
122
+ return { header: 'chore: update code' };
123
+ }
124
+ const header = lines[0].trim();
125
+ if (lines.length === 1) {
126
+ return { header };
127
+ }
128
+ const bodyLines = lines.slice(1).join('\n');
129
+ return { header, body: bodyLines };
130
+ }
131
+ async function copyToClipboard(text) {
132
+ try {
133
+ await clipboardy.write(text);
134
+ output.success('Commit message copied to clipboard!');
135
+ }
136
+ catch (error) {
137
+ output.error('Failed to copy to clipboard');
138
+ throw error;
139
+ }
140
+ }
141
+ export class CommitService {
142
+ async generateCommitMessageWithLLM(diff, style) {
143
+ const stats = calculateDiffStats(diff);
144
+ const isLong = isLongDiff(stats);
145
+ if (!isLong) {
146
+ const result = await generateCommitMessage(diff, style, false);
147
+ return result;
148
+ }
149
+ const draftResult = await generateCommitMessage(diff, style, true);
150
+ if (!draftResult.success || !draftResult.message) {
151
+ return draftResult;
152
+ }
153
+ const refinedResult = await refineCommitMessage(draftResult.message);
154
+ if (refinedResult.success && refinedResult.message) {
155
+ return refinedResult;
156
+ }
157
+ return draftResult;
158
+ }
159
+ parseCommitMessage(llmOutput) {
160
+ return parseCommitMessage(llmOutput);
161
+ }
162
+ displayCommitMessage(message, model) {
163
+ output.header('Generated Commit Message');
164
+ if (model) {
165
+ output.dim(`Model: ${model}`);
166
+ console.log();
167
+ }
168
+ console.log(chalk.cyan(message.header));
169
+ if (message.body) {
170
+ console.log();
171
+ console.log(chalk.dim(message.body));
172
+ }
173
+ output.divider();
174
+ }
175
+ async copyCommitMessage(message) {
176
+ const fullMessage = message.body ? `${message.header}\n\n${message.body}` : message.header;
177
+ await copyToClipboard(fullMessage);
178
+ }
179
+ }
180
+ //# sourceMappingURL=service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"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"}
package/dist/test.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/test.js ADDED
@@ -0,0 +1,3 @@
1
+ console.log("this is a test file");
2
+ export {};
3
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../src/test.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAA"}
package/dist/ui.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import type { CommitStyle } from './config.js';
2
+ export declare function promptCommitStyle(): Promise<CommitStyle>;
3
+ export declare function promptAction(): Promise<'accept' | 'regenerate' | 'reject'>;
4
+ export declare function promptContinueRegeneration(): Promise<boolean>;
package/dist/ui.js ADDED
@@ -0,0 +1,30 @@
1
+ // User interaction prompts and CLI flow
2
+ import { select, confirm } from '@inquirer/prompts';
3
+ export async function promptCommitStyle() {
4
+ const choices = [
5
+ { name: 'Concise — single-line, clean history', value: 'concise' },
6
+ { name: 'Detailed — header + body with context', value: 'detailed' }
7
+ ];
8
+ return await select({
9
+ message: 'Choose commit message style:',
10
+ choices
11
+ });
12
+ }
13
+ export async function promptAction() {
14
+ const choices = [
15
+ { name: 'Accept & copy to clipboard', value: 'accept' },
16
+ { name: 'Regenerate', value: 'regenerate' },
17
+ { name: 'Reject & exit', value: 'reject' }
18
+ ];
19
+ return await select({
20
+ message: 'What would you like to do?',
21
+ choices
22
+ });
23
+ }
24
+ export async function promptContinueRegeneration() {
25
+ return await confirm({
26
+ message: 'Continue regenerating?',
27
+ default: true
28
+ });
29
+ }
30
+ //# sourceMappingURL=ui.js.map
package/dist/ui.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.js","sourceRoot":"","sources":["../src/ui.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAGpD,MAAM,CAAC,KAAK,UAAU,iBAAiB;IACrC,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,uCAAuC,EAAE,KAAK,EAAE,SAAwB,EAAE;QAClF,EAAE,IAAI,EAAE,uCAAuC,EAAE,KAAK,EAAE,UAAyB,EAAE;KACpF,CAAC;IAEF,OAAO,MAAM,MAAM,CAAC;QAClB,OAAO,EAAE,8BAA8B;QACvC,OAAO;KACR,CAAgB,CAAC;AACpB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY;IAChC,MAAM,OAAO,GAAG;QACd,EAAE,IAAI,EAAE,4BAA4B,EAAE,KAAK,EAAE,QAAQ,EAAE;QACvD,EAAE,IAAI,EAAE,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE;QAC3C,EAAE,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,QAAQ,EAAE;KAC3C,CAAC;IAEF,OAAO,MAAM,MAAM,CAAC;QAClB,OAAO,EAAE,4BAA4B;QACrC,OAAO;KACR,CAAuC,CAAC;AAC3C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,0BAA0B;IAC9C,OAAO,MAAM,OAAO,CAAC;QACnB,OAAO,EAAE,wBAAwB;QACjC,OAAO,EAAE,IAAI;KACd,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "diffscribe",
3
+ "version": "1.0.1",
4
+ "description": "AI-powered commit message generator for Conventional Commits",
5
+ "type": "module",
6
+ "bin": {
7
+ "dcs": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": "./dist/index.js",
13
+ "main": "./dist/index.js",
14
+ "scripts": {
15
+ "build": "tsc && shx chmod +x dist/index.js",
16
+ "dev": "tsc --watch",
17
+ "typecheck": "tsc --noEmit",
18
+ "prepare": "npm run build"
19
+ },
20
+ "engines": {
21
+ "node": ">=20"
22
+ },
23
+ "dependencies": {
24
+ "@inquirer/prompts": "^7.2.0",
25
+ "@openrouter/sdk": "^0.5.1",
26
+ "chalk": "^5.4.1",
27
+ "clipboardy": "^4.0.0",
28
+ "commander": "^12.1.0",
29
+ "dotenv": "^17.2.3",
30
+ "execa": "^9.5.2"
31
+ },
32
+ "devDependencies": {
33
+ "@types/node": "^22.10.2",
34
+ "shx": "^0.3.4",
35
+ "typescript": "^5.7.2"
36
+ },
37
+ "keywords": [
38
+ "git",
39
+ "commit",
40
+ "conventional-commits",
41
+ "cli",
42
+ "ai",
43
+ "diffscribe",
44
+ "dcs"
45
+ ],
46
+ "preferGlobal": true,
47
+ "publishConfig": {
48
+ "access": "public"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "https://github.com/raghavvvgaba/diffscribe.git"
53
+ },
54
+ "author": "Raghav Gaba <raghav@example.com>",
55
+ "bugs": {
56
+ "url": "https://github.com/raghavvvgaba/diffscribe/issues"
57
+ },
58
+ "homepage": "https://github.com/raghavvvgaba/diffscribe#readme",
59
+ "license": "MIT"
60
+ }