@johnowennixon/diffdash 1.0.3 → 1.2.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/dist/package.json +80 -0
- package/dist/src/diffdash.js +26 -0
- package/dist/src/lib_abort.js +21 -0
- package/dist/src/lib_ansi.js +21 -0
- package/dist/src/lib_char_box.js +3 -0
- package/dist/src/lib_char_control.js +8 -0
- package/dist/src/lib_char_digit.js +10 -0
- package/dist/src/lib_char_empty.js +1 -0
- package/dist/src/lib_char_punctuation.js +37 -0
- package/dist/src/lib_cli.js +187 -0
- package/dist/src/lib_datetime.js +62 -0
- package/dist/src/lib_debug.js +68 -0
- package/dist/src/lib_diffdash_add.js +23 -0
- package/dist/src/lib_diffdash_cli.js +34 -0
- package/dist/src/lib_diffdash_config.js +52 -0
- package/dist/src/lib_diffdash_llm.js +24 -0
- package/dist/src/lib_diffdash_sequence.js +199 -0
- package/dist/src/lib_duration.js +29 -0
- package/dist/src/lib_enabled.js +30 -0
- package/dist/src/lib_env.js +18 -0
- package/dist/src/lib_error.js +14 -0
- package/dist/src/lib_file_path.js +22 -0
- package/dist/src/lib_git_message_display.js +4 -0
- package/dist/src/lib_git_message_generate.js +55 -0
- package/dist/src/lib_git_message_prompt.js +72 -0
- package/dist/src/lib_git_message_schema.js +16 -0
- package/dist/src/lib_git_message_validate.js +61 -0
- package/dist/src/lib_git_simple_open.js +24 -0
- package/dist/src/lib_git_simple_staging.js +41 -0
- package/dist/src/lib_inspect.js +4 -0
- package/dist/src/lib_llm_access.js +69 -0
- package/dist/src/lib_llm_chat.js +66 -0
- package/dist/src/lib_llm_config.js +23 -0
- package/dist/src/lib_llm_list.js +21 -0
- package/dist/src/lib_llm_model.js +336 -0
- package/dist/src/lib_llm_provider.js +63 -0
- package/dist/src/lib_llm_tokens.js +14 -0
- package/dist/src/lib_package.js +7 -0
- package/dist/src/lib_parse_number.js +13 -0
- package/dist/src/lib_stdio_write.js +14 -0
- package/dist/src/lib_string_types.js +1 -0
- package/dist/src/lib_tell.js +58 -0
- package/dist/src/lib_tui_block.js +10 -0
- package/dist/src/lib_tui_justify.js +29 -0
- package/dist/src/lib_tui_readline.js +16 -0
- package/dist/src/lib_tui_table.js +20 -0
- package/dist/src/lib_tui_truncate.js +13 -0
- package/dist/src/lib_type_infer.js +1 -0
- package/package.json +30 -25
- package/out/diffdash.cjs +0 -33011
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@johnowennixon/diffdash",
|
|
3
|
+
"version": "1.2.1",
|
|
4
|
+
"description": "A command-line tool to generate Git commit messages using AI",
|
|
5
|
+
"license": "0BSD",
|
|
6
|
+
"author": "John Owen Nixon",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/johnowennixon/diffdash.git"
|
|
10
|
+
},
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"bin": {
|
|
19
|
+
"diffdash": "dist/src/diffdash.js"
|
|
20
|
+
},
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "run-s -ls build:clean build:tsc build:shebang build:chmod",
|
|
23
|
+
"build:chmod": "echo 'Changing bin files to be executable' && chmodx --package",
|
|
24
|
+
"build:clean": "echo 'Removing dist' && rimraf dist",
|
|
25
|
+
"build:shebang": "echo 'Fixing the shebangs' && add-shebangs --node --exclude 'dist/**/lib_*.js' 'dist/**/*.js'",
|
|
26
|
+
"build:tsc": "echo 'Transpiling TypeScript to dist (using tsc)' && tsc --erasableSyntaxOnly --libReplacement false",
|
|
27
|
+
"fix": "run-s -ls fix:biome fix:markdownlint",
|
|
28
|
+
"fix:biome": "echo 'Fixing with Biome' && biome check --write",
|
|
29
|
+
"fix:eslint": "echo 'Fixing with ESLint' && eslint --fix",
|
|
30
|
+
"fix:markdownlint": "echo 'Fixing with markdownlint' && markdownlint-cli2 '**/*.md' --fix",
|
|
31
|
+
"fix:oxlint": "echo 'Fixing with oxlint' && oxlint --fix",
|
|
32
|
+
"lint": "run-s -ls lint:biome lint:oxlint lint:knip lint:markdownlint",
|
|
33
|
+
"lint:biome": "echo 'Linting with Biome' && biome check",
|
|
34
|
+
"lint:eslint": "echo 'Linting with ESLint' && eslint",
|
|
35
|
+
"lint:knip": "echo 'Linting with Knip' && knip",
|
|
36
|
+
"lint:markdownlint": "echo 'Linting with markdownlint' && markdownlint-cli2 '**/*.md'",
|
|
37
|
+
"lint:oxlint": "echo 'Linting with oxlint' && oxlint",
|
|
38
|
+
"lint:tsc": "echo 'Linting with tsc' && tsc --noEmit --erasableSyntaxOnly --libReplacement false",
|
|
39
|
+
"test": "run-s -ls lint build"
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
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",
|
|
46
|
+
"@openrouter/ai-sdk-provider": "0.7.2",
|
|
47
|
+
"@requesty/ai-sdk": "0.0.9",
|
|
48
|
+
"ai": "4.3.16",
|
|
49
|
+
"ansis": "4.1.0",
|
|
50
|
+
"argparse": "2.0.1",
|
|
51
|
+
"cli-table3": "0.6.5",
|
|
52
|
+
"simple-git": "3.28.0",
|
|
53
|
+
"zod": "3.25.67"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@biomejs/biome": "2.0.6",
|
|
57
|
+
"@eslint/eslintrc": "3.3.1",
|
|
58
|
+
"@eslint/js": "9.30.0",
|
|
59
|
+
"@johnowennixon/add-shebangs": "1.1.0",
|
|
60
|
+
"@johnowennixon/chmodx": "2.0.0",
|
|
61
|
+
"@stylistic/eslint-plugin": "5.1.0",
|
|
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",
|
|
67
|
+
"eslint-import-resolver-typescript": "4.4.4",
|
|
68
|
+
"eslint-plugin-import-x": "4.16.1",
|
|
69
|
+
"eslint-plugin-sonarjs": "3.0.4",
|
|
70
|
+
"eslint-plugin-unicorn": "59.0.1",
|
|
71
|
+
"globals": "16.3.0",
|
|
72
|
+
"knip": "5.61.3",
|
|
73
|
+
"markdownlint-cli2": "0.18.1",
|
|
74
|
+
"npm-run-all2": "8.0.4",
|
|
75
|
+
"oxlint": "1.4.0",
|
|
76
|
+
"rimraf": "6.0.1",
|
|
77
|
+
"typescript": "5.8.3",
|
|
78
|
+
"typescript-eslint": "8.35.1"
|
|
79
|
+
}
|
|
80
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { ansi_blue, ansi_italic, ansi_red } from "./lib_ansi.js";
|
|
3
|
+
import { diffdash_config_get } from "./lib_diffdash_config.js";
|
|
4
|
+
import { diffdash_sequence_compare, diffdash_sequence_normal } from "./lib_diffdash_sequence.js";
|
|
5
|
+
import { error_abort } from "./lib_error.js";
|
|
6
|
+
import { tell_okay, tell_plain } from "./lib_tell.js";
|
|
7
|
+
async function main() {
|
|
8
|
+
const config = diffdash_config_get();
|
|
9
|
+
const { llm_compare, silent } = config;
|
|
10
|
+
if (!silent) {
|
|
11
|
+
const diffdash = ansi_italic(ansi_blue("Diff") + ansi_red("Dash"));
|
|
12
|
+
tell_plain(`This is ${diffdash} - the fast AI Git commit tool`);
|
|
13
|
+
}
|
|
14
|
+
// eslint-disable-next-line unicorn/prefer-ternary
|
|
15
|
+
if (llm_compare) {
|
|
16
|
+
await diffdash_sequence_compare(config);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
await diffdash_sequence_normal(config);
|
|
20
|
+
}
|
|
21
|
+
if (!silent) {
|
|
22
|
+
tell_okay();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
// eslint-disable-next-line unicorn/prefer-top-level-await
|
|
26
|
+
main().catch(error_abort);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ansi_red, ansi_yellow } from "./lib_ansi.js";
|
|
2
|
+
import { stdio_write_stderr_linefeed } from "./lib_stdio_write.js";
|
|
3
|
+
export function abort_exit() {
|
|
4
|
+
process.exit(1);
|
|
5
|
+
}
|
|
6
|
+
export function abort_with_warning(message) {
|
|
7
|
+
if (process.stdout.isTTY) {
|
|
8
|
+
process.stdout.clearLine(0);
|
|
9
|
+
process.stdout.cursorTo(0);
|
|
10
|
+
}
|
|
11
|
+
stdio_write_stderr_linefeed(ansi_yellow(message));
|
|
12
|
+
abort_exit();
|
|
13
|
+
}
|
|
14
|
+
export function abort_with_error(message) {
|
|
15
|
+
if (process.stdout.isTTY) {
|
|
16
|
+
process.stdout.clearLine(0);
|
|
17
|
+
process.stdout.cursorTo(0);
|
|
18
|
+
}
|
|
19
|
+
stdio_write_stderr_linefeed(ansi_red(message));
|
|
20
|
+
abort_exit();
|
|
21
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import ansis from "ansis";
|
|
2
|
+
import { EMPTY } from "./lib_char_empty.js";
|
|
3
|
+
export const ansi_normal = (message) => message;
|
|
4
|
+
export const ansi_dim = (message) => ansis.dim(message);
|
|
5
|
+
export const ansi_bold = (message) => ansis.bold(message);
|
|
6
|
+
export const ansi_italic = (message) => ansis.italic(message);
|
|
7
|
+
export const ansi_red = (message) => ansis.redBright(message);
|
|
8
|
+
export const ansi_yellow = (message) => ansis.yellowBright(message);
|
|
9
|
+
export const ansi_green = (message) => ansis.greenBright(message);
|
|
10
|
+
export const ansi_cyan = (message) => ansis.cyanBright(message);
|
|
11
|
+
export const ansi_blue = (message) => ansis.blueBright(message);
|
|
12
|
+
export const ansi_magenta = (message) => ansis.magentaBright(message);
|
|
13
|
+
export const ansi_grey = (message) => ansis.gray(message);
|
|
14
|
+
export function ansi_boolean(bool) {
|
|
15
|
+
return bool ? ansi_green : ansi_red;
|
|
16
|
+
}
|
|
17
|
+
export function ansi_strip(text) {
|
|
18
|
+
const pattern = String.raw `\u001B\[[^m]*m`;
|
|
19
|
+
// eslint-disable-next-line sonarjs/no-control-regex
|
|
20
|
+
return text.replace(new RegExp(pattern, "g"), EMPTY);
|
|
21
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const DIGIT_0 = "0";
|
|
2
|
+
export const DIGIT_1 = "1";
|
|
3
|
+
export const DIGIT_2 = "2";
|
|
4
|
+
export const DIGIT_3 = "3";
|
|
5
|
+
export const DIGIT_4 = "4";
|
|
6
|
+
export const DIGIT_5 = "5";
|
|
7
|
+
export const DIGIT_6 = "6";
|
|
8
|
+
export const DIGIT_7 = "7";
|
|
9
|
+
export const DIGIT_8 = "8";
|
|
10
|
+
export const DIGIT_9 = "9";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const EMPTY = "";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export const AMPERSAND = "&";
|
|
2
|
+
export const APOSTROPHE = "'";
|
|
3
|
+
export const ASTERISK = "*";
|
|
4
|
+
export const AT_SIGN = "@";
|
|
5
|
+
export const BACKSLASH = "\\";
|
|
6
|
+
export const BACKTICK = "`";
|
|
7
|
+
export const CARET = "^";
|
|
8
|
+
export const COLON = ":";
|
|
9
|
+
export const COMMA = ",";
|
|
10
|
+
export const CURLY_LEFT = "{";
|
|
11
|
+
export const CURLY_RIGHT = "}";
|
|
12
|
+
export const DASH = "-";
|
|
13
|
+
export const DOLLAR = "$";
|
|
14
|
+
export const DOT = ".";
|
|
15
|
+
export const ELLIPSIS = "…";
|
|
16
|
+
export const EQUALS = "=";
|
|
17
|
+
export const HASH = "#";
|
|
18
|
+
export const HAT = "^";
|
|
19
|
+
export const LESS_THAN = "<";
|
|
20
|
+
export const MORE_THAN = ">";
|
|
21
|
+
export const MINUS = "-";
|
|
22
|
+
export const PERCENT = "%";
|
|
23
|
+
export const PIPE = "|";
|
|
24
|
+
export const PLUS = "+";
|
|
25
|
+
export const POUND_SIGN = "£";
|
|
26
|
+
export const QUESTION = "?";
|
|
27
|
+
export const QUOTE_DOUBLE = '"';
|
|
28
|
+
export const QUOTE_SINGLE = "'";
|
|
29
|
+
export const ROUND_LEFT = "(";
|
|
30
|
+
export const ROUND_RIGHT = ")";
|
|
31
|
+
export const SEMICOLON = ";";
|
|
32
|
+
export const SLASH = "/";
|
|
33
|
+
export const SPACE = " ";
|
|
34
|
+
export const SQUARE_LEFT = "[";
|
|
35
|
+
export const SQUARE_RIGHT = "]";
|
|
36
|
+
export const TILDE = "~";
|
|
37
|
+
export const UNDERSCORE = "_";
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { ArgumentParser } from "argparse";
|
|
2
|
+
import { EMPTY } from "./lib_char_empty.js";
|
|
3
|
+
import { DASH, PLUS, UNDERSCORE } from "./lib_char_punctuation.js";
|
|
4
|
+
import { debug_channels, debug_inspect_when } from "./lib_debug.js";
|
|
5
|
+
export function cli_string(options = {}) {
|
|
6
|
+
return { kind: "string", options, value: "" };
|
|
7
|
+
}
|
|
8
|
+
export function cli_string_always(options = {}) {
|
|
9
|
+
return { kind: "string", options, value: "" };
|
|
10
|
+
}
|
|
11
|
+
export function cli_integer(options = {}) {
|
|
12
|
+
return { kind: "integer", options, value: 0 };
|
|
13
|
+
}
|
|
14
|
+
export function cli_integer_always(options = {}) {
|
|
15
|
+
return { kind: "integer", options, value: 0 };
|
|
16
|
+
}
|
|
17
|
+
export function cli_boolean(options = {}) {
|
|
18
|
+
return { kind: "boolean", options, value: false };
|
|
19
|
+
}
|
|
20
|
+
export function cli_command_sync(command_sync, options = {}) {
|
|
21
|
+
return { kind: "boolean", options, value: false, command_sync };
|
|
22
|
+
}
|
|
23
|
+
export function cli_command_async(command_async, options = {}) {
|
|
24
|
+
return { kind: "boolean", options, value: false, command_async };
|
|
25
|
+
}
|
|
26
|
+
export function cli_choice_optional(options = {}) {
|
|
27
|
+
return { kind: "choice", options, value: undefined };
|
|
28
|
+
}
|
|
29
|
+
export function cli_choice_default(options = {}) {
|
|
30
|
+
return { kind: "choice", options, value: undefined };
|
|
31
|
+
}
|
|
32
|
+
export function cli_choice_required(options = {}) {
|
|
33
|
+
options.required = true;
|
|
34
|
+
return { kind: "choice", options, value: undefined };
|
|
35
|
+
}
|
|
36
|
+
export function cli_list(options = {}) {
|
|
37
|
+
return { kind: "list", options, value: [] };
|
|
38
|
+
}
|
|
39
|
+
export function cli_meg_optional(meg_schema) {
|
|
40
|
+
return { kind: "meg", options: { required: false }, value: meg_schema };
|
|
41
|
+
}
|
|
42
|
+
export function cli_meg_required(meg_schema) {
|
|
43
|
+
return { kind: "meg", options: { required: true }, value: meg_schema };
|
|
44
|
+
}
|
|
45
|
+
export function cli_meg_required_predicate(meg_schema, predicate) {
|
|
46
|
+
return { kind: "meg", options: { required: true, predicate }, value: meg_schema };
|
|
47
|
+
}
|
|
48
|
+
function cli_omit(obj, key_to_omit) {
|
|
49
|
+
const { [key_to_omit]: _, ...rest } = obj;
|
|
50
|
+
return rest;
|
|
51
|
+
}
|
|
52
|
+
function cli_add_keys({ cli_schema, parser_group, predicate, }) {
|
|
53
|
+
for (const key in cli_schema) {
|
|
54
|
+
if (!Object.hasOwn(cli_schema, key)) {
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
const cli = cli_schema[key];
|
|
58
|
+
if (!cli) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (predicate !== undefined) {
|
|
62
|
+
if (!predicate(key)) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const key_replaced = key.replaceAll(UNDERSCORE, DASH);
|
|
67
|
+
const key_amended = `${cli.options.positional === true ? EMPTY : "--"}${key_replaced}`;
|
|
68
|
+
const options = cli_omit(cli.options, "positional");
|
|
69
|
+
switch (cli.kind) {
|
|
70
|
+
case "string":
|
|
71
|
+
case "choice":
|
|
72
|
+
parser_group.add_argument(key_amended, { ...options });
|
|
73
|
+
break;
|
|
74
|
+
case "integer":
|
|
75
|
+
parser_group.add_argument(key_amended, { ...options, type: "int" });
|
|
76
|
+
break;
|
|
77
|
+
case "boolean":
|
|
78
|
+
parser_group.add_argument(key_amended, { ...options, action: "store_true" });
|
|
79
|
+
break;
|
|
80
|
+
case "list":
|
|
81
|
+
parser_group.add_argument(key_amended, { ...options, nargs: PLUS });
|
|
82
|
+
break;
|
|
83
|
+
case "meg":
|
|
84
|
+
{
|
|
85
|
+
const mutually_exclusive_group = parser_group.add_mutually_exclusive_group({
|
|
86
|
+
required: cli.options.required === true,
|
|
87
|
+
});
|
|
88
|
+
cli_add_keys({
|
|
89
|
+
cli_schema: cli.value,
|
|
90
|
+
parser_group: mutually_exclusive_group,
|
|
91
|
+
predicate: cli.options.predicate,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
default:
|
|
96
|
+
throw new Error("Unknown argument kind");
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function cli_recursive_parse({ schema, namespace, predicate, }) {
|
|
101
|
+
const result = {};
|
|
102
|
+
for (const key in schema) {
|
|
103
|
+
if (!Object.hasOwn(schema, key)) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
const cli = schema[key];
|
|
107
|
+
if (!cli) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (predicate !== undefined) {
|
|
111
|
+
if (!predicate(key)) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
if (cli.kind === "meg") {
|
|
116
|
+
const nested_schema = cli.value;
|
|
117
|
+
result[key] = cli_recursive_parse({
|
|
118
|
+
schema: nested_schema,
|
|
119
|
+
namespace,
|
|
120
|
+
predicate: cli.options.predicate,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
result[key] = namespace[key];
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return result;
|
|
128
|
+
}
|
|
129
|
+
function cli_recursive_despatch_sync({ schema, namespace, parsed_args, }) {
|
|
130
|
+
for (const key in schema) {
|
|
131
|
+
if (!Object.hasOwn(schema, key)) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const cli = schema[key];
|
|
135
|
+
if (!cli) {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (cli.kind === "meg") {
|
|
139
|
+
const nested_schema = cli.value;
|
|
140
|
+
cli_recursive_despatch_sync({ schema: nested_schema, namespace, parsed_args });
|
|
141
|
+
}
|
|
142
|
+
else if (cli.kind === "boolean") {
|
|
143
|
+
if (namespace[key]) {
|
|
144
|
+
if (cli.command_sync) {
|
|
145
|
+
cli.command_sync(parsed_args);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
async function cli_recursive_despatch_async({ schema, namespace, parsed_args, }) {
|
|
152
|
+
for (const key in schema) {
|
|
153
|
+
if (!Object.hasOwn(schema, key)) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
const cli = schema[key];
|
|
157
|
+
if (!cli) {
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
if (cli.kind === "meg") {
|
|
161
|
+
const nested_schema = cli.value;
|
|
162
|
+
await cli_recursive_despatch_async({ schema: nested_schema, namespace, parsed_args });
|
|
163
|
+
}
|
|
164
|
+
else if (cli.kind === "boolean") {
|
|
165
|
+
if (namespace[key]) {
|
|
166
|
+
if (cli.command_async) {
|
|
167
|
+
await cli.command_async(parsed_args);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export function cli_make_parser({ cli_schema, description, }) {
|
|
174
|
+
const argument_parser_options = { description, allow_abbrev: false };
|
|
175
|
+
const parser = new ArgumentParser(argument_parser_options);
|
|
176
|
+
cli_add_keys({ cli_schema, parser_group: parser });
|
|
177
|
+
const namespace = parser.parse_args();
|
|
178
|
+
debug_inspect_when(debug_channels.cli, namespace, "namespace");
|
|
179
|
+
const parsed_args = cli_recursive_parse({ schema: cli_schema, namespace });
|
|
180
|
+
function despatch_sync() {
|
|
181
|
+
cli_recursive_despatch_sync({ schema: cli_schema, namespace, parsed_args });
|
|
182
|
+
}
|
|
183
|
+
async function despatch_async() {
|
|
184
|
+
await cli_recursive_despatch_async({ schema: cli_schema, namespace, parsed_args });
|
|
185
|
+
}
|
|
186
|
+
return { parsed_args, despatch_sync, despatch_async };
|
|
187
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { EMPTY } from "./lib_char_empty.js";
|
|
2
|
+
import { COLON, DASH, SPACE } from "./lib_char_punctuation.js";
|
|
3
|
+
export function datetime_now() {
|
|
4
|
+
return new Date();
|
|
5
|
+
}
|
|
6
|
+
export function datetime_now_minus_days(days) {
|
|
7
|
+
const date = datetime_now();
|
|
8
|
+
date.setDate(date.getDate() - days);
|
|
9
|
+
return date;
|
|
10
|
+
}
|
|
11
|
+
export function datetime_parse(s) {
|
|
12
|
+
return new Date(s);
|
|
13
|
+
}
|
|
14
|
+
export function datetime_parse_timestamp(timestamp) {
|
|
15
|
+
const year = Number.parseInt(timestamp.slice(0, 4), 10);
|
|
16
|
+
const month_index = Number.parseInt(timestamp.slice(4, 6), 10) - 1;
|
|
17
|
+
const day = Number.parseInt(timestamp.slice(6, 8), 10);
|
|
18
|
+
const hours = Number.parseInt(timestamp.slice(8, 10), 10);
|
|
19
|
+
const minutes = Number.parseInt(timestamp.slice(10, 12), 10);
|
|
20
|
+
const seconds = Number.parseInt(timestamp.slice(12, 14), 10);
|
|
21
|
+
const utc = Date.UTC(year, month_index, day, hours, minutes, seconds);
|
|
22
|
+
const date = new Date(utc);
|
|
23
|
+
return date;
|
|
24
|
+
}
|
|
25
|
+
export function datetime_format_utc_iso_ymdthms(date) {
|
|
26
|
+
return date.toISOString().slice(0, 19);
|
|
27
|
+
}
|
|
28
|
+
export function datetime_format_utc_iso_ymd(date) {
|
|
29
|
+
return date.toISOString().slice(0, 10);
|
|
30
|
+
}
|
|
31
|
+
export function datetime_format_utc_iso_hms(date) {
|
|
32
|
+
return date.toISOString().slice(11, 19);
|
|
33
|
+
}
|
|
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;
|
|
40
|
+
}
|
|
41
|
+
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;
|
|
47
|
+
}
|
|
48
|
+
function datetime_localize(date) {
|
|
49
|
+
return new Date(date.valueOf() - date.getTimezoneOffset() * 60_000);
|
|
50
|
+
}
|
|
51
|
+
export function datetime_format_local_iso_ymdthms(date) {
|
|
52
|
+
return datetime_format_utc_iso_ymdthms(datetime_localize(date));
|
|
53
|
+
}
|
|
54
|
+
export function datetime_format_local_iso_ymd_hms(date) {
|
|
55
|
+
return datetime_format_utc_iso_ymdthms(datetime_localize(date)).replace("T", SPACE);
|
|
56
|
+
}
|
|
57
|
+
export function datetime_format_local_iso_ymd(date) {
|
|
58
|
+
return datetime_format_utc_iso_ymd(datetime_localize(date));
|
|
59
|
+
}
|
|
60
|
+
export function datetime_format_local_iso_hms(date) {
|
|
61
|
+
return datetime_format_utc_iso_hms(datetime_localize(date));
|
|
62
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { ansi_grey } from "./lib_ansi.js";
|
|
2
|
+
import { EMPTY } from "./lib_char_empty.js";
|
|
3
|
+
import { enabled_from_env } from "./lib_enabled.js";
|
|
4
|
+
import { inspect_obj_to_string } from "./lib_inspect.js";
|
|
5
|
+
import { stdio_write_stderr_linefeed } from "./lib_stdio_write.js";
|
|
6
|
+
import { tell_debug } from "./lib_tell.js";
|
|
7
|
+
export const debug_channels = {
|
|
8
|
+
api: false,
|
|
9
|
+
backups: false,
|
|
10
|
+
child_input: false,
|
|
11
|
+
child_output: false,
|
|
12
|
+
cleanup: false,
|
|
13
|
+
cli: false,
|
|
14
|
+
cloudflare: false,
|
|
15
|
+
config: false,
|
|
16
|
+
data: false,
|
|
17
|
+
detail: false,
|
|
18
|
+
forwarding: false,
|
|
19
|
+
gcp: false,
|
|
20
|
+
git: false,
|
|
21
|
+
helm: false,
|
|
22
|
+
lines: false,
|
|
23
|
+
llm_inputs: false,
|
|
24
|
+
llm_outputs: false,
|
|
25
|
+
llm_tokens: false,
|
|
26
|
+
llm_tools: false,
|
|
27
|
+
node: false,
|
|
28
|
+
path: false,
|
|
29
|
+
kubectl: false,
|
|
30
|
+
postgresql: false,
|
|
31
|
+
rejects: false,
|
|
32
|
+
retries: false,
|
|
33
|
+
sql: false,
|
|
34
|
+
};
|
|
35
|
+
export function debug_enable_if(channel, enabled) {
|
|
36
|
+
if (enabled && !debug_channels[channel]) {
|
|
37
|
+
debug_channels[channel] = true;
|
|
38
|
+
tell_debug(`Debugging enabled for ‘${channel}’`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
function debug_init() {
|
|
42
|
+
for (const channel in debug_channels) {
|
|
43
|
+
if (enabled_from_env(`lib_debug_${channel}`)) {
|
|
44
|
+
debug_enable_if(channel, true);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
debug_init();
|
|
49
|
+
export function debug_tell_when(when, message) {
|
|
50
|
+
if (when !== undefined && when) {
|
|
51
|
+
tell_debug(message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function debug_inspect(obj, name = null) {
|
|
55
|
+
const prefix = name ? `${name}: ` : EMPTY;
|
|
56
|
+
const message = prefix + inspect_obj_to_string(obj);
|
|
57
|
+
stdio_write_stderr_linefeed(ansi_grey(message));
|
|
58
|
+
}
|
|
59
|
+
export function debug_inspect_if(obj, name = null) {
|
|
60
|
+
if (obj !== undefined) {
|
|
61
|
+
debug_inspect(obj, name);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
export function debug_inspect_when(when, obj, name = null) {
|
|
65
|
+
if (when !== undefined && when) {
|
|
66
|
+
debug_inspect(obj, name);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { LF } from "./lib_char_control.js";
|
|
2
|
+
import { EMPTY } from "./lib_char_empty.js";
|
|
3
|
+
import { datetime_format_local_iso_ymd_hms } from "./lib_datetime.js";
|
|
4
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from "./lib_package.js";
|
|
5
|
+
export function diffdash_add_prefix_or_suffix({ git_message, add_prefix, add_suffix, }) {
|
|
6
|
+
if (!add_prefix && !add_suffix) {
|
|
7
|
+
return git_message;
|
|
8
|
+
}
|
|
9
|
+
const lines = git_message.split(LF);
|
|
10
|
+
if (lines.length === 0) {
|
|
11
|
+
return git_message;
|
|
12
|
+
}
|
|
13
|
+
const formatted_prefix = add_prefix ? `${add_prefix} ` : EMPTY;
|
|
14
|
+
const formatted_suffix = add_suffix ? ` ${add_suffix}` : EMPTY;
|
|
15
|
+
lines[0] = `${formatted_prefix}${lines[0]}${formatted_suffix}`;
|
|
16
|
+
return lines.join(LF);
|
|
17
|
+
}
|
|
18
|
+
export function diffdash_add_footer({ git_message, llm_config }) {
|
|
19
|
+
const { llm_model_name } = llm_config;
|
|
20
|
+
const timestamp = datetime_format_local_iso_ymd_hms(new Date());
|
|
21
|
+
const footer = `Generated by: ${PACKAGE_NAME} v${PACKAGE_VERSION} at ${timestamp} using ${llm_model_name}`;
|
|
22
|
+
return git_message.trim() + LF + LF + footer;
|
|
23
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { cli_boolean, cli_choice_default, cli_make_parser, cli_string } from "./lib_cli.js";
|
|
2
|
+
import { diffdash_llm_model_choices, diffdash_llm_model_default, diffdash_llm_model_fallback, } from "./lib_diffdash_llm.js";
|
|
3
|
+
const diffdash_cli_schema = {
|
|
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
|
+
auto_add: cli_boolean({ help: "automatically stage all changes without confirmation" }),
|
|
8
|
+
auto_commit: cli_boolean({ help: "automatically commit changes without confirmation" }),
|
|
9
|
+
auto_push: cli_boolean({ help: "automatically push changes after commit without confirmation" }),
|
|
10
|
+
disable_add: cli_boolean({ help: "disable adding unstaged changes - exit if no changes staged" }),
|
|
11
|
+
disable_status: cli_boolean({ help: "disable listing the staged files before generating a message" }),
|
|
12
|
+
disable_preview: cli_boolean({ help: "disable previewing the generated message" }),
|
|
13
|
+
disable_commit: cli_boolean({ help: "disable committing changes - exit after generating the message" }),
|
|
14
|
+
disable_push: cli_boolean({ help: "disable pushing changes - exit after making the commit" }),
|
|
15
|
+
push_no_verify: cli_boolean({ help: "bypass git hooks when pushing to Git" }),
|
|
16
|
+
push_force: cli_boolean({ help: "apply force when pushing to Git" }),
|
|
17
|
+
llm_list: cli_boolean({ help: "display a list of available Large Language Models and exit" }),
|
|
18
|
+
llm_compare: cli_boolean({ help: "compare the generated messages from all models - but do not commit" }),
|
|
19
|
+
llm_router: cli_boolean({ help: "prefer to access the LLM via a router rather than direct" }),
|
|
20
|
+
llm_fallback: cli_boolean({ help: `use the fallback model (${diffdash_llm_model_fallback})` }),
|
|
21
|
+
llm_model: cli_choice_default({
|
|
22
|
+
help: `choose the Large Language Model by name (defaults to ${diffdash_llm_model_default})`,
|
|
23
|
+
choices: diffdash_llm_model_choices,
|
|
24
|
+
default: diffdash_llm_model_default,
|
|
25
|
+
}),
|
|
26
|
+
llm_excludes: cli_string({ help: "models to exclude from comparison (comma separated)", metavar: "MODELS" }),
|
|
27
|
+
silent: cli_boolean({ help: "suppress all normal output - errors and aborts still display" }),
|
|
28
|
+
debug_llm_inputs: cli_boolean({ help: "debug inputs (including all prompts) sent to the LLM" }),
|
|
29
|
+
debug_llm_outputs: cli_boolean({ help: "debug outputs received from the LLM" }),
|
|
30
|
+
};
|
|
31
|
+
export const diffdash_cli_parser = cli_make_parser({
|
|
32
|
+
cli_schema: diffdash_cli_schema,
|
|
33
|
+
description: "DiffDash - generate Git commit messages using AI",
|
|
34
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { debug_channels, debug_inspect_when } from "./lib_debug.js";
|
|
2
|
+
import { diffdash_cli_parser } from "./lib_diffdash_cli.js";
|
|
3
|
+
import { diffdash_llm_model_details, diffdash_llm_model_fallback } from "./lib_diffdash_llm.js";
|
|
4
|
+
import { llm_config_get, llm_config_get_all } from "./lib_llm_config.js";
|
|
5
|
+
import { llm_list_models } from "./lib_llm_list.js";
|
|
6
|
+
import { PACKAGE_NAME, PACKAGE_VERSION } from "./lib_package.js";
|
|
7
|
+
import { tell_plain } from "./lib_tell.js";
|
|
8
|
+
export function diffdash_config_get() {
|
|
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;
|
|
11
|
+
if (version) {
|
|
12
|
+
tell_plain(`${PACKAGE_NAME} v${PACKAGE_VERSION}`);
|
|
13
|
+
process.exit(0);
|
|
14
|
+
}
|
|
15
|
+
if (llm_list) {
|
|
16
|
+
llm_list_models({ llm_model_details: diffdash_llm_model_details });
|
|
17
|
+
process.exit(0);
|
|
18
|
+
}
|
|
19
|
+
const llm_model_name = llm_fallback ? diffdash_llm_model_fallback : llm_model;
|
|
20
|
+
const llm_config = llm_config_get({
|
|
21
|
+
llm_model_details: diffdash_llm_model_details,
|
|
22
|
+
llm_model_name,
|
|
23
|
+
llm_router,
|
|
24
|
+
});
|
|
25
|
+
const all_llm_configs = llm_config_get_all({
|
|
26
|
+
llm_model_details: diffdash_llm_model_details,
|
|
27
|
+
llm_router,
|
|
28
|
+
llm_excludes,
|
|
29
|
+
});
|
|
30
|
+
debug_channels.llm_inputs = debug_llm_inputs;
|
|
31
|
+
debug_channels.llm_outputs = debug_llm_outputs;
|
|
32
|
+
const config = {
|
|
33
|
+
add_prefix,
|
|
34
|
+
add_suffix,
|
|
35
|
+
auto_add,
|
|
36
|
+
auto_commit,
|
|
37
|
+
auto_push,
|
|
38
|
+
disable_add,
|
|
39
|
+
disable_commit,
|
|
40
|
+
disable_preview,
|
|
41
|
+
disable_status,
|
|
42
|
+
disable_push,
|
|
43
|
+
push_no_verify,
|
|
44
|
+
push_force,
|
|
45
|
+
llm_compare,
|
|
46
|
+
llm_config,
|
|
47
|
+
all_llm_configs,
|
|
48
|
+
silent,
|
|
49
|
+
};
|
|
50
|
+
debug_inspect_when(debug_channels.config, config, "config");
|
|
51
|
+
return config;
|
|
52
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { env_get_substitute } from "./lib_env.js";
|
|
2
|
+
import { llm_model_get_choices, llm_model_get_details } from "./lib_llm_model.js";
|
|
3
|
+
const model_name_default = "gpt-4.1-mini";
|
|
4
|
+
const model_name_fallback = "claude-3.5-haiku";
|
|
5
|
+
const model_name_options = [
|
|
6
|
+
"claude-3.5-haiku",
|
|
7
|
+
"deepseek-v3",
|
|
8
|
+
"deepseek-r1",
|
|
9
|
+
"devstral-small",
|
|
10
|
+
"ernie-4.5-300b",
|
|
11
|
+
"gemini-2.0-flash",
|
|
12
|
+
"gemini-2.5-flash",
|
|
13
|
+
"gpt-4.1-mini", // the best
|
|
14
|
+
"gpt-4.1-nano",
|
|
15
|
+
"gpt-4o-mini",
|
|
16
|
+
"grok-3-mini",
|
|
17
|
+
"llama-4-maverick",
|
|
18
|
+
"mistral-medium-3",
|
|
19
|
+
"qwen3-235b-a22b",
|
|
20
|
+
];
|
|
21
|
+
export const diffdash_llm_model_details = llm_model_get_details({ llm_model_names: model_name_options });
|
|
22
|
+
export const diffdash_llm_model_choices = llm_model_get_choices(diffdash_llm_model_details);
|
|
23
|
+
export const diffdash_llm_model_default = env_get_substitute("DIFFDASH_LLM_MODEL", model_name_default);
|
|
24
|
+
export const diffdash_llm_model_fallback = model_name_fallback;
|