@quikcommit/cli 1.0.0 → 2.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 (3) hide show
  1. package/README.md +242 -0
  2. package/dist/index.js +407 -44
  3. package/package.json +3 -3
package/README.md ADDED
@@ -0,0 +1,242 @@
1
+ # @quikcommit/cli
2
+
3
+ AI-powered conventional commit messages. Stage your changes, run `qc`, get a perfect commit.
4
+
5
+ ```bash
6
+ git add .
7
+ qc
8
+ # → feat(auth): add OAuth2 login with GitHub and Google providers
9
+ ```
10
+
11
+ [![npm version](https://img.shields.io/npm/v/@quikcommit/cli)](https://www.npmjs.com/package/@quikcommit/cli)
12
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
13
+
14
+ ---
15
+
16
+ ## Install
17
+
18
+ ```bash
19
+ npm install -g @quikcommit/cli
20
+ # or
21
+ bun add -g @quikcommit/cli
22
+ ```
23
+
24
+ **Pre-built binaries** (no Node/Bun required) — download from [GitHub Releases](https://github.com/Quikcommit-Internal/public/releases/latest):
25
+
26
+ | Platform | File |
27
+ |----------|------|
28
+ | macOS Apple Silicon | `qc-darwin-arm64` |
29
+ | macOS Intel | `qc-darwin-x64` |
30
+ | Linux x64 | `qc-linux-x64` |
31
+ | Linux ARM64 | `qc-linux-arm64` |
32
+
33
+ ```bash
34
+ # Example: macOS Apple Silicon
35
+ curl -fsSL https://github.com/Quikcommit-Internal/public/releases/latest/download/qc-darwin-arm64 \
36
+ -o /usr/local/bin/qc && chmod +x /usr/local/bin/qc
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Quick Start
42
+
43
+ ```bash
44
+ # 1. Sign in (opens browser — GitHub or Google)
45
+ qc login
46
+
47
+ # 2. Stage your changes
48
+ git add .
49
+
50
+ # 3. Generate + commit
51
+ qc
52
+
53
+ # That's it.
54
+ ```
55
+
56
+ ---
57
+
58
+ ## Usage
59
+
60
+ ```
61
+ qc [command] [options]
62
+ ```
63
+
64
+ ### Generate a commit (default)
65
+
66
+ ```bash
67
+ qc # generate + commit
68
+ qc --message-only # preview message, don't commit
69
+ qc --push # generate, commit, and push
70
+ qc --model llama-3.3-70b # use a specific model
71
+ ```
72
+
73
+ ### Auth & status
74
+
75
+ ```bash
76
+ qc login # sign in via browser
77
+ qc logout # clear credentials
78
+ qc status # show plan, usage, auth
79
+ ```
80
+
81
+ ### PR descriptions *(Pro+)*
82
+
83
+ ```bash
84
+ qc pr # generate PR description from branch commits
85
+ qc pr --base develop # compare against a different base branch
86
+ qc pr --create # generate + open PR with gh CLI
87
+ ```
88
+
89
+ ### Changelog *(Pro+)*
90
+
91
+ ```bash
92
+ qc changelog # generate since last tag
93
+ qc changelog --from v1.0.0 --to HEAD # specific range
94
+ qc changelog --write --version 1.1.0 # write to CHANGELOG.md
95
+ ```
96
+
97
+ ### Git hook
98
+
99
+ ```bash
100
+ qc init # install prepare-commit-msg hook
101
+ qc init --uninstall # remove hook
102
+ ```
103
+
104
+ After `qc init`, every `git commit` auto-generates a message for you to review before saving.
105
+
106
+ ### Team *(Team+ plan)*
107
+
108
+ ```bash
109
+ qc team info # show team and members
110
+ qc team rules # view team commit rules
111
+ qc team rules --push # push local commitlint config to team
112
+ qc team invite alice@corp.com # invite a teammate
113
+ ```
114
+
115
+ ### Configuration
116
+
117
+ ```bash
118
+ qc config # show current config
119
+ qc config set model qwen25-coder-32b
120
+ qc config set api_url https://api.quikcommit.dev
121
+ qc config reset # reset to defaults
122
+ ```
123
+
124
+ ---
125
+
126
+ ## Plans
127
+
128
+ | Plan | Commits/mo | PR descriptions | Changelog | Team rules |
129
+ |------|-----------|----------------|-----------|------------|
130
+ | **Free** | 50 | — | — | — |
131
+ | **Pro** | 500 | ✓ | ✓ | — |
132
+ | **Team** | 2,000 | ✓ | ✓ | ✓ |
133
+ | **Scale** | Unlimited | ✓ | ✓ | ✓ |
134
+
135
+ ```bash
136
+ qc upgrade # open billing page
137
+ ```
138
+
139
+ ---
140
+
141
+ ## Models
142
+
143
+ | Model ID | Description | Min Plan |
144
+ |----------|-------------|----------|
145
+ | `qwen3-30b` | Fast, good quality | Free |
146
+ | `qwen25-coder-32b` | Best code understanding | Pro |
147
+ | `deepseek-r1-32b` | Reasoning model | Pro |
148
+ | `llama-3.3-70b` | Large, strong understanding | Pro |
149
+
150
+ ```bash
151
+ qc --model qwen25-coder-32b # one-time override
152
+ qc config set model qwen25-coder-32b # set permanently
153
+ ```
154
+
155
+ ---
156
+
157
+ ## Local / Self-Hosted (no subscription needed)
158
+
159
+ ```bash
160
+ qc --use-ollama # Ollama on localhost:11434 (codellama by default)
161
+ qc --use-lmstudio # LM Studio on localhost:1234
162
+ qc --use-openrouter # OpenRouter (set api_url + credentials)
163
+ qc --use-cloudflare # Your own Cloudflare Worker
164
+ ```
165
+
166
+ If no API key is stored but a local provider is configured, `qc` auto-falls back to it silently.
167
+
168
+ ---
169
+
170
+ ## Monorepo Support
171
+
172
+ In a pnpm / npm / yarn workspace, `qc` detects which packages have staged changes and sets the commit scope automatically:
173
+
174
+ ```
175
+ packages/api/src/auth.ts staged → scope: api
176
+ packages/ui/src/button.tsx staged → scope: ui
177
+ ```
178
+
179
+ ---
180
+
181
+ ## Excluding Files
182
+
183
+ Create a `.qcignore` in your repo root (same syntax as `.gitignore`) to prevent certain files from being included in the diff sent to the AI:
184
+
185
+ ```
186
+ # .qcignore
187
+ pnpm-lock.yaml
188
+ dist/
189
+ *.min.js
190
+ ```
191
+
192
+ Lock files and build output are excluded by default.
193
+
194
+ ---
195
+
196
+ ## Environment Variables
197
+
198
+ | Variable | Description |
199
+ |----------|-------------|
200
+ | `QC_API_KEY` | Override stored API key (useful in CI) |
201
+
202
+ ```bash
203
+ QC_API_KEY=$QC_API_KEY qc --message-only
204
+ ```
205
+
206
+ ---
207
+
208
+ ## Configuration File
209
+
210
+ Stored at `~/.config/qc/config.json`:
211
+
212
+ ```jsonc
213
+ {
214
+ "model": "qwen25-coder-32b",
215
+ "apiUrl": "https://api.quikcommit.dev",
216
+ "provider": "saas",
217
+ "excludes": ["*.lock"],
218
+ "rules": {
219
+ "scopes": ["api", "ui", "auth"],
220
+ "types": ["feat", "fix", "docs", "chore"],
221
+ "maxHeaderLength": 72
222
+ }
223
+ }
224
+ ```
225
+
226
+ Credentials are stored separately at `~/.config/qc/credentials` (mode 600).
227
+
228
+ ---
229
+
230
+ ## Links
231
+
232
+ - [GitHub](https://github.com/Quikcommit-Internal/public)
233
+ - [Full Documentation](https://github.com/Quikcommit-Internal/public/tree/main/docs)
234
+ - [Getting Started](https://github.com/Quikcommit-Internal/public/blob/main/docs/getting-started.md)
235
+ - [CLI Reference](https://github.com/Quikcommit-Internal/public/blob/main/docs/cli-reference.md)
236
+ - [Local Providers](https://github.com/Quikcommit-Internal/public/blob/main/docs/local-providers.md)
237
+
238
+ ---
239
+
240
+ ## License
241
+
242
+ MIT
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- #!/usr/bin/env node
3
2
  "use strict";
4
3
  var __create = Object.create;
5
4
  var __defProp = Object.defineProperty;
@@ -186,6 +185,13 @@ var init_api = __esm({
186
185
  );
187
186
  return { message: data.message ?? "" };
188
187
  }
188
+ async generateChangeset(req) {
189
+ const data = await this.request("/v1/changeset", req);
190
+ return {
191
+ packages: data.packages ?? [],
192
+ summary: data.summary ?? ""
193
+ };
194
+ }
189
195
  async fetchJson(endpoint, options) {
190
196
  if (!this.apiKey) {
191
197
  throw new Error("Not authenticated. Run `qc login` first.");
@@ -344,6 +350,32 @@ function getCommitsSince(ref, to = "HEAD") {
344
350
  return { hash: hash ?? "", subject: rest.join(" ").trim() };
345
351
  });
346
352
  }
353
+ function getChangedFilesSince(base = "main") {
354
+ validateRef(base, "base");
355
+ const output = (0, import_child_process.execFileSync)("git", ["diff", `${base}..HEAD`, "--name-only"], {
356
+ encoding: "utf-8",
357
+ maxBuffer: 10 * 1024 * 1024
358
+ });
359
+ return output.trim().split("\n").filter(Boolean);
360
+ }
361
+ function getOnlineLog(base = "main") {
362
+ validateRef(base, "base");
363
+ return (0, import_child_process.execFileSync)(
364
+ "git",
365
+ ["log", `${base}..HEAD`, "--oneline", "--max-count=200"],
366
+ {
367
+ encoding: "utf-8",
368
+ maxBuffer: 5 * 1024 * 1024
369
+ }
370
+ ).trim();
371
+ }
372
+ function getFullDiff(base = "main") {
373
+ validateRef(base, "base");
374
+ return (0, import_child_process.execFileSync)("git", ["diff", `${base}..HEAD`], {
375
+ encoding: "utf-8",
376
+ maxBuffer: 10 * 1024 * 1024
377
+ });
378
+ }
347
379
  var import_child_process, import_fs2, import_path2, import_os2, SAFE_GIT_REF;
348
380
  var init_git = __esm({
349
381
  "src/git.ts"() {
@@ -357,6 +389,12 @@ var init_git = __esm({
357
389
  });
358
390
 
359
391
  // src/monorepo.ts
392
+ var monorepo_exports = {};
393
+ __export(monorepo_exports, {
394
+ autoDetectScope: () => autoDetectScope,
395
+ detectWorkspace: () => detectWorkspace,
396
+ getPackageForFile: () => getPackageForFile
397
+ });
360
398
  function findGitRoot(start) {
361
399
  try {
362
400
  return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
@@ -440,10 +478,18 @@ function matchGlobPattern(rel, pattern) {
440
478
  return null;
441
479
  }
442
480
  const prefix = dir + "/";
443
- if (rel.startsWith(prefix)) {
444
- const rest = rel.slice(prefix.length);
445
- const pkg = rest.split("/")[0];
446
- return pkg || null;
481
+ const hasGlob = /\*/.test(pattern);
482
+ if (hasGlob) {
483
+ if (rel.startsWith(prefix)) {
484
+ const rest = rel.slice(prefix.length);
485
+ const pkg = rest.split("/")[0];
486
+ return pkg || null;
487
+ }
488
+ } else {
489
+ if (rel === dir || rel.startsWith(prefix)) {
490
+ const segments = dir.split("/").filter(Boolean);
491
+ return segments[segments.length - 1] ?? null;
492
+ }
447
493
  }
448
494
  return null;
449
495
  }
@@ -734,6 +780,312 @@ var init_changelog = __esm({
734
780
  }
735
781
  });
736
782
 
783
+ // src/commands/changeset.ts
784
+ var changeset_exports = {};
785
+ __export(changeset_exports, {
786
+ changeset: () => changeset,
787
+ formatChangesetFile: () => formatChangesetFile,
788
+ generateSlug: () => generateSlug,
789
+ mapFilesToPackages: () => mapFilesToPackages
790
+ });
791
+ function generateSlug() {
792
+ const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
793
+ return `${pick(ADJECTIVES)}-${pick(ANIMALS)}-${pick(VERBS)}`;
794
+ }
795
+ function mapFilesToPackages(files, workspace) {
796
+ const dirToName = /* @__PURE__ */ new Map();
797
+ for (const file of files) {
798
+ const dirName = getPackageForFile(file, workspace);
799
+ if (!dirName || dirToName.has(dirName)) continue;
800
+ for (const pattern of workspace.packages) {
801
+ const hasGlob = /\*/.test(pattern);
802
+ const dir = pattern.replace(/\/?\*\*?$/, "").replace(/\/$/, "");
803
+ const pkgJsonPath = hasGlob ? (0, import_path5.join)(workspace.root, dir, dirName, "package.json") : (0, import_path5.join)(workspace.root, pattern.replace(/\/$/, ""), "package.json");
804
+ try {
805
+ const pkg = JSON.parse((0, import_fs5.readFileSync)(pkgJsonPath, "utf-8"));
806
+ dirToName.set(dirName, pkg.name ?? dirName);
807
+ break;
808
+ } catch {
809
+ }
810
+ }
811
+ if (!dirToName.has(dirName)) {
812
+ dirToName.set(dirName, dirName);
813
+ }
814
+ }
815
+ return dirToName;
816
+ }
817
+ function formatChangesetFile(packages, summary) {
818
+ const frontmatter = packages.map((p) => `"${p.name}": ${p.bump}`).join("\n");
819
+ return `---
820
+ ${frontmatter}
821
+ ---
822
+
823
+ ${summary}
824
+ `;
825
+ }
826
+ async function prompt(rl, question) {
827
+ return new Promise((resolve) => rl.question(question, resolve));
828
+ }
829
+ async function changeset(options) {
830
+ const base = options.base ?? "main";
831
+ const apiKey = getApiKey();
832
+ if (!apiKey) {
833
+ console.error("Error: Not authenticated. Run `qc login` first.");
834
+ process.exit(1);
835
+ }
836
+ const { detectWorkspace: detectWorkspace2 } = await Promise.resolve().then(() => (init_monorepo(), monorepo_exports));
837
+ const workspace = detectWorkspace2();
838
+ if (!workspace) {
839
+ console.error(
840
+ "No workspace packages found. Is this a pnpm monorepo?"
841
+ );
842
+ process.exit(1);
843
+ }
844
+ const changedFiles = getChangedFilesSince(base);
845
+ if (changedFiles.length === 0) {
846
+ console.error(`No changes detected vs ${base}.`);
847
+ process.exit(1);
848
+ }
849
+ const packageMap = mapFilesToPackages(changedFiles, workspace);
850
+ const packageNames = Array.from(packageMap.values());
851
+ if (packageNames.length === 0) {
852
+ console.error("No workspace packages detected in changed files.");
853
+ process.exit(1);
854
+ }
855
+ const commits = getOnlineLog(base);
856
+ const diff = getFullDiff(base);
857
+ const commitCount = commits.split("\n").filter(Boolean).length;
858
+ console.error(
859
+ `Analyzing changes vs ${base}... ${commitCount} commit(s), ${packageNames.length} package(s) changed`
860
+ );
861
+ const client = new ApiClient({ apiKey });
862
+ let result;
863
+ const msg = (e) => e instanceof Error ? e.message : String(e);
864
+ const isTransient = (m) => /invalid json|no changeset|unexpected response|ai worker|timeout|502|503|504/i.test(m);
865
+ let attempts = 0;
866
+ while (true) {
867
+ try {
868
+ result = await client.generateChangeset({
869
+ diff,
870
+ packages: packageNames,
871
+ commits,
872
+ model: options.model
873
+ });
874
+ break;
875
+ } catch (err) {
876
+ const m = msg(err);
877
+ if (!isTransient(m)) {
878
+ console.error(m);
879
+ process.exit(1);
880
+ }
881
+ if (attempts === 0) {
882
+ attempts++;
883
+ continue;
884
+ }
885
+ console.error(m);
886
+ process.exit(1);
887
+ }
888
+ }
889
+ const resultNames = new Set(result.packages.map((p) => p.name));
890
+ for (const name of packageNames) {
891
+ if (!resultNames.has(name)) {
892
+ result.packages.push({ name, bump: "patch", reason: "included in changeset" });
893
+ }
894
+ }
895
+ console.log("");
896
+ for (const pkg of result.packages) {
897
+ console.log(` ${pkg.name.padEnd(32)} ${pkg.bump.padEnd(7)} \u2014 ${pkg.reason}`);
898
+ }
899
+ console.log("");
900
+ console.log(`Summary: ${result.summary}`);
901
+ console.log("");
902
+ const rl = readline.createInterface({
903
+ input: process.stdin,
904
+ output: process.stdout
905
+ });
906
+ try {
907
+ const answer = (await prompt(rl, "Accept all? [Y/n/edit] > ")).trim().toLowerCase();
908
+ if (answer === "n") {
909
+ console.error("Aborted.");
910
+ process.exit(0);
911
+ }
912
+ if (answer === "edit") {
913
+ for (let i = 0; i < result.packages.length; i++) {
914
+ const pkg = result.packages[i];
915
+ const response = (await prompt(rl, ` ${pkg.name} [${pkg.bump}]: major/minor/patch? > `)).trim().toLowerCase();
916
+ if (response === "major" || response === "minor" || response === "patch") {
917
+ result.packages[i] = { ...pkg, bump: response };
918
+ }
919
+ }
920
+ }
921
+ } finally {
922
+ rl.close();
923
+ }
924
+ const slug = generateSlug();
925
+ const gitRoot = getGitRoot();
926
+ const changesetDir = (0, import_path5.join)(gitRoot, ".changeset");
927
+ if (!(0, import_fs5.existsSync)(changesetDir)) {
928
+ (0, import_fs5.mkdirSync)(changesetDir, { recursive: true });
929
+ }
930
+ const filePath = (0, import_path5.join)(changesetDir, `${slug}.md`);
931
+ const content = formatChangesetFile(result.packages, result.summary);
932
+ (0, import_fs5.writeFileSync)(filePath, content, "utf-8");
933
+ console.log(`
934
+ \u2713 Written .changeset/${slug}.md`);
935
+ }
936
+ var import_fs5, import_path5, readline, ADJECTIVES, ANIMALS, VERBS;
937
+ var init_changeset = __esm({
938
+ "src/commands/changeset.ts"() {
939
+ "use strict";
940
+ import_fs5 = require("fs");
941
+ import_path5 = require("path");
942
+ readline = __toESM(require("readline"));
943
+ init_config();
944
+ init_api();
945
+ init_git();
946
+ init_monorepo();
947
+ ADJECTIVES = [
948
+ "bouncy",
949
+ "brave",
950
+ "calm",
951
+ "clean",
952
+ "cool",
953
+ "damp",
954
+ "epic",
955
+ "fair",
956
+ "fast",
957
+ "firm",
958
+ "flat",
959
+ "free",
960
+ "glad",
961
+ "gold",
962
+ "good",
963
+ "gray",
964
+ "huge",
965
+ "keen",
966
+ "kind",
967
+ "lazy",
968
+ "lean",
969
+ "lush",
970
+ "mild",
971
+ "neat",
972
+ "nice",
973
+ "noble",
974
+ "pure",
975
+ "rare",
976
+ "rich",
977
+ "safe",
978
+ "sharp",
979
+ "slim",
980
+ "slow",
981
+ "soft",
982
+ "swift",
983
+ "tall",
984
+ "tame",
985
+ "tidy",
986
+ "tiny",
987
+ "tough",
988
+ "trim",
989
+ "true",
990
+ "vast",
991
+ "warm",
992
+ "wild",
993
+ "wise"
994
+ ];
995
+ ANIMALS = [
996
+ "ant",
997
+ "bear",
998
+ "bee",
999
+ "bird",
1000
+ "bug",
1001
+ "cat",
1002
+ "crab",
1003
+ "crow",
1004
+ "deer",
1005
+ "dog",
1006
+ "dove",
1007
+ "duck",
1008
+ "elk",
1009
+ "fish",
1010
+ "frog",
1011
+ "goat",
1012
+ "hawk",
1013
+ "lamb",
1014
+ "lark",
1015
+ "lion",
1016
+ "lynx",
1017
+ "mole",
1018
+ "moth",
1019
+ "mule",
1020
+ "owl",
1021
+ "pony",
1022
+ "puma",
1023
+ "raven",
1024
+ "slug",
1025
+ "snail",
1026
+ "swan",
1027
+ "toad",
1028
+ "vole",
1029
+ "wasp",
1030
+ "wolf",
1031
+ "wren",
1032
+ "yak"
1033
+ ];
1034
+ VERBS = [
1035
+ "bite",
1036
+ "bolt",
1037
+ "burn",
1038
+ "buzz",
1039
+ "call",
1040
+ "cast",
1041
+ "chase",
1042
+ "chew",
1043
+ "claw",
1044
+ "climb",
1045
+ "crawl",
1046
+ "dart",
1047
+ "dash",
1048
+ "dive",
1049
+ "draw",
1050
+ "drift",
1051
+ "drop",
1052
+ "eat",
1053
+ "fall",
1054
+ "find",
1055
+ "flee",
1056
+ "flip",
1057
+ "flow",
1058
+ "fly",
1059
+ "glow",
1060
+ "gnaw",
1061
+ "growl",
1062
+ "howl",
1063
+ "hunt",
1064
+ "jump",
1065
+ "kick",
1066
+ "leap",
1067
+ "lick",
1068
+ "lift",
1069
+ "lurk",
1070
+ "pace",
1071
+ "peck",
1072
+ "play",
1073
+ "race",
1074
+ "roam",
1075
+ "roar",
1076
+ "roll",
1077
+ "run",
1078
+ "skim",
1079
+ "sniff",
1080
+ "soar",
1081
+ "spin",
1082
+ "swim",
1083
+ "wade",
1084
+ "walk"
1085
+ ];
1086
+ }
1087
+ });
1088
+
737
1089
  // src/commands/init.ts
738
1090
  var init_exports = {};
739
1091
  __export(init_exports, {
@@ -749,12 +1101,12 @@ async function init(options) {
749
1101
  console.error("Error: Not a git repository");
750
1102
  process.exit(1);
751
1103
  }
752
- const hookPath = (0, import_path5.join)(hooksDir, "prepare-commit-msg");
1104
+ const hookPath = (0, import_path6.join)(hooksDir, "prepare-commit-msg");
753
1105
  if (options.uninstall) {
754
- if ((0, import_fs5.existsSync)(hookPath)) {
755
- const content = (0, import_fs5.readFileSync)(hookPath, "utf-8");
1106
+ if ((0, import_fs6.existsSync)(hookPath)) {
1107
+ const content = (0, import_fs6.readFileSync)(hookPath, "utf-8");
756
1108
  if (content.includes("QuikCommit")) {
757
- (0, import_fs5.unlinkSync)(hookPath);
1109
+ (0, import_fs6.unlinkSync)(hookPath);
758
1110
  console.log("QuikCommit hook removed.");
759
1111
  } else {
760
1112
  console.log("Hook exists but was not installed by QuikCommit. Skipping.");
@@ -764,8 +1116,8 @@ async function init(options) {
764
1116
  }
765
1117
  return;
766
1118
  }
767
- if ((0, import_fs5.existsSync)(hookPath)) {
768
- const content = (0, import_fs5.readFileSync)(hookPath, "utf-8");
1119
+ if ((0, import_fs6.existsSync)(hookPath)) {
1120
+ const content = (0, import_fs6.readFileSync)(hookPath, "utf-8");
769
1121
  if (content.includes("QuikCommit")) {
770
1122
  console.log("QuikCommit hook is already installed.");
771
1123
  return;
@@ -775,17 +1127,17 @@ async function init(options) {
775
1127
  );
776
1128
  process.exit(1);
777
1129
  }
778
- (0, import_fs5.writeFileSync)(hookPath, HOOK_CONTENT);
779
- (0, import_fs5.chmodSync)(hookPath, 493);
1130
+ (0, import_fs6.writeFileSync)(hookPath, HOOK_CONTENT);
1131
+ (0, import_fs6.chmodSync)(hookPath, 493);
780
1132
  console.log("QuikCommit hook installed.");
781
1133
  console.log("Now just run `git commit` and a message will be generated automatically.");
782
1134
  }
783
- var import_fs5, import_path5, import_child_process5, HOOK_CONTENT;
1135
+ var import_fs6, import_path6, import_child_process5, HOOK_CONTENT;
784
1136
  var init_init = __esm({
785
1137
  "src/commands/init.ts"() {
786
1138
  "use strict";
787
- import_fs5 = require("fs");
788
- import_path5 = require("path");
1139
+ import_fs6 = require("fs");
1140
+ import_path6 = require("path");
789
1141
  import_child_process5 = require("child_process");
790
1142
  HOOK_CONTENT = `#!/bin/sh
791
1143
  # QuikCommit - auto-generate commit messages
@@ -860,10 +1212,10 @@ function detectLocalCommitlintRules() {
860
1212
  "commitlint.config.mjs"
861
1213
  ];
862
1214
  for (const file of files) {
863
- const path = (0, import_path6.join)(cwd, file);
864
- if (!(0, import_fs6.existsSync)(path)) continue;
1215
+ const path = (0, import_path7.join)(cwd, file);
1216
+ if (!(0, import_fs7.existsSync)(path)) continue;
865
1217
  try {
866
- const content = (0, import_fs6.readFileSync)(path, "utf-8");
1218
+ const content = (0, import_fs7.readFileSync)(path, "utf-8");
867
1219
  let parsed;
868
1220
  if (file.endsWith(".json") || file === ".commitlintrc") {
869
1221
  parsed = JSON.parse(content);
@@ -875,10 +1227,10 @@ function detectLocalCommitlintRules() {
875
1227
  } catch {
876
1228
  }
877
1229
  }
878
- const pkgPath = (0, import_path6.join)(cwd, "package.json");
879
- if ((0, import_fs6.existsSync)(pkgPath)) {
1230
+ const pkgPath = (0, import_path7.join)(cwd, "package.json");
1231
+ if ((0, import_fs7.existsSync)(pkgPath)) {
880
1232
  try {
881
- const content = (0, import_fs6.readFileSync)(pkgPath, "utf-8");
1233
+ const content = (0, import_fs7.readFileSync)(pkgPath, "utf-8");
882
1234
  const pkg = JSON.parse(content);
883
1235
  if (pkg.commitlint) {
884
1236
  const rules = mapCommitlintToRules(pkg.commitlint);
@@ -941,12 +1293,12 @@ async function team(subcommand, args) {
941
1293
  process.exit(1);
942
1294
  }
943
1295
  }
944
- var import_fs6, import_path6;
1296
+ var import_fs7, import_path7;
945
1297
  var init_team = __esm({
946
1298
  "src/commands/team.ts"() {
947
1299
  "use strict";
948
- import_fs6 = require("fs");
949
- import_path6 = require("path");
1300
+ import_fs7 = require("fs");
1301
+ import_path7 = require("path");
950
1302
  init_api();
951
1303
  init_config();
952
1304
  }
@@ -1072,9 +1424,9 @@ __export(local_exports, {
1072
1424
  });
1073
1425
  function getLegacyProvider() {
1074
1426
  try {
1075
- const p = (0, import_path7.join)(CONFIG_PATH2, "provider");
1076
- if ((0, import_fs7.existsSync)(p)) {
1077
- const v = (0, import_fs7.readFileSync)(p, "utf-8").trim().toLowerCase();
1427
+ const p = (0, import_path8.join)(CONFIG_PATH2, "provider");
1428
+ if ((0, import_fs8.existsSync)(p)) {
1429
+ const v = (0, import_fs8.readFileSync)(p, "utf-8").trim().toLowerCase();
1078
1430
  if (["ollama", "lmstudio", "openrouter", "custom", "cloudflare"].includes(v)) {
1079
1431
  return v;
1080
1432
  }
@@ -1085,9 +1437,9 @@ function getLegacyProvider() {
1085
1437
  }
1086
1438
  function getLegacyBaseUrl(provider) {
1087
1439
  try {
1088
- const p = (0, import_path7.join)(CONFIG_PATH2, "base_url");
1089
- if ((0, import_fs7.existsSync)(p)) {
1090
- return (0, import_fs7.readFileSync)(p, "utf-8").trim();
1440
+ const p = (0, import_path8.join)(CONFIG_PATH2, "base_url");
1441
+ if ((0, import_fs8.existsSync)(p)) {
1442
+ return (0, import_fs8.readFileSync)(p, "utf-8").trim();
1091
1443
  }
1092
1444
  } catch {
1093
1445
  }
@@ -1095,9 +1447,9 @@ function getLegacyBaseUrl(provider) {
1095
1447
  }
1096
1448
  function getLegacyModel(provider) {
1097
1449
  try {
1098
- const p = (0, import_path7.join)(CONFIG_PATH2, "model");
1099
- if ((0, import_fs7.existsSync)(p)) {
1100
- const v = (0, import_fs7.readFileSync)(p, "utf-8").trim();
1450
+ const p = (0, import_path8.join)(CONFIG_PATH2, "model");
1451
+ if ((0, import_fs8.existsSync)(p)) {
1452
+ const v = (0, import_fs8.readFileSync)(p, "utf-8").trim();
1101
1453
  if (v) return v;
1102
1454
  }
1103
1455
  } catch {
@@ -1116,7 +1468,7 @@ function getLocalProviderConfig() {
1116
1468
  return { provider, baseUrl, model, apiKey };
1117
1469
  }
1118
1470
  function buildUserPrompt(changes, diff, rules) {
1119
- let prompt = `Generate a commit message for these changes:
1471
+ let prompt2 = `Generate a commit message for these changes:
1120
1472
 
1121
1473
  ## File changes:
1122
1474
  <file_changes>
@@ -1130,14 +1482,14 @@ ${diff}
1130
1482
 
1131
1483
  `;
1132
1484
  if (rules && Object.keys(rules).length > 0) {
1133
- prompt += `Rules: ${JSON.stringify(rules)}
1485
+ prompt2 += `Rules: ${JSON.stringify(rules)}
1134
1486
 
1135
1487
  `;
1136
1488
  }
1137
- prompt += `Important:
1489
+ prompt2 += `Important:
1138
1490
  - Follow conventional commit format: <type>(<scope>): <subject>
1139
1491
  - Response should be the commit message only, no explanations`;
1140
- return prompt;
1492
+ return prompt2;
1141
1493
  }
1142
1494
  function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiKey, rules) {
1143
1495
  const headers = {
@@ -1147,7 +1499,7 @@ function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiK
1147
1499
  headers["Authorization"] = `Bearer ${apiKey}`;
1148
1500
  }
1149
1501
  if (provider === "openrouter") {
1150
- headers["HTTP-Referer"] = "https://github.com/quikcommit/quikcommit";
1502
+ headers["HTTP-Referer"] = "https://github.com/Quikcommit-Internal/public";
1151
1503
  headers["X-Title"] = "qc - AI Commit Message Generator";
1152
1504
  }
1153
1505
  let url;
@@ -1284,18 +1636,18 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
1284
1636
  gitPush();
1285
1637
  }
1286
1638
  }
1287
- var import_fs7, import_path7, import_os4, CONFIG_PATH2, PROVIDER_URLS, DEFAULT_MODELS;
1639
+ var import_fs8, import_path8, import_os4, CONFIG_PATH2, PROVIDER_URLS, DEFAULT_MODELS;
1288
1640
  var init_local = __esm({
1289
1641
  "src/local.ts"() {
1290
1642
  "use strict";
1291
- import_fs7 = require("fs");
1292
- import_path7 = require("path");
1643
+ import_fs8 = require("fs");
1644
+ import_path8 = require("path");
1293
1645
  import_os4 = require("os");
1294
1646
  init_config();
1295
1647
  init_dist();
1296
1648
  init_git();
1297
1649
  init_monorepo();
1298
- CONFIG_PATH2 = (0, import_path7.join)((0, import_os4.homedir)(), CONFIG_DIR);
1650
+ CONFIG_PATH2 = (0, import_path8.join)((0, import_os4.homedir)(), CONFIG_DIR);
1299
1651
  PROVIDER_URLS = {
1300
1652
  ollama: "http://localhost:11434",
1301
1653
  lmstudio: "http://localhost:1234/v1",
@@ -1326,6 +1678,7 @@ Usage:
1326
1678
  qc --push Commit and push to origin
1327
1679
  qc pr Generate PR description from branch commits
1328
1680
  qc changelog Generate changelog from commits since last tag
1681
+ qc changeset Automate pnpm changeset with AI
1329
1682
  qc init Install prepare-commit-msg hook for auto-generation
1330
1683
  qc login Sign in via browser
1331
1684
  qc logout Clear local credentials
@@ -1337,7 +1690,7 @@ Options:
1337
1690
  -m, --message-only Generate message only
1338
1691
  -p, --push Commit and push after generating
1339
1692
  --api-key <key> Use this API key (overrides credentials file)
1340
- --base <branch> Base branch for qc pr (default: main)
1693
+ --base <branch> Base branch for qc pr, qc changeset (default: main)
1341
1694
  --create Create PR with gh CLI after qc pr
1342
1695
  --from <ref> Start ref for qc changelog (default: latest tag)
1343
1696
  --to <ref> End ref for qc changelog (default: HEAD)
@@ -1411,6 +1764,8 @@ function parseArgs(args) {
1411
1764
  command = "config";
1412
1765
  } else if (arg === "upgrade") {
1413
1766
  command = "upgrade";
1767
+ } else if (arg === "changeset") {
1768
+ command = "changeset";
1414
1769
  } else if (arg === "--model" && i + 1 < args.length) {
1415
1770
  model = args[++i];
1416
1771
  } else if (arg === "--local" || arg === "--use-ollama" || arg === "--use-lmstudio" || arg === "--use-openrouter" || arg === "--use-cloudflare") {
@@ -1535,6 +1890,14 @@ async function main() {
1535
1890
  });
1536
1891
  return;
1537
1892
  }
1893
+ if (command === "changeset") {
1894
+ const { changeset: changeset2 } = await Promise.resolve().then(() => (init_changeset(), changeset_exports));
1895
+ await changeset2({
1896
+ base: values.base,
1897
+ model: values.model ?? getConfig().model
1898
+ });
1899
+ return;
1900
+ }
1538
1901
  if (command === "init") {
1539
1902
  const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
1540
1903
  await init2({ uninstall: values.uninstall });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quikcommit/cli",
3
- "version": "1.0.0",
3
+ "version": "2.0.0",
4
4
  "description": "AI-powered conventional commit messages",
5
5
  "bin": {
6
6
  "qc": "./dist/index.js"
@@ -20,7 +20,7 @@
20
20
  "license": "MIT",
21
21
  "repository": {
22
22
  "type": "git",
23
- "url": "https://github.com/quikcommit/quikcommit"
23
+ "url": "https://github.com/Quikcommit-Internal/public"
24
24
  },
25
25
  "publishConfig": {
26
26
  "access": "public"
@@ -32,7 +32,7 @@
32
32
  "vitest": "~4.0.18"
33
33
  },
34
34
  "dependencies": {
35
- "@quikcommit/shared": "1.0.0"
35
+ "@quikcommit/shared": "2.0.0"
36
36
  },
37
37
  "scripts": {
38
38
  "build": "node build.mjs",