@johnowennixon/diffdash 1.4.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,16 +2,19 @@
2
2
 
3
3
  A command-line tool to generate Git commit messages using AI.
4
4
 
5
+ ## Demonstration
6
+
5
7
  ![Demonstration](asciinema/diffdash-demo.gif)
6
8
 
7
9
  ## Features
8
10
 
9
- * Generates Git commit messages in natural English
10
- * Adds a footer to the generated commit messages
11
- * Options to disable or auto-approve various stages of the process
12
- * Able to add a prefix or suffix to the summary line
13
- * Optionally select from a choice of LLM models
11
+ * Generate Git commit messages in natural English
12
+ * Add a footer to the generated commit messages
13
+ * Add a prefix or suffix to the summary line
14
+ * Select from a choice of LLM models
14
15
  * Compare messages generated from all configured models
16
+ * Disable or auto-approve various stages
17
+ * Just output the commit message for use in scripts
15
18
  * Configuration using standard API provider environment variables
16
19
  * Uses the Vercel AI SDK
17
20
  * Uses structured JSON with compatible models
@@ -59,12 +62,6 @@ export GEMINI_API_KEY=your-api-key
59
62
  # Basic usage (uses defaults)
60
63
  diffdash
61
64
 
62
- # Add a prefix to the commit message summary line
63
- diffdash --add-prefix "[FIX]"
64
-
65
- # Add a suffix to the commit message summary line
66
- diffdash --add-suffix "(closes #123)"
67
-
68
65
  # Automatically stage all changes, but still prompt for commit and push
69
66
  diffdash --auto-add
70
67
 
@@ -92,6 +89,12 @@ diffdash --disable-push
92
89
  # Skip git hooks when pushing
93
90
  diffdash --no-verify
94
91
 
92
+ # Add a prefix to the commit message summary line
93
+ diffdash --add-prefix "[FIX]"
94
+
95
+ # Add a suffix to the commit message summary line
96
+ diffdash --add-suffix "(closes #123)"
97
+
95
98
  # Display commit messages generated by all models
96
99
  diffdash --llm-compare
97
100
 
@@ -101,6 +104,9 @@ diffdash --llm-fallback
101
104
  # Specify the LLM model by name
102
105
  diffdash --llm-model claude-3.5-haiku
103
106
 
107
+ # Just output the commit message for use in scripts
108
+ diffdash --just-output
109
+
104
110
  # Debug options
105
111
  diffdash --debug-llm-inputs --debug-llm-outputs
