opencommit 1.1.15 → 1.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +26 -0
  2. package/out/cli.cjs +320 -14
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -84,6 +84,32 @@ To remove description:
84
84
  oc config set description=false
85
85
  ```
86
86
 
87
+ ### Git flags
88
+
89
+ The `opencommit` or `oc` commands can be used in place of the `git commit -m "${generatedMessage}"` command. This means that any regular flags that are used with the `git commit` command will also be applied when using `opencommit` or `oc`.
90
+
91
+ ```sh
92
+ oc --no-verify
93
+ ```
94
+
95
+ is translated to :
96
+
97
+ ```sh
98
+ git commit -m "${generatedMessage}" --no-verify
99
+ ```
100
+
101
+ ### Ignore files
102
+ You can ignore files from submission to OpenAI by creating a `.opencommitignore` file. For example:
103
+
104
+ ```ignorelang
105
+ path/to/large-asset.zip
106
+ **/*.jpg
107
+ ```
108
+
109
+ This is useful for preventing opencommit from uploading artifacts and large files.
110
+
111
+ By default, opencommit ignores files matching: `*-lock.*` and `*.lock`
112
+
87
113
  ## Git hook
88
114
 
89
115
  You can set OpenCommit as Git [`prepare-commit-msg`](https://git-scm.com/docs/githooks#_prepare_commit_msg) hook. Hook integrates with you IDE Source Control and allows you edit the message before commit.
package/out/cli.cjs CHANGED
@@ -1138,6 +1138,287 @@ var require_merge_stream = __commonJS({
1138
1138
  }
1139
1139
  });
1140
1140
 
1141
+ // node_modules/ignore/index.js
1142
+ var require_ignore = __commonJS({
1143
+ "node_modules/ignore/index.js"(exports, module2) {
1144
+ function makeArray(subject) {
1145
+ return Array.isArray(subject) ? subject : [subject];
1146
+ }
1147
+ var EMPTY = "";
1148
+ var SPACE = " ";
1149
+ var ESCAPE = "\\";
1150
+ var REGEX_TEST_BLANK_LINE = /^\s+$/;
1151
+ var REGEX_INVALID_TRAILING_BACKSLASH = /(?:[^\\]|^)\\$/;
1152
+ var REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION = /^\\!/;
1153
+ var REGEX_REPLACE_LEADING_EXCAPED_HASH = /^\\#/;
1154
+ var REGEX_SPLITALL_CRLF = /\r?\n/g;
1155
+ var REGEX_TEST_INVALID_PATH = /^\.*\/|^\.+$/;
1156
+ var SLASH = "/";
1157
+ var TMP_KEY_IGNORE = "node-ignore";
1158
+ if (typeof Symbol !== "undefined") {
1159
+ TMP_KEY_IGNORE = Symbol.for("node-ignore");
1160
+ }
1161
+ var KEY_IGNORE = TMP_KEY_IGNORE;
1162
+ var define = (object, key, value) => Object.defineProperty(object, key, { value });
1163
+ var REGEX_REGEXP_RANGE = /([0-z])-([0-z])/g;
1164
+ var RETURN_FALSE = () => false;
1165
+ var sanitizeRange = (range) => range.replace(
1166
+ REGEX_REGEXP_RANGE,
1167
+ (match, from, to) => from.charCodeAt(0) <= to.charCodeAt(0) ? match : EMPTY
1168
+ );
1169
+ var cleanRangeBackSlash = (slashes) => {
1170
+ const { length } = slashes;
1171
+ return slashes.slice(0, length - length % 2);
1172
+ };
1173
+ var REPLACERS = [
1174
+ [
1175
+ /\\?\s+$/,
1176
+ (match) => match.indexOf("\\") === 0 ? SPACE : EMPTY
1177
+ ],
1178
+ [
1179
+ /\\\s/g,
1180
+ () => SPACE
1181
+ ],
1182
+ [
1183
+ /[\\$.|*+(){^]/g,
1184
+ (match) => `\\${match}`
1185
+ ],
1186
+ [
1187
+ /(?!\\)\?/g,
1188
+ () => "[^/]"
1189
+ ],
1190
+ [
1191
+ /^\//,
1192
+ () => "^"
1193
+ ],
1194
+ [
1195
+ /\//g,
1196
+ () => "\\/"
1197
+ ],
1198
+ [
1199
+ /^\^*\\\*\\\*\\\//,
1200
+ () => "^(?:.*\\/)?"
1201
+ ],
1202
+ [
1203
+ /^(?=[^^])/,
1204
+ function startingReplacer() {
1205
+ return !/\/(?!$)/.test(this) ? "(?:^|\\/)" : "^";
1206
+ }
1207
+ ],
1208
+ [
1209
+ /\\\/\\\*\\\*(?=\\\/|$)/g,
1210
+ (_6, index, str) => index + 6 < str.length ? "(?:\\/[^\\/]+)*" : "\\/.+"
1211
+ ],
1212
+ [
1213
+ /(^|[^\\]+)(\\\*)+(?=.+)/g,
1214
+ (_6, p1, p22) => {
1215
+ const unescaped = p22.replace(/\\\*/g, "[^\\/]*");
1216
+ return p1 + unescaped;
1217
+ }
1218
+ ],
1219
+ [
1220
+ /\\\\\\(?=[$.|*+(){^])/g,
1221
+ () => ESCAPE
1222
+ ],
1223
+ [
1224
+ /\\\\/g,
1225
+ () => ESCAPE
1226
+ ],
1227
+ [
1228
+ /(\\)?\[([^\]/]*?)(\\*)($|\])/g,
1229
+ (match, leadEscape, range, endEscape, close) => leadEscape === ESCAPE ? `\\[${range}${cleanRangeBackSlash(endEscape)}${close}` : close === "]" ? endEscape.length % 2 === 0 ? `[${sanitizeRange(range)}${endEscape}]` : "[]" : "[]"
1230
+ ],
1231
+ [
1232
+ /(?:[^*])$/,
1233
+ (match) => /\/$/.test(match) ? `${match}$` : `${match}(?=$|\\/$)`
1234
+ ],
1235
+ [
1236
+ /(\^|\\\/)?\\\*$/,
1237
+ (_6, p1) => {
1238
+ const prefix = p1 ? `${p1}[^/]+` : "[^/]*";
1239
+ return `${prefix}(?=$|\\/$)`;
1240
+ }
1241
+ ]
1242
+ ];
1243
+ var regexCache = /* @__PURE__ */ Object.create(null);
1244
+ var makeRegex = (pattern, ignoreCase) => {
1245
+ let source = regexCache[pattern];
1246
+ if (!source) {
1247
+ source = REPLACERS.reduce(
1248
+ (prev, current) => prev.replace(current[0], current[1].bind(pattern)),
1249
+ pattern
1250
+ );
1251
+ regexCache[pattern] = source;
1252
+ }
1253
+ return ignoreCase ? new RegExp(source, "i") : new RegExp(source);
1254
+ };
1255
+ var isString2 = (subject) => typeof subject === "string";
1256
+ var checkPattern = (pattern) => pattern && isString2(pattern) && !REGEX_TEST_BLANK_LINE.test(pattern) && !REGEX_INVALID_TRAILING_BACKSLASH.test(pattern) && pattern.indexOf("#") !== 0;
1257
+ var splitPattern = (pattern) => pattern.split(REGEX_SPLITALL_CRLF);
1258
+ var IgnoreRule = class {
1259
+ constructor(origin, pattern, negative, regex) {
1260
+ this.origin = origin;
1261
+ this.pattern = pattern;
1262
+ this.negative = negative;
1263
+ this.regex = regex;
1264
+ }
1265
+ };
1266
+ var createRule = (pattern, ignoreCase) => {
1267
+ const origin = pattern;
1268
+ let negative = false;
1269
+ if (pattern.indexOf("!") === 0) {
1270
+ negative = true;
1271
+ pattern = pattern.substr(1);
1272
+ }
1273
+ pattern = pattern.replace(REGEX_REPLACE_LEADING_EXCAPED_EXCLAMATION, "!").replace(REGEX_REPLACE_LEADING_EXCAPED_HASH, "#");
1274
+ const regex = makeRegex(pattern, ignoreCase);
1275
+ return new IgnoreRule(
1276
+ origin,
1277
+ pattern,
1278
+ negative,
1279
+ regex
1280
+ );
1281
+ };
1282
+ var throwError = (message, Ctor) => {
1283
+ throw new Ctor(message);
1284
+ };
1285
+ var checkPath = (path4, originalPath, doThrow) => {
1286
+ if (!isString2(path4)) {
1287
+ return doThrow(
1288
+ `path must be a string, but got \`${originalPath}\``,
1289
+ TypeError
1290
+ );
1291
+ }
1292
+ if (!path4) {
1293
+ return doThrow(`path must not be empty`, TypeError);
1294
+ }
1295
+ if (checkPath.isNotRelative(path4)) {
1296
+ const r2 = "`path.relative()`d";
1297
+ return doThrow(
1298
+ `path should be a ${r2} string, but got "${originalPath}"`,
1299
+ RangeError
1300
+ );
1301
+ }
1302
+ return true;
1303
+ };
1304
+ var isNotRelative = (path4) => REGEX_TEST_INVALID_PATH.test(path4);
1305
+ checkPath.isNotRelative = isNotRelative;
1306
+ checkPath.convert = (p4) => p4;
1307
+ var Ignore2 = class {
1308
+ constructor({
1309
+ ignorecase = true,
1310
+ ignoreCase = ignorecase,
1311
+ allowRelativePaths = false
1312
+ } = {}) {
1313
+ define(this, KEY_IGNORE, true);
1314
+ this._rules = [];
1315
+ this._ignoreCase = ignoreCase;
1316
+ this._allowRelativePaths = allowRelativePaths;
1317
+ this._initCache();
1318
+ }
1319
+ _initCache() {
1320
+ this._ignoreCache = /* @__PURE__ */ Object.create(null);
1321
+ this._testCache = /* @__PURE__ */ Object.create(null);
1322
+ }
1323
+ _addPattern(pattern) {
1324
+ if (pattern && pattern[KEY_IGNORE]) {
1325
+ this._rules = this._rules.concat(pattern._rules);
1326
+ this._added = true;
1327
+ return;
1328
+ }
1329
+ if (checkPattern(pattern)) {
1330
+ const rule = createRule(pattern, this._ignoreCase);
1331
+ this._added = true;
1332
+ this._rules.push(rule);
1333
+ }
1334
+ }
1335
+ add(pattern) {
1336
+ this._added = false;
1337
+ makeArray(
1338
+ isString2(pattern) ? splitPattern(pattern) : pattern
1339
+ ).forEach(this._addPattern, this);
1340
+ if (this._added) {
1341
+ this._initCache();
1342
+ }
1343
+ return this;
1344
+ }
1345
+ addPattern(pattern) {
1346
+ return this.add(pattern);
1347
+ }
1348
+ _testOne(path4, checkUnignored) {
1349
+ let ignored = false;
1350
+ let unignored = false;
1351
+ this._rules.forEach((rule) => {
1352
+ const { negative } = rule;
1353
+ if (unignored === negative && ignored !== unignored || negative && !ignored && !unignored && !checkUnignored) {
1354
+ return;
1355
+ }
1356
+ const matched = rule.regex.test(path4);
1357
+ if (matched) {
1358
+ ignored = !negative;
1359
+ unignored = negative;
1360
+ }
1361
+ });
1362
+ return {
1363
+ ignored,
1364
+ unignored
1365
+ };
1366
+ }
1367
+ _test(originalPath, cache, checkUnignored, slices) {
1368
+ const path4 = originalPath && checkPath.convert(originalPath);
1369
+ checkPath(
1370
+ path4,
1371
+ originalPath,
1372
+ this._allowRelativePaths ? RETURN_FALSE : throwError
1373
+ );
1374
+ return this._t(path4, cache, checkUnignored, slices);
1375
+ }
1376
+ _t(path4, cache, checkUnignored, slices) {
1377
+ if (path4 in cache) {
1378
+ return cache[path4];
1379
+ }
1380
+ if (!slices) {
1381
+ slices = path4.split(SLASH);
1382
+ }
1383
+ slices.pop();
1384
+ if (!slices.length) {
1385
+ return cache[path4] = this._testOne(path4, checkUnignored);
1386
+ }
1387
+ const parent = this._t(
1388
+ slices.join(SLASH) + SLASH,
1389
+ cache,
1390
+ checkUnignored,
1391
+ slices
1392
+ );
1393
+ return cache[path4] = parent.ignored ? parent : this._testOne(path4, checkUnignored);
1394
+ }
1395
+ ignores(path4) {
1396
+ return this._test(path4, this._ignoreCache, false).ignored;
1397
+ }
1398
+ createFilter() {
1399
+ return (path4) => !this.ignores(path4);
1400
+ }
1401
+ filter(paths) {
1402
+ return makeArray(paths).filter(this.createFilter());
1403
+ }
1404
+ test(path4) {
1405
+ return this._test(path4, this._testCache, true);
1406
+ }
1407
+ };
1408
+ var factory = (options) => new Ignore2(options);
1409
+ var isPathValid = (path4) => checkPath(path4 && checkPath.convert(path4), path4, RETURN_FALSE);
1410
+ factory.isPathValid = isPathValid;
1411
+ factory.default = factory;
1412
+ module2.exports = factory;
1413
+ if (typeof process !== "undefined" && (process.env && process.env.IGNORE_TEST_WIN32 || process.platform === "win32")) {
1414
+ const makePosix = (str) => /^\\\\\?\\/.test(str) || /["<>|\u0000-\u001F]+/u.test(str) ? str : str.replace(/\\/g, "/");
1415
+ checkPath.convert = makePosix;
1416
+ const REGIX_IS_WINDOWS_PATH_ABSOLUTE = /^[a-z]:\//i;
1417
+ checkPath.isNotRelative = (path4) => REGIX_IS_WINDOWS_PATH_ABSOLUTE.test(path4) || isNotRelative(path4);
1418
+ }
1419
+ }
1420
+ });
1421
+
1141
1422
  // node_modules/openai/node_modules/axios/lib/helpers/bind.js
