@johnowennixon/diffdash 1.5.0 → 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,17 +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
15
- * Can just output the commit message for use in scripts
16
+ * Disable or auto-approve various stages
17
+ * Just output the commit message for use in scripts
16
18
  * Configuration using standard API provider environment variables
17
19
  * Uses the Vercel AI SDK
18
20
  * Uses structured JSON with compatible models
package/dist/package.json CHANGED
@@ -40,30 +40,30 @@
40
40
  },
41
41
  "dependencies": {
42
42
  "@ai-sdk/anthropic": "1.2.12",
43
- "@ai-sdk/deepseek": "0.2.14",
43
+ "@ai-sdk/deepseek": "0.2.16",
44
44
  "@ai-sdk/google": "1.2.22",
45
- "@ai-sdk/openai": "1.3.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.1",
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.10",
64
- "@typescript-eslint/eslint-plugin": "8.35.1",
65
- "@typescript-eslint/parser": "8.35.1",
66
- "eslint": "9.30.1",
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.5.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
  }
@@ -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);
@@ -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
  ];
@@ -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
  }
@@ -9,13 +10,14 @@ export function error_get_text(error) {
9
10
  return error instanceof Error ? `${error.name}: ${error.message}` : String(error);
10
11
  }
11
12
  export function error_get_message(error) {
12
- return error instanceof Error
13
+ const message_perhaps_multiline = error instanceof Error
13
14
  ? error.name === "Error"
14
15
  ? error.message
15
16
  : `${error.name}: ${error.message}`
16
17
  : String(error);
18
+ return message_perhaps_multiline.split(LF)[0] || "Blank Error";
17
19
  }
18
20
  export function error_abort(error) {
19
- const message = `Unhandled error: ${error_get_text(error)}`;
20
- abort_with_error(message);
21
+ const message_perhaps_multiline = `Unhandled error: ${error_get_text(error)}`;
22
+ abort_with_error(message_perhaps_multiline);
21
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
  {
@@ -220,6 +231,17 @@ const LLM_MODEL_DETAILS = [
220
231
  cents_output: 1500,
221
232
  has_structured_json: true,
222
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
+ },
223
245
  {
224
246
  llm_model_name: "llama-4-maverick",
225
247
  llm_provider: null,
@@ -227,8 +249,8 @@ const LLM_MODEL_DETAILS = [
227
249
  llm_model_code_requesty: "parasail/meta-llama/Llama-4-Maverick-17B-128E-Instruct-FP8",
228
250
  llm_model_code_openrouter: "meta-llama/llama-4-maverick",
229
251
  context_window: 1_048_576,
230
- cents_input: 21,
231
- cents_output: 85,
252
+ cents_input: 15,
253
+ cents_output: 60,
232
254
  has_structured_json: true,
233
255
  },
234
256
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@johnowennixon/diffdash",
3
- "version": "1.5.0",
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",
23
+ "@ai-sdk/deepseek": "0.2.16",
24
24
  "@ai-sdk/google": "1.2.22",
25
- "@ai-sdk/openai": "1.3.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.1",
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.10",
44
- "@typescript-eslint/eslint-plugin": "8.35.1",
45
- "@typescript-eslint/parser": "8.35.1",
46
- "eslint": "9.30.1",
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.5.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",