@quikcommit/cli 12.0.0 → 13.0.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.
Files changed (2) hide show
  1. package/dist/index.js +181 -38
  2. package/package.json +4 -4
package/dist/index.js CHANGED
@@ -89,20 +89,59 @@ function slugifyFilename(path) {
89
89
  const noExt = basename.replace(/\.[^.]+$/, "");
90
90
  return noExt.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
91
91
  }
92
+ function isTestPath(f) {
93
+ return /\.(test|spec)\.[^.]+$/i.test(f) || /^tests?\//i.test(f);
94
+ }
92
95
  function pickBestFile(files) {
93
96
  const codeFiles = files.filter((f) => !NON_CODE_PATTERNS.some((rx) => rx.test(f)));
94
- return codeFiles[0] ?? files[0] ?? "";
97
+ const srcDirs = codeFiles.filter((f) => /^(src|lib|app|packages)\//.test(f));
98
+ const nonTestSrc = srcDirs.filter((f) => !isTestPath(f));
99
+ return nonTestSrc[0] ?? srcDirs[0] ?? codeFiles[0] ?? files[0] ?? "";
100
+ }
101
+ function inferTypeFromFiles(files) {
102
+ const isNonCode = (f) => NON_CODE_PATTERNS.some((rx) => rx.test(f));
103
+ let srcCount = 0, testCount = 0, docCount = 0, ciCount = 0;
104
+ for (const f of files) {
105
+ if (/^\.github\//i.test(f)) {
106
+ ciCount++;
107
+ } else if (isTestPath(f)) {
108
+ testCount++;
109
+ } else if (/^docs?\//i.test(f) || /\.md$/i.test(f) || /^readme/i.test(f)) {
110
+ docCount++;
111
+ } else if (!isNonCode(f)) {
112
+ srcCount++;
113
+ }
114
+ }
115
+ if (srcCount === 0 && testCount > 0 && docCount === 0 && ciCount === 0)
116
+ return "test";
117
+ if (srcCount === 0 && docCount > 0 && testCount === 0 && ciCount === 0)
118
+ return "docs";
119
+ if (srcCount === 0 && ciCount > 0 && testCount === 0 && docCount === 0)
120
+ return "ci";
121
+ if (srcCount > 10)
122
+ return "refactor";
123
+ if (srcCount > 0)
124
+ return "feat";
125
+ if (testCount >= docCount && testCount >= ciCount)
126
+ return "test";
127
+ if (docCount >= ciCount)
128
+ return "docs";
129
+ if (ciCount > 0)
130
+ return "ci";
131
+ return "chore";
95
132
  }
96
133
  function deterministicBranchName(opts) {
97
134
  const files = opts.files ?? [];
98
- const codeFiles = files.filter((f) => !NON_CODE_PATTERNS.some((rx) => rx.test(f)));
99
- const haystack = `${opts.description ?? ""} ${(codeFiles.length > 0 ? codeFiles : files).join(" ")}`;
100
135
  let type = "chore";
101
- for (const [rx, t] of TYPE_HINTS) {
102
- if (rx.test(haystack)) {
103
- type = t;
104
- break;
136
+ if (opts.description) {
137
+ for (const [rx, t] of TYPE_HINTS) {
138
+ if (rx.test(opts.description)) {
139
+ type = t;
140
+ break;
141
+ }
105
142
  }
143
+ } else if (files.length > 0) {
144
+ type = inferTypeFromFiles(files);
106
145
  }
107
146
  let slug;
108
147
  if (opts.description) {
@@ -127,16 +166,16 @@ var init_branch = __esm({
127
166
  "../shared/dist/branch.js"() {
128
167
  "use strict";
129
168
  BRANCH_NAME_RX = /^(feat|fix|refactor|perf|docs|test|chore|ci)\/[a-z0-9][a-z0-9-]{0,51}$/;
130
- PROTECTED_BRANCH_RX = /(^|[/-])(main|master|develop|trunk|release)([/-]|$)/i;
169
+ PROTECTED_BRANCH_RX = /(^|\/)(main|master|develop|trunk|release)(\/|$)/i;
131
170
  MAX_BRANCH_NAME_LENGTH = 60;
132
171
  TYPE_HINTS = [
133
- [/\btest|spec\b/i, "test"],
134
- [/\bdocs?\b|readme|\.md$/i, "docs"],
135
- [/\bperf|benchmark/i, "perf"],
172
+ [/\b(ci|workflow)\b|github\/actions/i, "ci"],
173
+ [/\b(perf|benchmark)\b/i, "perf"],
136
174
  [/\brefactor\b/i, "refactor"],
137
- [/\bci|workflow|github\/actions/i, "ci"],
138
- [/\bfix|bug|issue/i, "fix"],
139
- [/\bfeat|add|new\b/i, "feat"]
175
+ [/\b(fix|bug|issue|patch)\b/i, "fix"],
176
+ [/\b(feat|add|new)\b/i, "feat"],
177
+ [/\b(docs?)\b|\breadme\b/i, "docs"],
178
+ [/\b(test|spec)\b/i, "test"]
140
179
  ];
141
180
  NON_CODE_PATTERNS = [
142
181
  /^docs?\//i,
@@ -150,6 +189,8 @@ var init_branch = __esm({
150
189
  /\.json$/,
151
190
  /\.toml$/,
152
191
  /\/\.?config\b/i,
192
+ /\.config\.[a-z]+$/i,
193
+ // root-level config: eslint.config.mjs, vitest.config.ts
153
194
  /^\.github\//,
154
195
  /^\.vscode\//
155
196
  ];
@@ -400,7 +441,8 @@ var init_api = __esm({
400
441
  "Content-Type": "application/json",
401
442
  Authorization: `Bearer ${this.apiKey}`
402
443
  },
403
- body: JSON.stringify(body)
444
+ body: JSON.stringify(body),
445
+ signal: AbortSignal.timeout(3e4)
404
446
  });
405
447
  if (!res.ok) {
406
448
  if (res.status === 413) {
@@ -476,7 +518,8 @@ var init_api = __esm({
476
518
  "Content-Type": "application/json",
477
519
  Authorization: `Bearer ${this.apiKey}`
478
520
  },
479
- body: options?.body
521
+ body: options?.body,
522
+ signal: AbortSignal.timeout(3e4)
480
523
  });
481
524
  if (!res.ok) {
482
525
  const err = await res.json().catch(() => ({ error: res.statusText }));
@@ -7868,6 +7911,7 @@ __export(git_exports, {
7868
7911
  createBranch: () => createBranch,
7869
7912
  deleteBranch: () => deleteBranch,
7870
7913
  getAllChangedFiles: () => getAllChangedFiles,
7914
+ getAllChangedFilesWithStatus: () => getAllChangedFilesWithStatus,
7871
7915
  getBranchCommits: () => getBranchCommits,
7872
7916
  getChangedFilesSince: () => getChangedFilesSince,
7873
7917
  getCommitHash: () => getCommitHash,
@@ -7890,6 +7934,7 @@ __export(git_exports, {
7890
7934
  getStagedFiles: () => getStagedFiles,
7891
7935
  getUnstagedFiles: () => getUnstagedFiles,
7892
7936
  getUpstreamRef: () => getUpstreamRef,
7937
+ getWorkingDiffStat: () => getWorkingDiffStat,
7893
7938
  getWorkingTreeDiff: () => getWorkingTreeDiff,
7894
7939
  gitCommit: () => gitCommit,
7895
7940
  gitPush: () => gitPush,
@@ -7945,7 +7990,8 @@ function getStagedDiff(excludes = []) {
7945
7990
  }
7946
7991
  function getStagedFiles() {
7947
7992
  return (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
7948
- encoding: "utf-8"
7993
+ encoding: "utf-8",
7994
+ maxBuffer: 10 * 1024 * 1024
7949
7995
  });
7950
7996
  }
7951
7997
  function getWorkingTreeDiff(excludes = []) {
@@ -7968,9 +8014,36 @@ function getAllChangedFiles() {
7968
8014
  });
7969
8015
  return output.trim().split("\n").filter(Boolean).map((line) => line.slice(3)).join("\n");
7970
8016
  }
8017
+ function getAllChangedFilesWithStatus() {
8018
+ const output = (0, import_child_process2.execFileSync)("git", ["status", "--porcelain"], {
8019
+ encoding: "utf-8"
8020
+ });
8021
+ return output.trim().split("\n").filter(Boolean).map((line) => {
8022
+ const xy = line.slice(0, 2).trim();
8023
+ const filepath = line.slice(3);
8024
+ let status = "M";
8025
+ if (xy === "??") status = "A";
8026
+ else if (xy.includes("D")) status = "D";
8027
+ else if (xy.includes("A")) status = "A";
8028
+ else if (xy.includes("R")) status = "R";
8029
+ return `${status} ${filepath}`;
8030
+ }).join("\n");
8031
+ }
8032
+ function getWorkingDiffStat(staged) {
8033
+ const args = staged ? ["diff", "--cached", "--stat=120"] : ["diff", "HEAD", "--stat=120"];
8034
+ try {
8035
+ return (0, import_child_process2.execFileSync)("git", args, {
8036
+ encoding: "utf-8",
8037
+ maxBuffer: 1024 * 1024
8038
+ }).trim();
8039
+ } catch {
8040
+ return "";
8041
+ }
8042
+ }
7971
8043
  function hasStagedChanges() {
7972
8044
  const output = (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
7973
- encoding: "utf-8"
8045
+ encoding: "utf-8",
8046
+ maxBuffer: 10 * 1024 * 1024
7974
8047
  });
7975
8048
  return output.trim().length > 0;
7976
8049
  }
@@ -8839,7 +8912,8 @@ function isMinified(content) {
8839
8912
  (l) => (l.startsWith("+") || l.startsWith("-")) && !l.startsWith("+++") && !l.startsWith("---")
8840
8913
  );
8841
8914
  if (lines.length === 0) return false;
8842
- return lines.some((l) => l.length > 500);
8915
+ const longLines = lines.filter((l) => l.length > 500).length;
8916
+ return longLines > lines.length / 2;
8843
8917
  }
8844
8918
  function preprocessDiff(diff) {
8845
8919
  const files = parseDiffIntoFiles(diff);
@@ -9564,7 +9638,9 @@ function sanitizeBranchName(input) {
9564
9638
  function ensureUniqueName(name, exists) {
9565
9639
  if (!exists(name)) return name;
9566
9640
  for (let i = 2; i <= 100; i++) {
9567
- const candidate = `${name}-${i}`;
9641
+ const suffix = `-${i}`;
9642
+ const base = name.length + suffix.length > MAX_BRANCH_NAME_LENGTH ? name.slice(0, MAX_BRANCH_NAME_LENGTH - suffix.length) : name;
9643
+ const candidate = `${base}${suffix}`;
9568
9644
  if (!exists(candidate)) return candidate;
9569
9645
  }
9570
9646
  throw new Error(`Could not find a unique name for ${name} after 100 attempts`);
@@ -10835,6 +10911,8 @@ async function generateLocalBranchName(opts) {
10835
10911
  sections.push("Generate a git branch name in the format <type>/<kebab-case-slug>.");
10836
10912
  sections.push("Type must be one of: feat, fix, refactor, perf, docs, test, chore, ci.");
10837
10913
  sections.push("Slug: 2-5 words, lowercase, hyphen-separated, max 55 chars.");
10914
+ sections.push("For LARGE changesets: identify the dominant THEME (migration, refactor, feature). Name for the theme, not a single file.");
10915
+ sections.push("Look for DELETED + ADDED files as signals of migration.");
10838
10916
  sections.push("Output ONLY the branch name on a single line. No explanation.");
10839
10917
  sections.push("");
10840
10918
  if (opts.description) {
@@ -10843,9 +10921,22 @@ async function generateLocalBranchName(opts) {
10843
10921
  } else if (opts.recentCommits && opts.recentCommits.length > 0) {
10844
10922
  sections.push("RECENT COMMITS:");
10845
10923
  for (const c of opts.recentCommits) sections.push(`- ${c}`);
10846
- } else if (opts.diff) {
10847
- sections.push("DIFF:");
10848
- sections.push(opts.diff.slice(0, 3e4));
10924
+ } else {
10925
+ if (opts.changes) {
10926
+ sections.push("FILES CHANGED (PRIMARY signal \u2014 complete list):");
10927
+ sections.push(opts.changes.slice(0, 8e3));
10928
+ sections.push("");
10929
+ }
10930
+ if (opts.diffStat) {
10931
+ sections.push("CHANGE MAGNITUDE:");
10932
+ sections.push(opts.diffStat.slice(0, 6e3));
10933
+ sections.push("");
10934
+ }
10935
+ if (opts.diff) {
10936
+ const budget = opts.diffStat ? 8e3 : 3e4;
10937
+ sections.push("DIFF (supplementary, may be truncated):");
10938
+ sections.push(opts.diff.slice(0, budget));
10939
+ }
10849
10940
  }
10850
10941
  const userContent = sections.join("\n");
10851
10942
  const model = opts.model ?? local.model;
@@ -10883,6 +10974,7 @@ async function generateLocalBranchName(opts) {
10883
10974
  body = {
10884
10975
  diff: opts.diff,
10885
10976
  changes: opts.changes,
10977
+ diff_stat: opts.diffStat,
10886
10978
  recent_commits: opts.recentCommits,
10887
10979
  description: opts.description,
10888
10980
  model,
@@ -10952,6 +11044,7 @@ async function runLocalBranch(opts) {
10952
11044
  description: opts.description,
10953
11045
  diff: opts.diff,
10954
11046
  changes: opts.changes,
11047
+ diffStat: opts.diffStat,
10955
11048
  recentCommits: opts.recentCommits,
10956
11049
  model: opts.model,
10957
11050
  rules: opts.rules
@@ -11193,8 +11286,11 @@ async function runBranch(opts) {
11193
11286
  "No staged changes detected. Stage with `git add`, or provide -m '<description>'."
11194
11287
  );
11195
11288
  }
11196
- payload.diff = getStagedDiff(config2.excludes ?? []);
11289
+ const rawDiff = getStagedDiff(config2.excludes ?? []);
11290
+ const processed = preprocessDiff(rawDiff).processedDiff;
11291
+ payload.diff = processed.slice(0, 6e4) || void 0;
11197
11292
  payload.changes = getStagedFiles();
11293
+ payload.diff_stat = getWorkingDiffStat(true) || void 0;
11198
11294
  }
11199
11295
  const apiKey = opts.apiKey ?? getApiKey();
11200
11296
  if (!apiKey) {
@@ -11204,6 +11300,7 @@ async function runBranch(opts) {
11204
11300
  description: opts.message,
11205
11301
  diff: opts.message ? void 0 : payload.diff,
11206
11302
  changes: opts.message ? void 0 : payload.changes,
11303
+ diffStat: opts.message ? void 0 : payload.diff_stat,
11207
11304
  recentCommits: payload.recent_commits,
11208
11305
  model: opts.model,
11209
11306
  noSwitch: opts.noSwitch,
@@ -11231,6 +11328,10 @@ async function runBranch(opts) {
11231
11328
  try {
11232
11329
  const client = new ApiClient({ apiKey });
11233
11330
  result = await client.generateBranchName(payload);
11331
+ } catch {
11332
+ const filesArr = payload.changes?.split("\n").filter(Boolean) ?? [];
11333
+ result = deterministicBranchName({ files: filesArr, description: payload.description });
11334
+ log.dim("(used deterministic fallback; API generation failed)");
11234
11335
  } finally {
11235
11336
  spinner.stop();
11236
11337
  }
@@ -11260,6 +11361,7 @@ var init_branch2 = __esm({
11260
11361
  init_branch_rescue();
11261
11362
  init_protected_branch_guard();
11262
11363
  init_git();
11364
+ init_smart_diff();
11263
11365
  init_branch_name();
11264
11366
  init_commit_helpers();
11265
11367
  init_ui();
@@ -11645,14 +11747,21 @@ async function runBranchGuard(args, log) {
11645
11747
  return { action: "continue" };
11646
11748
  }
11647
11749
  let stagedDiff = "";
11648
- let stagedChanges = "";
11750
+ let plainFiles = "";
11751
+ let changesForAI = "";
11752
+ let diffStat = "";
11649
11753
  if (state.mode === "uncommitted") {
11650
- stagedDiff = getStagedDiff(args.excludes ?? []);
11651
- stagedChanges = getStagedFiles();
11652
- if (!stagedDiff.trim()) {
11653
- stagedDiff = getWorkingTreeDiff(args.excludes ?? []);
11654
- stagedChanges = getAllChangedFiles();
11655
- }
11754
+ let rawDiff = getStagedDiff(args.excludes ?? []);
11755
+ plainFiles = getStagedFiles();
11756
+ changesForAI = plainFiles;
11757
+ const isStaged = !!rawDiff.trim();
11758
+ if (!isStaged) {
11759
+ rawDiff = getWorkingTreeDiff(args.excludes ?? []);
11760
+ plainFiles = getAllChangedFiles();
11761
+ changesForAI = getAllChangedFilesWithStatus();
11762
+ }
11763
+ stagedDiff = preprocessDiff(rawDiff).processedDiff;
11764
+ diffStat = getWorkingDiffStat(isStaged);
11656
11765
  }
11657
11766
  const recentCommits = state.mode === "rescue" ? getRecentBranchCommits(state.commitsAhead) : void 0;
11658
11767
  const branchRules = args.branchRules ?? (config2.branch?.generation?.types && config2.branch.generation.types.length > 0 ? { types: [...config2.branch.generation.types] } : void 0);
@@ -11682,30 +11791,33 @@ async function runBranchGuard(args, log) {
11682
11791
  if (apiKey) {
11683
11792
  const client = new ApiClient({ apiKey });
11684
11793
  try {
11794
+ const cappedDiff = stagedDiff ? stagedDiff.slice(0, 6e4) : void 0;
11685
11795
  const branchResult = await client.generateBranchName({
11686
- diff: stagedDiff || void 0,
11687
- changes: stagedChanges || void 0,
11796
+ diff: cappedDiff || void 0,
11797
+ changes: changesForAI || void 0,
11798
+ diff_stat: diffStat || void 0,
11688
11799
  recent_commits: recentCommits,
11689
11800
  model: args.model,
11690
11801
  rules: branchRules
11691
11802
  });
11692
11803
  rawName = branchResult.name;
11693
11804
  } catch {
11694
- const fallbackInput = state.mode === "rescue" ? { files: [], description: recentCommits?.join(" ") ?? "" } : { files: stagedChanges ? stagedChanges.split("\n").filter(Boolean) : [] };
11805
+ const fallbackInput = state.mode === "rescue" ? { files: [], description: recentCommits?.join(" ") ?? "" } : { files: plainFiles ? plainFiles.split("\n").filter(Boolean) : [] };
11695
11806
  rawName = deterministicBranchName(fallbackInput).name;
11696
11807
  usedFallback = true;
11697
11808
  }
11698
11809
  } else {
11699
11810
  try {
11700
11811
  rawName = await generateLocalBranchNameFn({
11701
- diff: stagedDiff || void 0,
11702
- changes: stagedChanges || void 0,
11812
+ diff: stagedDiff ? stagedDiff.slice(0, 6e4) : void 0,
11813
+ changes: changesForAI || void 0,
11814
+ diffStat: diffStat || void 0,
11703
11815
  recentCommits,
11704
11816
  model: args.model,
11705
11817
  rules: branchRules
11706
11818
  });
11707
11819
  } catch {
11708
- const fallbackInput = state.mode === "rescue" ? { files: [], description: recentCommits?.join(" ") ?? "" } : { files: stagedChanges ? stagedChanges.split("\n").filter(Boolean) : [] };
11820
+ const fallbackInput = state.mode === "rescue" ? { files: [], description: recentCommits?.join(" ") ?? "" } : { files: plainFiles ? plainFiles.split("\n").filter(Boolean) : [] };
11709
11821
  rawName = deterministicBranchName(fallbackInput).name;
11710
11822
  usedFallback = true;
11711
11823
  }
@@ -11779,6 +11891,7 @@ var init_branch_guard = __esm({
11779
11891
  import_promises2 = __toESM(require("node:readline/promises"));
11780
11892
  init_api();
11781
11893
  init_git();
11894
+ init_smart_diff();
11782
11895
  init_protected_branch_guard();
11783
11896
  init_branch_rescue();
11784
11897
  init_branch_name();
@@ -11882,7 +11995,11 @@ async function runCommit(args) {
11882
11995
  if (intersected.length > 0) rules = { ...rules, scopes: intersected };
11883
11996
  }
11884
11997
  }
11885
- } catch {
11998
+ } catch (err) {
11999
+ const msg = err instanceof Error ? err.message : String(err);
12000
+ if (msg && !/not found|404|403/i.test(msg)) {
12001
+ log.dim("\u26A0 could not fetch team rules: " + msg.slice(0, 80));
12002
+ }
11886
12003
  }
11887
12004
  rules = applyCliTypeScopeToRules(rules, args.type, args.scope);
11888
12005
  const recentCommits = args.noContext ? void 0 : getRecentBranchCommits(5);
@@ -12308,6 +12425,8 @@ function parseArgs(args) {
12308
12425
  result.messageOnly = true;
12309
12426
  } else if (arg === "--message" && i + 1 < args.length) {
12310
12427
  result.message = args[++i];
12428
+ } else if (arg === "--message") {
12429
+ throw new Error("Flag --message requires a value");
12311
12430
  } else if (arg === "--push") {
12312
12431
  result.push = true;
12313
12432
  } else if (arg === "--rescue") {
@@ -12347,33 +12466,51 @@ function parseArgs(args) {
12347
12466
  }
12348
12467
  } else if (arg === "--api-key" && i + 1 < args.length) {
12349
12468
  result.apiKey = args[++i];
12469
+ } else if (arg === "--api-key") {
12470
+ throw new Error("Flag --api-key requires a value");
12350
12471
  } else if (arg === "--base" && i + 1 < args.length) {
12351
12472
  result.base = args[++i];
12473
+ } else if (arg === "--base") {
12474
+ throw new Error("Flag --base requires a value");
12352
12475
  } else if (arg === "--create") {
12353
12476
  result.create = true;
12354
12477
  } else if (arg === "--from" && i + 1 < args.length) {
12355
12478
  result.from = args[++i];
12479
+ } else if (arg === "--from") {
12480
+ throw new Error("Flag --from requires a value");
12356
12481
  } else if (arg === "--from-commits") {
12357
12482
  result.fromCommits = true;
12358
12483
  } else if (arg === "--to" && i + 1 < args.length) {
12359
12484
  result.to = args[++i];
12485
+ } else if (arg === "--to") {
12486
+ throw new Error("Flag --to requires a value");
12360
12487
  } else if (arg === "--write") {
12361
12488
  result.write = true;
12362
12489
  } else if (arg === "--version" && i + 1 < args.length) {
12363
12490
  result.version = args[++i];
12491
+ } else if (arg === "--version") {
12492
+ throw new Error("Flag --version requires a value");
12364
12493
  } else if (arg === "--uninstall") {
12365
12494
  result.uninstall = true;
12366
12495
  } else if (arg === "--hook-mode") {
12367
12496
  result.hookMode = true;
12368
12497
  } else if (arg === "--model" && i + 1 < args.length) {
12369
12498
  result.model = args[++i];
12499
+ } else if (arg === "--model") {
12500
+ throw new Error("Flag --model requires a value");
12370
12501
  } else if (arg === "--type" && i + 1 < args.length) {
12371
12502
  result.type = args[++i];
12503
+ } else if (arg === "--type") {
12504
+ throw new Error("Flag --type requires a value");
12372
12505
  } else if (arg === "--scope" && i + 1 < args.length) {
12373
12506
  result.scope = args[++i];
12507
+ } else if (arg === "--scope") {
12508
+ throw new Error("Flag --scope requires a value");
12374
12509
  } else if (arg === "--exclude" && i + 1 < args.length) {
12375
12510
  const ex = args[++i];
12376
12511
  if (ex) result.exclude.push(ex);
12512
+ } else if (arg === "--exclude") {
12513
+ throw new Error("Flag --exclude requires a value");
12377
12514
  } else if (arg === "--no-color") {
12378
12515
  } else if (arg === "--no-animate") {
12379
12516
  result.noAnimate = true;
@@ -12385,6 +12522,8 @@ function parseArgs(args) {
12385
12522
  );
12386
12523
  }
12387
12524
  result.boxStyleOverride = v;
12525
+ } else if (arg === "--style") {
12526
+ throw new Error("Flag --style requires a value");
12388
12527
  } else if (arg === "login") {
12389
12528
  result.command = "login";
12390
12529
  subcommandSeen = true;
@@ -12420,6 +12559,10 @@ function parseArgs(args) {
12420
12559
  subcommandSeen = true;
12421
12560
  } else if (subcommandSeen && !arg.startsWith("-")) {
12422
12561
  result.positionals.push(arg);
12562
+ } else if (arg.startsWith("--")) {
12563
+ throw new Error(`Unknown flag: ${arg}`);
12564
+ } else if (!subcommandSeen) {
12565
+ throw new Error(`Unknown command: ${arg}. Run 'qc --help' for usage.`);
12423
12566
  }
12424
12567
  }
12425
12568
  if (result.messageOnly && result.push) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quikcommit/cli",
3
- "version": "12.0.0",
3
+ "version": "13.0.0",
4
4
  "description": "AI-powered conventional commit messages",
5
5
  "bin": {
6
6
  "qc": "./dist/index.js"
@@ -34,14 +34,14 @@
34
34
  "esbuild": "^0.28.0",
35
35
  "typescript": "^5.9.3",
36
36
  "vitest": "^4.1.5",
37
- "@quikcommit/shared": "9.0.0"
37
+ "@quikcommit/shared": "10.0.0"
38
38
  },
39
39
  "scripts": {
40
40
  "build": "node build.mjs",
41
41
  "typecheck": "tsc --noEmit",
42
42
  "start": "node dist/index.js",
43
43
  "test": "vitest run",
44
- "lint": "eslint .",
45
- "lint:fix": "eslint . --fix"
44
+ "lint": "oxlint .",
45
+ "lint:fix": "oxlint --fix"
46
46
  }
47
47
  }