1142
1423
  var require_bind = __commonJS({
1143
1424
  "node_modules/openai/node_modules/axios/lib/helpers/bind.js"(exports, module2) {
@@ -15482,7 +15763,7 @@ function G3(t, e2) {
15482
15763
  // package.json
15483
15764
  var package_default = {
15484
15765
  name: "opencommit",
15485
- version: "1.1.14",
15766
+ version: "1.1.16",
15486
15767
  description: "GPT CLI to auto-generate impressive commits in 1 second. Killing lame commits with AI \u{1F92F}\u{1F52B}",
15487
15768
  keywords: [
15488
15769
  "git",
@@ -15546,6 +15827,7 @@ var package_default = {
15546
15827
  chalk: "^5.2.0",
15547
15828
  cleye: "^1.3.2",
15548
15829
  execa: "^7.0.0",
15830
+ ignore: "^5.2.4",
15549
15831
  ini: "^3.0.1",
15550
15832
  inquirer: "^9.1.4",
15551
15833
  openai: "^3.2.1"
@@ -17671,6 +17953,8 @@ function execa(file, args, options) {
17671
17953
  }
17672
17954
 
17673
17955
  // src/utils/git.ts
17956
+ var import_fs2 = require("fs");
17957
+ var import_ignore = __toESM(require_ignore(), 1);
17674
17958
  var assertGitRepo = async () => {
17675
17959
  try {
17676
17960
  await execa("git", ["rev-parse"]);
@@ -17678,6 +17962,14 @@ var assertGitRepo = async () => {
17678
17962
  throw new Error(error);
17679
17963
  }
17680
17964
  };
17965
+ var getOpenCommitIgnore = () => {
17966
+ const ig = (0, import_ignore.default)();
17967
+ try {
17968
+ ig.add((0, import_fs2.readFileSync)(".opencommitignore").toString().split("\n"));
17969
+ } catch (e2) {
17970
+ }
17971
+ return ig;
17972
+ };
17681
17973
  var getStagedFiles = async () => {
17682
17974
  const { stdout: files } = await execa("git", [
17683
17975
  "diff",
@@ -17686,7 +17978,12 @@ var getStagedFiles = async () => {
17686
17978
  ]);
17687
17979
  if (!files)
17688
17980
  return [];
17689
- return files.split("\n").sort();
17981
+ const filesList = files.split("\n");
17982
+ const ig = getOpenCommitIgnore();
17983
+ const allowedFiles = filesList.filter((file) => !ig.ignores(file));
17984
+ if (!allowedFiles)
17985
+ return [];
17986
+ return allowedFiles.sort();
17690
17987
  };
17691
17988
  var getChangedFiles = async () => {
17692
17989
  const { stdout: modified } = await execa("git", ["ls-files", "--modified"]);
@@ -17724,17 +18021,18 @@ ${lockFiles.join(
17724
18021
  const { stdout: diff } = await execa("git", [
17725
18022
  "diff",
17726
18023
  "--staged",
18024
+ "--",
17727
18025
  ...filesWithoutLocks
17728
18026
  ]);
17729
18027
  return diff;
17730
18028
  };
17731
18029
 
17732
18030
  // src/commands/githook.ts
17733
- var import_fs2 = require("fs");
18031
+ var import_fs3 = require("fs");
17734
18032
  var HOOK_NAME = "prepare-commit-msg";
17735
18033
  var SYMLINK_URL = `.git/hooks/${HOOK_NAME}`;
17736
18034
  var isHookCalled = process.argv[1].endsWith(`/${SYMLINK_URL}`);
17737
- var isHookExists = (0, import_fs2.existsSync)(SYMLINK_URL);
18035
+ var isHookExists = (0, import_fs3.existsSync)(SYMLINK_URL);
17738
18036
  var hookCommand = G3(
17739
18037
  {
17740
18038
  name: "hook" /* hook */,
@@ -20687,7 +20985,7 @@ var translation = i18n[config2?.language || "en"];
20687
20985
  var INIT_MESSAGES_PROMPT = [
20688
20986
  {
20689
20987
  role: import_openai2.ChatCompletionRequestMessageRoleEnum.System,
20690
- content: `You are to act as the author of a commit message in git. Your mission is to create clean and comprehensive commit messages in the conventional commit convention. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. ${config2?.emoji ? "Use Gitmoji convention to preface the commit" : "Do not preface the commit with anything"}, use the present tense. ${config2?.description ? `Add a short description of what commit is about after the commit message. Don't start it with "This commit", just describe the changes.` : "Don't add any descriptions to the commit, only commit message."} Use ${translation.localLanguage} to answer.}`
20988
+ content: `You are to act as the author of a commit message in git. Your mission is to create clean and comprehensive commit messages in the conventional commit convention. I'll send you an output of 'git diff --staged' command, and you convert it into a commit message. ${config2?.emoji ? "Use Gitmoji convention to preface the commit" : "Do not preface the commit with anything"}, use the present tense. ${config2?.description ? `Add a short description of what commit is about after the commit message. Don't start it with "This commit", just describe the changes.` : "Don't add any descriptions to the commit, only commit message."} Use ${translation.localLanguage} to answer.`
20691
20989
  },
20692
20990
  {
20693
20991
  role: import_openai2.ChatCompletionRequestMessageRoleEnum.User,
@@ -20842,7 +21140,7 @@ var trytm = async (promise) => {
20842
21140
  };
20843
21141
 
20844
21142
  // src/commands/commit.ts
20845
- var generateCommitMessageFromGitDiff = async (diff) => {
21143
+ var generateCommitMessageFromGitDiff = async (diff, extraArgs2) => {
20846
21144
  await assertGitRepo();
20847
21145
  const commitSpinner = le();
20848
21146
  commitSpinner.start("Generating the commit message");
@@ -20867,7 +21165,12 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
20867
21165
  message: "Confirm the commit message"
20868
21166
  });
20869
21167
  if (isCommitConfirmedByUser && !eD2(isCommitConfirmedByUser)) {
20870
- const { stdout } = await execa("git", ["commit", "-m", commitMessage]);
21168
+ const { stdout } = await execa("git", [
21169
+ "commit",
21170
+ "-m",
21171
+ commitMessage,
21172
+ ...extraArgs2
21173
+ ]);
20871
21174
  ce(`${source_default.green("\u2714")} successfully committed`);
20872
21175
  ce(stdout);
20873
21176
  const isPushConfirmedByUser = await Q3({
@@ -20884,7 +21187,7 @@ ${source_default.grey("\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2014\u2
20884
21187
  } else
20885
21188
  ce(`${source_default.gray("\u2716")} process cancelled`);
20886
21189
  };
20887
- async function commit(isStageAllFlag = false) {
21190
+ async function commit(extraArgs2 = [], isStageAllFlag = false) {
20888
21191
  if (isStageAllFlag) {
20889
21192
  const changedFiles2 = await getChangedFiles();
20890
21193
  if (changedFiles2)
@@ -20913,7 +21216,7 @@ async function commit(isStageAllFlag = false) {
20913
21216
  message: "Do you want to stage all files and generate commit message?"
20914
21217
  });
20915
21218
  if (isStageAllAndCommitConfirmedByUser && !eD2(isStageAllAndCommitConfirmedByUser)) {
20916
- await commit(true);
21219
+ await commit(extraArgs2, true);
20917
21220
  process.exit(1);
20918
21221
  }
20919
21222
  if (stagedFiles.length === 0 && changedFiles.length > 0) {
@@ -20928,7 +21231,7 @@ async function commit(isStageAllFlag = false) {
20928
21231
  process.exit(1);
20929
21232
  await gitAdd({ files });
20930
21233
  }
20931
- await commit(false);
21234
+ await commit(extraArgs2, false);
20932
21235
  process.exit(1);
20933
21236
  }
20934
21237
  stagedFilesSpinner.stop(
@@ -20936,7 +21239,10 @@ async function commit(isStageAllFlag = false) {
20936
21239
  ${stagedFiles.map((file) => ` ${file}`).join("\n")}`
20937
21240
  );
20938
21241
  const [, generateCommitError] = await trytm(
20939
- generateCommitMessageFromGitDiff(await getDiff({ files: stagedFiles }))
21242
+ generateCommitMessageFromGitDiff(
21243
+ await getDiff({ files: stagedFiles }),
21244
+ extraArgs2
21245
+ )
20940
21246
  );
20941
21247
  if (generateCommitError) {
20942
21248
  ce(`${source_default.red("\u2716")} ${generateCommitError}`);
@@ -20946,7 +21252,7 @@ ${stagedFiles.map((file) => ` ${file}`).join("\n")}`
20946
21252
  }
20947
21253
 
20948
21254
  // src/cli.ts
20949
- var rawArgv = process.argv.slice(2);
21255
+ var extraArgs = process.argv.slice(2);
20950
21256
  Z2(
20951
21257
  {
20952
21258
  version: package_default.version,
@@ -20960,10 +21266,10 @@ Z2(
20960
21266
  if (isHookCalled) {
20961
21267
  prepareCommitMessageHook();
20962
21268
  } else {
20963
- commit();
21269
+ commit(extraArgs);
20964
21270
  }
20965
21271
  },
20966
- rawArgv
21272
+ extraArgs
20967
21273
  );
20968
21274
  /*!
20969
21275
  * mime-db
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencommit",
3
- "version": "1.1.15",
3
+ "version": "1.1.17",
4
4
  "description": "GPT CLI to auto-generate impressive commits in 1 second. Killing lame commits with AI 🤯🔫",
5
5
  "keywords": [
6
6
  "git",
@@ -64,6 +64,7 @@
64
64
  "chalk": "^5.2.0",
65
65
  "cleye": "^1.3.2",
66
66
  "execa": "^7.0.0",
67
+ "ignore": "^5.2.4",
67
68
  "ini": "^3.0.1",
68
69
  "inquirer": "^9.1.4",
69
70
  "openai": "^3.2.1"