106
112
  ```
@@ -113,8 +119,6 @@ All command-line arguments are optional.
113
119
  |--------|-------------|
114
120
  | `--help` | show a help message and exit |
115
121
  | `--version` | show program version information and exit |
116
- | `--add-prefix PREFIX` | add a prefix to the commit message summary line |
117
- | `--add-suffix SUFFIX` | add a suffix to the commit message summary line |
118
122
  | `--auto-add` | automatically stage all changes without confirmation |
119
123
  | `--auto-commit` | automatically commit changes without confirmation |
120
124
  | `--auto-push` | automatically push changes after commit without confirmation |
@@ -125,12 +129,15 @@ All command-line arguments are optional.
125
129
  | `--disable-push` | disable pushing changes - exit after making the commit |
126
130
  | `--push-no-verify` | bypass git hooks when pushing to Git |
127
131
  | `--push-force` | apply force when pushing to Git |
132
+ | `--add-prefix PREFIX` | add a prefix to the commit message summary line |
133
+ | `--add-suffix SUFFIX` | add a suffix to the commit message summary line |
128
134
  | `--llm-list` | display a list of available Large Language Models and exit |
129
135
  | `--llm-compare` | compare the generated messages from all models - but do not commit |
130
136
  | `--llm-router` | prefer to access the LLM via a router rather than direct |
131
137
  | `--llm-fallback` | use the fallback LLM model instead of the default |
132
138
  | `--llm-model MODEL` | choose the LLM model by name (the default is normally best) |
133
139
  | `--llm-excludes MODELS` | models to exclude from comparison (comma separated) |
140
+ | `--just-output` | just output the commit message for use in scripts |
134
141
  | `--silent` | suppress all normal output - errors and aborts still display |
135
142
  | `--debug-llm-inputs` | show inputs (including all prompts) sent to the LLM |
136
143
  | `--debug-llm-outputs` | show outputs received from the LLM |
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnowennixon/diffdash",
3
- "version": "1.4.1",
3
+ "version": "1.5.0",
4
4
  "description": "A command-line tool to generate Git commit messages using AI",
5
5
  "license": "0BSD",
6
6
  "author": "John Owen Nixon",
@@ -40,30 +40,30 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@ai-sdk/anthropic": "1.2.12",
43
- "@ai-sdk/deepseek": "0.2.14",
44
- "@ai-sdk/google": "1.2.21",
45
- "@ai-sdk/openai": "1.3.22",
43
+ "@ai-sdk/deepseek": "0.2.16",
44
+ "@ai-sdk/google": "1.2.22",
45
+ "@ai-sdk/openai": "1.3.23",
46
46
  "@openrouter/ai-sdk-provider": "0.7.2",
47
47
  "@requesty/ai-sdk": "0.0.9",
48
- "ai": "4.3.16",
48
+ "ai": "4.3.19",
49
49
  "ansis": "4.1.0",
50
50
  "argparse": "2.0.1",
51
51
  "cli-table3": "0.6.5",
52
52
  "simple-git": "3.28.0",
53
- "zod": "3.25.67"
53
+ "zod": "3.25.76"
54
54
  },
55
55
  "devDependencies": {
56
- "@biomejs/biome": "2.0.6",
56
+ "@biomejs/biome": "2.1.1",
57
57
  "@eslint/eslintrc": "3.3.1",
58
- "@eslint/js": "9.30.0",
58
+ "@eslint/js": "9.31.0",
59
59
  "@johnowennixon/add-shebangs": "1.1.0",
60
60
  "@johnowennixon/chmodx": "2.0.0",
61
- "@stylistic/eslint-plugin": "5.1.0",
61
+ "@stylistic/eslint-plugin": "5.2.0",
62
62
  "@types/argparse": "2.0.17",
63
- "@types/node": "24.0.8",
64
- "@typescript-eslint/eslint-plugin": "8.35.1",
65
- "@typescript-eslint/parser": "8.35.1",
66
- "eslint": "9.30.0",
63
+ "@types/node": "24.0.14",
64
+ "@typescript-eslint/eslint-plugin": "8.37.0",
65
+ "@typescript-eslint/parser": "8.37.0",
66
+ "eslint": "9.31.0",
67
67
  "eslint-import-resolver-typescript": "4.4.4",
68
68
  "eslint-plugin-import-x": "4.16.1",
69
69
  "eslint-plugin-sonarjs": "3.0.4",
@@ -72,9 +72,9 @@
72
72
  "knip": "5.61.3",
73
73
  "markdownlint-cli2": "0.18.1",
74
74
  "npm-run-all2": "8.0.4",
75
- "oxlint": "1.4.0",
75
+ "oxlint": "1.7.0",
76
76
  "rimraf": "6.0.1",
77
77
  "typescript": "5.8.3",
78
- "typescript-eslint": "8.35.1"
78
+ "typescript-eslint": "8.37.0"
79
79
  }
80
80
  }
@@ -1,24 +1,26 @@
1
1
  #!/usr/bin/env node
2
2
  import { ansi_blue, ansi_italic, ansi_red } from "./lib_ansi.js";
3
3
  import { diffdash_config_get } from "./lib_diffdash_config.js";
4
- import { diffdash_sequence_compare, diffdash_sequence_normal } from "./lib_diffdash_sequence.js";
4
+ import { diffdash_sequence_compare, diffdash_sequence_normal, diffdash_sequence_output } from "./lib_diffdash_sequence.js";
5
5
  import { error_abort } from "./lib_error.js";
6
6
  import { tell_okay, tell_plain } from "./lib_tell.js";
7
7
  async function main() {
8
8
  const config = diffdash_config_get();
9
- const { llm_compare, silent } = config;
10
- if (!silent) {
9
+ const { silent, just_output, llm_compare } = config;
10
+ if (!silent && !just_output) {
11
11
  const diffdash = ansi_italic(ansi_blue("Diff") + ansi_red("Dash"));
12
12
  tell_plain(`This is ${diffdash} - the fast AI Git commit tool`);
13
13
  }
14
- // eslint-disable-next-line unicorn/prefer-ternary
15
- if (llm_compare) {
14
+ if (just_output) {
15
+ await diffdash_sequence_output(config);
16
+ }
17
+ else if (llm_compare) {
16
18
  await diffdash_sequence_compare(config);
17
19
  }
18
20
  else {
19
21
  await diffdash_sequence_normal(config);
20
22
  }
21
- if (!silent) {
23
+ if (!silent && !just_output) {
22
24
  tell_okay();
23
25
  }
24
26
  }
@@ -32,18 +32,10 @@ export function datetime_format_utc_iso_hms(date) {
32
32
  return date.toISOString().slice(11, 19);
33
33
  }
34
34
  export function datetime_format_utc_timestamp_alpha(date) {
35
- const s1 = date.toISOString().slice(0, 19);
36
- const s2 = s1.replaceAll(DASH, EMPTY);
37
- const s3 = s2.replaceAll(COLON, EMPTY);
38
- const s4 = s3 + "Z";
39
- return s4;
35
+ return date.toISOString().slice(0, 19).replaceAll(DASH, EMPTY).replaceAll(COLON, EMPTY) + "Z";
40
36
  }
41
37
  export function datetime_format_utc_timestamp_numeric(date) {
42
- const s1 = date.toISOString().slice(0, 19);
43
- const s2 = s1.replaceAll(DASH, EMPTY);
44
- const s3 = s2.replaceAll(COLON, EMPTY);
45
- const s4 = s3.replace("T", EMPTY);
46
- return s4;
38
+ return date.toISOString().slice(0, 19).replaceAll(DASH, EMPTY).replaceAll(COLON, EMPTY).replace("T", EMPTY);
47
39
  }
48
40
  function datetime_localize(date) {
49
41
  return new Date(date.valueOf() - date.getTimezoneOffset() * 60_000);
@@ -2,8 +2,6 @@ import { cli_boolean, cli_choice_default, cli_make_parser, cli_string } from "./
2
2
  import { diffdash_llm_model_choices, diffdash_llm_model_default, diffdash_llm_model_fallback, } from "./lib_diffdash_llm.js";
3
3
  const diffdash_cli_schema = {
4
4
  version: cli_boolean({ help: "show program version information and exit" }),
5
- add_prefix: cli_string({ help: "add a prefix to the commit message summary line", metavar: "PREFIX" }),
6
- add_suffix: cli_string({ help: "add a suffix to the commit message summary line", metavar: "SUFFIX" }),
7
5
  auto_add: cli_boolean({ help: "automatically stage all changes without confirmation" }),
8
6
  auto_commit: cli_boolean({ help: "automatically commit changes without confirmation" }),
9
7
  auto_push: cli_boolean({ help: "automatically push changes after commit without confirmation" }),
@@ -14,6 +12,8 @@ const diffdash_cli_schema = {
14
12
  disable_push: cli_boolean({ help: "disable pushing changes - exit after making the commit" }),
15
13
  push_no_verify: cli_boolean({ help: "bypass git hooks when pushing to Git" }),
16
14
  push_force: cli_boolean({ help: "apply force when pushing to Git" }),
15
+ add_prefix: cli_string({ help: "add a prefix to the commit message summary line", metavar: "PREFIX" }),
16
+ add_suffix: cli_string({ help: "add a suffix to the commit message summary line", metavar: "SUFFIX" }),
17
17
  llm_list: cli_boolean({ help: "display a list of available Large Language Models and exit" }),
18
18
  llm_compare: cli_boolean({ help: "compare the generated messages from all models - but do not commit" }),
19
19
  llm_router: cli_boolean({ help: "prefer to access the LLM via a router rather than direct" }),
@@ -24,6 +24,7 @@ const diffdash_cli_schema = {
24
24
  default: diffdash_llm_model_default,
25
25
  }),
26
26
  llm_excludes: cli_string({ help: "models to exclude from comparison (comma separated)", metavar: "MODELS" }),
27
+ just_output: cli_boolean({ help: "just output the commit message for use in scripts" }),
27
28
  silent: cli_boolean({ help: "suppress all normal output - errors and aborts still display" }),
28
29
  debug_llm_inputs: cli_boolean({ help: "debug inputs (including all prompts) sent to the LLM" }),
29
30
  debug_llm_outputs: cli_boolean({ help: "debug outputs received from the LLM" }),
@@ -7,7 +7,7 @@ import { PACKAGE_NAME, PACKAGE_VERSION } from "./lib_package.js";
7
7
  import { tell_plain } from "./lib_tell.js";
8
8
  export function diffdash_config_get() {
9
9
  const pa = diffdash_cli_parser.parsed_args;
10
- const { version, add_prefix, add_suffix, auto_add, auto_commit, auto_push, disable_add, disable_commit, disable_preview, disable_status, disable_push, push_no_verify, push_force, llm_list, llm_compare, llm_router, llm_fallback, llm_model, llm_excludes, silent, debug_llm_inputs, debug_llm_outputs, } = pa;
10
+ const { version, auto_add, auto_commit, auto_push, disable_add, disable_commit, disable_preview, disable_status, disable_push, push_no_verify, push_force, add_prefix, add_suffix, llm_list, llm_compare, llm_router, llm_fallback, llm_model, llm_excludes, just_output, silent, debug_llm_inputs, debug_llm_outputs, } = pa;
11
11
  if (version) {
12
12
  tell_plain(`${PACKAGE_NAME} v${PACKAGE_VERSION}`);
13
13
  process.exit(0);
@@ -30,8 +30,6 @@ export function diffdash_config_get() {
30
30
  debug_channels.llm_inputs = debug_llm_inputs;
31
31
  debug_channels.llm_outputs = debug_llm_outputs;
32
32
  const config = {
33
- add_prefix,
34
- add_suffix,
35
33
  auto_add,
36
34
  auto_commit,
37
35
  auto_push,
@@ -42,9 +40,12 @@ export function diffdash_config_get() {
42
40
  disable_push,
43
41
  push_no_verify,
44
42
  push_force,
43
+ add_prefix,
44
+ add_suffix,
45
45
  llm_compare,
46
46
  llm_config,
47
47
  all_llm_configs,
48
+ just_output,
48
49
  silent,
49
50
  };
50
51
  debug_inspect_when(debug_channels.config, config, "config");
@@ -5,16 +5,17 @@ const model_name_fallback = "claude-3.5-haiku";
5
5
  const model_name_options = [
6
6
  "claude-3.5-haiku",
7
7
  "deepseek-v3",
8
- "deepseek-r1",
8
+ "devstral-medium",
9
9
  "devstral-small",
10
- "ernie-4.5-300b",
11
10
  "gemini-2.0-flash",
12
11
  "gemini-2.5-flash",
13
12
  "gpt-4.1-mini", // the best
14
13
  "gpt-4.1-nano",
15
14
  "gpt-4o-mini",
16
15
  "grok-3-mini",
16
+ "kimi-k2",
17
17
  "llama-4-maverick",
18
+ "mercury-coder",
18
19
  "mistral-medium-3",
19
20
  "qwen3-235b-a22b",
20
21
  ];
@@ -8,7 +8,7 @@ import { git_message_validate_check, git_message_validate_get_result } from "./l
8
8
  import { git_simple_open_check_not_bare, git_simple_open_git_repo } from "./lib_git_simple_open.js";
9
9
  import { git_simple_staging_create_commit, git_simple_staging_get_staged_diff, git_simple_staging_get_staged_diffstat, git_simple_staging_has_staged_changes, git_simple_staging_has_unstaged_changes, git_simple_staging_push_to_remote, git_simple_staging_stage_all_changes, } from "./lib_git_simple_staging.js";
10
10
  import { llm_config_get_model_via } from "./lib_llm_config.js";
11
- import { stdio_write_stdout_linefeed } from "./lib_stdio_write.js";
11
+ import { stdio_write_stdout, stdio_write_stdout_linefeed } from "./lib_stdio_write.js";
12
12
  import { tell_action, tell_info, tell_plain, tell_success, tell_warning } from "./lib_tell.js";
13
13
  import { tui_justify_left } from "./lib_tui_justify.js";
14
14
  import { tui_readline_confirm } from "./lib_tui_readline.js";
@@ -115,10 +115,10 @@ async function phase_compare({ config, git }) {
115
115
  git_message_display({ git_message, teller });
116
116
  }
117
117
  }
118
- async function phase_commit({ config, git }) {
119
- const { add_prefix, add_suffix, auto_commit, disable_commit, disable_preview, silent, llm_config } = config;
118
+ async function phase_generate({ config, git }) {
119
+ const { disable_preview, add_prefix, add_suffix, llm_config, just_output, silent } = config;
120
120
  const model_via = llm_config_get_model_via(llm_config);
121
- if (!silent) {
121
+ if (!silent && !just_output) {
122
122
  tell_action(`Generating the Git commit message using ${model_via}`);
123
123
  }
124
124
  const diffstat = await git_simple_staging_get_staged_diffstat(git);
@@ -127,18 +127,22 @@ async function phase_commit({ config, git }) {
127
127
  const result = await git_message_generate_result({ llm_config, inputs });
128
128
  const { error_text } = result;
129
129
  let { git_message } = result;
130
- if (error_text) {
130
+ if (error_text || git_message === null) {
131
131
  abort_with_error(`Failed to generate a commit message using ${model_via}: ${error_text}`);
132
132
  }
133
- if (!git_message) {
134
- return;
135
- }
136
133
  git_message_validate_check(git_message);
137
134
  git_message = diffdash_add_prefix_or_suffix({ git_message, add_prefix, add_suffix });
138
135
  git_message = diffdash_add_footer({ git_message, llm_config });
139
- if (!disable_preview && !silent) {
136
+ if (just_output) {
137
+ stdio_write_stdout(git_message);
138
+ }
139
+ else if (!disable_preview && !silent) {
140
140
  git_message_display({ git_message, teller: tell_plain });
141
141
  }
142
+ return git_message;
143
+ }
144
+ async function phase_commit({ config, git, git_message, }) {
145
+ const { auto_commit, disable_commit, silent } = config;
142
146
  if (disable_commit) {
143
147
  return;
144
148
  }
@@ -188,9 +192,14 @@ export async function diffdash_sequence_normal(config) {
188
192
  const git = await phase_open();
189
193
  await phase_add({ config, git });
190
194
  await phase_status({ config, git });
191
- await phase_commit({ config, git });
195
+ const git_message = await phase_generate({ config, git });
196
+ await phase_commit({ config, git, git_message });
192
197
  await phase_push({ config, git });
193
198
  }
199
+ export async function diffdash_sequence_output(config) {
200
+ const git = await phase_open();
201
+ await phase_generate({ config, git });
202
+ }
194
203
  export async function diffdash_sequence_compare(config) {
195
204
  const git = await phase_open();
196
205
  await phase_add({ config, git });
@@ -1,4 +1,5 @@
1
1
  import { abort_with_error } from "./lib_abort.js";
2
+ import { LF } from "./lib_char_control.js";
2
3
  export function error_ignore(_error) {
3
4
  /* intentionally left empty */
4
5
  }
@@ -8,7 +9,15 @@ export function error_console(error) {
8
9
  export function error_get_text(error) {
9
10
  return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
10
11
  }
12
+ export function error_get_message(error) {
13
+ const message_perhaps_multiline = error instanceof Error
14
+ ? error.name === "Error"
15
+ ? error.message
16
+ : `${error.name}: ${error.message}`
17
+ : String(error);
18
+ return message_perhaps_multiline.split(LF)[0] || "Blank Error";
19
+ }
11
20
  export function error_abort(error) {
12
- const message = `Unhandled error: ${error_get_text(error)}`;
13
- abort_with_error(message);
21
+ const message_perhaps_multiline = `Unhandled error: ${error_get_text(error)}`;
22
+ abort_with_error(message_perhaps_multiline);
14
23
  }
@@ -18,13 +18,13 @@ export function llm_access_available({ llm_model_details, llm_model_name, llm_ex
18
18
  return true;
19
19
  }
20
20
  }
21
- if (llm_model_code_requesty !== null) {
22
- if (llm_provider_get_api_key("requesty")) {
21
+ if (llm_model_code_openrouter !== null) {
22
+ if (llm_provider_get_api_key("openrouter")) {
23
23
  return true;
24
24
  }
25
25
  }
26
- if (llm_model_code_openrouter !== null) {
27
- if (llm_provider_get_api_key("openrouter")) {
26
+ if (llm_model_code_requesty !== null) {
27
+ if (llm_provider_get_api_key("requesty")) {
28
28
  return true;
29
29
  }
30
30
  }
@@ -41,29 +41,29 @@ export function llm_access_get({ llm_model_details, llm_model_name, llm_router,
41
41
  }
42
42
  }
43
43
  }
44
- if (llm_model_code_requesty !== null) {
45
- const llm_api_key = llm_provider_get_api_key("requesty");
46
- if (llm_api_key) {
47
- return { llm_model_code: llm_model_code_requesty, llm_provider: "requesty", llm_api_key };
48
- }
49
- }
50
44
  if (llm_model_code_openrouter !== null) {
51
45
  const llm_api_key = llm_provider_get_api_key("openrouter");
52
46
  if (llm_api_key) {
53
47
  return { llm_model_code: llm_model_code_openrouter, llm_provider: "openrouter", llm_api_key };
54
48
  }
55
49
  }
50
+ if (llm_model_code_requesty !== null) {
51
+ const llm_api_key = llm_provider_get_api_key("requesty");
52
+ if (llm_api_key) {
53
+ return { llm_model_code: llm_model_code_requesty, llm_provider: "requesty", llm_api_key };
54
+ }
55
+ }
56
56
  if (llm_model_code_direct !== null && llm_provider !== null) {
57
57
  const llm_api_key = llm_provider_get_api_key(llm_provider);
58
58
  if (llm_api_key) {
59
59
  return { llm_model_code: llm_model_code_direct, llm_provider, llm_api_key };
60
60
  }
61
61
  }
62
- const env_requesty = llm_provider_get_api_key_env("requesty");
63
62
  const env_openrouter = llm_provider_get_api_key_env("openrouter");
63
+ const env_requesty = llm_provider_get_api_key_env("requesty");
64
64
  if (llm_provider !== null) {
65
65
  const env_provider = llm_provider_get_api_key_env(llm_provider);
66
- abort_with_error(`Please set environment variable ${env_requesty}, ${env_openrouter} or ${env_provider}`);
66
+ abort_with_error(`Please set environment variable ${env_provider}, ${env_openrouter} or ${env_requesty}`);
67
67
  }
68
- abort_with_error(`Please set environment variable ${env_requesty} or ${env_openrouter}`);
68
+ abort_with_error(`Please set environment variable ${env_openrouter} or ${env_requesty}`);
69
69
  }
@@ -66,15 +66,26 @@ const LLM_MODEL_DETAILS = [
66
66
  cents_output: 219,
67
67
  has_structured_json: true,
68
68
  },
69
+ {
70
+ llm_model_name: "devstral-medium",
71
+ llm_provider: null,
72
+ llm_model_code_direct: "devstral-medium-latest",
73
+ llm_model_code_requesty: "mistral/devstral-medium-latest",
74
+ llm_model_code_openrouter: "mistralai/devstral-medium",
75
+ context_window: 128_000,
76
+ cents_input: 40,
77
+ cents_output: 200,
78
+ has_structured_json: true,
79
+ },
69
80
  {
70
81
  llm_model_name: "devstral-small",
71
82
  llm_provider: null,
72
- llm_model_code_direct: null,
83
+ llm_model_code_direct: "devstral-small-latest",
73
84
  llm_model_code_requesty: "mistral/devstral-small-latest",
74
85
  llm_model_code_openrouter: "mistralai/devstral-small",
75
86
  context_window: 128_000,
76
- cents_input: 7,
77
- cents_output: 10,
87
+ cents_input: 10,
88
+ cents_output: 30,
78
89
  has_structured_json: true,
79
90
  },
80
91
  {
@@ -192,7 +203,7 @@ const LLM_MODEL_DETAILS = [
192
203
  llm_provider: null,
193
204
  llm_model_code_direct: "grok-3",
194
205
  llm_model_code_requesty: "xai/grok-3-beta",
195
- llm_model_code_openrouter: "x-ai/grok-3-beta",
206
+ llm_model_code_openrouter: "x-ai/grok-3",
196
207
  context_window: 131_072,
197
208
  cents_input: 300,
198
209
  cents_output: 1500,
@@ -203,12 +214,34 @@ const LLM_MODEL_DETAILS = [
203
214
  llm_provider: null,
204
215
  llm_model_code_direct: "grok-3-mini",
205
216
  llm_model_code_requesty: "xai/grok-3-mini-beta",
206
- llm_model_code_openrouter: "x-ai/grok-3-mini-beta",
217
+ llm_model_code_openrouter: "x-ai/grok-3-mini",
207
218
  context_window: 131_072,
208
219
  cents_input: 30,
209
220
  cents_output: 50,
210
221
  has_structured_json: true,
211
222
  },
223
+ {
224
+ llm_model_name: "grok-4",
225
+ llm_provider: null,
226
+ llm_model_code_direct: "grok-4",
227
+ llm_model_code_requesty: "xai/grok-4",
228
+ llm_model_code_openrouter: "x-ai/grok-4",
229
+ context_window: 256_000,
230
+ cents_input: 300,
231
+ cents_output: 1500,
232
+ has_structured_json: true,
233
+ },
234
+ {
235
+ llm_model_name: "kimi-k2",
236
+ llm_provider: null,
237
+ llm_model_code_direct: "kimi-k2-0711-preview",
238
+ llm_model_code_requesty: null /* "novita/moonshotai/kimi-k2-instruct" */,
239
+ llm_model_code_openrouter: "moonshotai/kimi-k2",
240
+ context_window: 131_072,
241
+ cents_input: 60,
242
+ cents_output: 250,
243
+ has_structured_json: true,
244
+ },
212
245
  {
213
246
  llm_model_name: "llama-4-maverick",
214
247
  llm_provider: null,
@@ -216,8 +249,8 @@ const LLM_MODEL_DETAILS = [
216
249
  llm_model_code_requesty: "parasail/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
217
250
  llm_model_code_openrouter: "meta-llama/llama-4-maverick",
218
251
  context_window: 1_048_576,
219
- cents_input: 21,
220
- cents_output: 85,
252
+ cents_input: 15,
253
+ cents_output: 60,
221
254
  has_structured_json: true,
222
255
  },
223
256
  {
@@ -232,7 +265,18 @@ const LLM_MODEL_DETAILS = [
232
265
  has_structured_json: true,
233
266
  },
234
267
  {
235
- llm_model_name: "mercury-coder-small",
268
+ llm_model_name: "mercury",
269
+ llm_provider: null,
270
+ llm_model_code_direct: null,
271
+ llm_model_code_requesty: null,
272
+ llm_model_code_openrouter: "inception/mercury",
273
+ context_window: 32_000,
274
+ cents_input: 25,
275
+ cents_output: 100,
276
+ has_structured_json: false,
277
+ },
278
+ {
279
+ llm_model_name: "mercury-coder",
236
280
  llm_provider: null,
237
281
  llm_model_code_direct: null,
238
282
  llm_model_code_requesty: null,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnowennixon/diffdash",
3
- "version": "1.4.1",
3
+ "version": "1.6.0",
4
4
  "description": "A command-line tool to generate Git commit messages using AI",
5
5
  "license": "0BSD",
6
6
  "author": "John Owen Nixon",
@@ -20,30 +20,30 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@ai-sdk/anthropic": "1.2.12",
23
- "@ai-sdk/deepseek": "0.2.14",
24
- "@ai-sdk/google": "1.2.21",
25
- "@ai-sdk/openai": "1.3.22",
23
+ "@ai-sdk/deepseek": "0.2.16",
24
+ "@ai-sdk/google": "1.2.22",
25
+ "@ai-sdk/openai": "1.3.23",
26
26
  "@openrouter/ai-sdk-provider": "0.7.2",
27
27
  "@requesty/ai-sdk": "0.0.9",
28
- "ai": "4.3.16",
28
+ "ai": "4.3.19",
29
29
  "ansis": "4.1.0",
30
30
  "argparse": "2.0.1",
31
31
  "cli-table3": "0.6.5",
32
32
  "simple-git": "3.28.0",
33
- "zod": "3.25.67"
33
+ "zod": "3.25.76"
34
34
  },
35
35
  "devDependencies": {
36
- "@biomejs/biome": "2.0.6",
36
+ "@biomejs/biome": "2.1.1",
37
37
  "@eslint/eslintrc": "3.3.1",
38
- "@eslint/js": "9.30.0",
38
+ "@eslint/js": "9.31.0",
39
39
  "@johnowennixon/add-shebangs": "1.1.0",
40
40
  "@johnowennixon/chmodx": "2.0.0",
41
- "@stylistic/eslint-plugin": "5.1.0",
41
+ "@stylistic/eslint-plugin": "5.2.0",
42
42
  "@types/argparse": "2.0.17",
43
- "@types/node": "24.0.8",
44
- "@typescript-eslint/eslint-plugin": "8.35.1",
45
- "@typescript-eslint/parser": "8.35.1",
46
- "eslint": "9.30.0",
43
+ "@types/node": "24.0.14",
44
+ "@typescript-eslint/eslint-plugin": "8.37.0",
45
+ "@typescript-eslint/parser": "8.37.0",
46
+ "eslint": "9.31.0",
47
47
  "eslint-import-resolver-typescript": "4.4.4",
48
48
  "eslint-plugin-import-x": "4.16.1",
49
49
  "eslint-plugin-sonarjs": "3.0.4",
@@ -52,10 +52,10 @@
52
52
  "knip": "5.61.3",
53
53
  "markdownlint-cli2": "0.18.1",
54
54
  "npm-run-all2": "8.0.4",
55
- "oxlint": "1.4.0",
55
+ "oxlint": "1.7.0",
56
56
  "rimraf": "6.0.1",
57
57
  "typescript": "5.8.3",
58
- "typescript-eslint": "8.35.1"
58
+ "typescript-eslint": "8.37.0"
59
59
  },
60
60
  "scripts": {
61
61
  "build": "run-s -ls build:clean build:tsc build:shebang build:chmod",