@quikcommit/cli 1.0.1 → 3.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.
- package/README.md +7 -7
- package/dist/index.js +415 -53
- package/package.json +5 -4
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ npm install -g @quikcommit/cli
|
|
|
21
21
|
bun add -g @quikcommit/cli
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
**Pre-built binaries** (no Node/Bun required) — download from [GitHub Releases](https://github.com/
|
|
24
|
+
**Pre-built binaries** (no Node/Bun required) — download from [GitHub Releases](https://github.com/Quikcommit-Internal/public/releases/latest):
|
|
25
25
|
|
|
26
26
|
| Platform | File |
|
|
27
27
|
|----------|------|
|
|
@@ -32,7 +32,7 @@ bun add -g @quikcommit/cli
|
|
|
32
32
|
|
|
33
33
|
```bash
|
|
34
34
|
# Example: macOS Apple Silicon
|
|
35
|
-
curl -fsSL https://github.com/
|
|
35
|
+
curl -fsSL https://github.com/Quikcommit-Internal/public/releases/latest/download/qc-darwin-arm64 \
|
|
36
36
|
-o /usr/local/bin/qc && chmod +x /usr/local/bin/qc
|
|
37
37
|
```
|
|
38
38
|
|
|
@@ -229,11 +229,11 @@ Credentials are stored separately at `~/.config/qc/credentials` (mode 600).
|
|
|
229
229
|
|
|
230
230
|
## Links
|
|
231
231
|
|
|
232
|
-
- [GitHub](https://github.com/
|
|
233
|
-
- [Full Documentation](https://github.com/
|
|
234
|
-
- [Getting Started](https://github.com/
|
|
235
|
-
- [CLI Reference](https://github.com/
|
|
236
|
-
- [Local Providers](https://github.com/
|
|
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
237
|
|
|
238
238
|
---
|
|
239
239
|
|
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;
|
|
@@ -154,8 +153,7 @@ var init_api = __esm({
|
|
|
154
153
|
});
|
|
155
154
|
if (!res.ok) {
|
|
156
155
|
const err = await res.json().catch(() => ({ error: res.statusText }));
|
|
157
|
-
|
|
158
|
-
if (planRequiredMsg && code === "PLAN_REQUIRED") {
|
|
156
|
+
if (planRequiredMsg && err.code === "PLAN_REQUIRED") {
|
|
159
157
|
throw new Error(planRequiredMsg);
|
|
160
158
|
}
|
|
161
159
|
throw new Error(err.error ?? `HTTP ${res.status}`);
|
|
@@ -186,6 +184,13 @@ var init_api = __esm({
|
|
|
186
184
|
);
|
|
187
185
|
return { message: data.message ?? "" };
|
|
188
186
|
}
|
|
187
|
+
async generateChangeset(req) {
|
|
188
|
+
const data = await this.request("/v1/changeset", req);
|
|
189
|
+
return {
|
|
190
|
+
packages: data.packages ?? [],
|
|
191
|
+
summary: data.summary ?? ""
|
|
192
|
+
};
|
|
193
|
+
}
|
|
189
194
|
async fetchJson(endpoint, options) {
|
|
190
195
|
if (!this.apiKey) {
|
|
191
196
|
throw new Error("Not authenticated. Run `qc login` first.");
|
|
@@ -344,6 +349,32 @@ function getCommitsSince(ref, to = "HEAD") {
|
|
|
344
349
|
return { hash: hash ?? "", subject: rest.join(" ").trim() };
|
|
345
350
|
});
|
|
346
351
|
}
|
|
352
|
+
function getChangedFilesSince(base = "main") {
|
|
353
|
+
validateRef(base, "base");
|
|
354
|
+
const output = (0, import_child_process.execFileSync)("git", ["diff", `${base}..HEAD`, "--name-only"], {
|
|
355
|
+
encoding: "utf-8",
|
|
356
|
+
maxBuffer: 10 * 1024 * 1024
|
|
357
|
+
});
|
|
358
|
+
return output.trim().split("\n").filter(Boolean);
|
|
359
|
+
}
|
|
360
|
+
function getOnlineLog(base = "main") {
|
|
361
|
+
validateRef(base, "base");
|
|
362
|
+
return (0, import_child_process.execFileSync)(
|
|
363
|
+
"git",
|
|
364
|
+
["log", `${base}..HEAD`, "--oneline", "--max-count=200"],
|
|
365
|
+
{
|
|
366
|
+
encoding: "utf-8",
|
|
367
|
+
maxBuffer: 5 * 1024 * 1024
|
|
368
|
+
}
|
|
369
|
+
).trim();
|
|
370
|
+
}
|
|
371
|
+
function getFullDiff(base = "main") {
|
|
372
|
+
validateRef(base, "base");
|
|
373
|
+
return (0, import_child_process.execFileSync)("git", ["diff", `${base}..HEAD`], {
|
|
374
|
+
encoding: "utf-8",
|
|
375
|
+
maxBuffer: 10 * 1024 * 1024
|
|
376
|
+
});
|
|
377
|
+
}
|
|
347
378
|
var import_child_process, import_fs2, import_path2, import_os2, SAFE_GIT_REF;
|
|
348
379
|
var init_git = __esm({
|
|
349
380
|
"src/git.ts"() {
|
|
@@ -357,6 +388,12 @@ var init_git = __esm({
|
|
|
357
388
|
});
|
|
358
389
|
|
|
359
390
|
// src/monorepo.ts
|
|
391
|
+
var monorepo_exports = {};
|
|
392
|
+
__export(monorepo_exports, {
|
|
393
|
+
autoDetectScope: () => autoDetectScope,
|
|
394
|
+
detectWorkspace: () => detectWorkspace,
|
|
395
|
+
getPackageForFile: () => getPackageForFile
|
|
396
|
+
});
|
|
360
397
|
function findGitRoot(start) {
|
|
361
398
|
try {
|
|
362
399
|
return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
@@ -440,10 +477,18 @@ function matchGlobPattern(rel, pattern) {
|
|
|
440
477
|
return null;
|
|
441
478
|
}
|
|
442
479
|
const prefix = dir + "/";
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
480
|
+
const hasGlob = /\*/.test(pattern);
|
|
481
|
+
if (hasGlob) {
|
|
482
|
+
if (rel.startsWith(prefix)) {
|
|
483
|
+
const rest = rel.slice(prefix.length);
|
|
484
|
+
const pkg = rest.split("/")[0];
|
|
485
|
+
return pkg || null;
|
|
486
|
+
}
|
|
487
|
+
} else {
|
|
488
|
+
if (rel === dir || rel.startsWith(prefix)) {
|
|
489
|
+
const segments = dir.split("/").filter(Boolean);
|
|
490
|
+
return segments[segments.length - 1] ?? null;
|
|
491
|
+
}
|
|
447
492
|
}
|
|
448
493
|
return null;
|
|
449
494
|
}
|
|
@@ -734,12 +779,318 @@ var init_changelog = __esm({
|
|
|
734
779
|
}
|
|
735
780
|
});
|
|
736
781
|
|
|
782
|
+
// src/commands/changeset.ts
|
|
783
|
+
var changeset_exports = {};
|
|
784
|
+
__export(changeset_exports, {
|
|
785
|
+
changeset: () => changeset,
|
|
786
|
+
formatChangesetFile: () => formatChangesetFile,
|
|
787
|
+
generateSlug: () => generateSlug,
|
|
788
|
+
mapFilesToPackages: () => mapFilesToPackages
|
|
789
|
+
});
|
|
790
|
+
function generateSlug() {
|
|
791
|
+
const pick = (arr) => arr[Math.floor(Math.random() * arr.length)];
|
|
792
|
+
return `${pick(ADJECTIVES)}-${pick(ANIMALS)}-${pick(VERBS)}`;
|
|
793
|
+
}
|
|
794
|
+
function mapFilesToPackages(files, workspace) {
|
|
795
|
+
const dirToName = /* @__PURE__ */ new Map();
|
|
796
|
+
for (const file of files) {
|
|
797
|
+
const dirName = getPackageForFile(file, workspace);
|
|
798
|
+
if (!dirName || dirToName.has(dirName)) continue;
|
|
799
|
+
for (const pattern of workspace.packages) {
|
|
800
|
+
const hasGlob = /\*/.test(pattern);
|
|
801
|
+
const dir = pattern.replace(/\/?\*\*?$/, "").replace(/\/$/, "");
|
|
802
|
+
const pkgJsonPath = hasGlob ? (0, import_path5.join)(workspace.root, dir, dirName, "package.json") : (0, import_path5.join)(workspace.root, pattern.replace(/\/$/, ""), "package.json");
|
|
803
|
+
try {
|
|
804
|
+
const pkg = JSON.parse((0, import_fs5.readFileSync)(pkgJsonPath, "utf-8"));
|
|
805
|
+
dirToName.set(dirName, pkg.name ?? dirName);
|
|
806
|
+
break;
|
|
807
|
+
} catch {
|
|
808
|
+
}
|
|
809
|
+
}
|
|
810
|
+
if (!dirToName.has(dirName)) {
|
|
811
|
+
dirToName.set(dirName, dirName);
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
return dirToName;
|
|
815
|
+
}
|
|
816
|
+
function formatChangesetFile(packages, summary) {
|
|
817
|
+
const frontmatter = packages.map((p) => `"${p.name}": ${p.bump}`).join("\n");
|
|
818
|
+
return `---
|
|
819
|
+
${frontmatter}
|
|
820
|
+
---
|
|
821
|
+
|
|
822
|
+
${summary}
|
|
823
|
+
`;
|
|
824
|
+
}
|
|
825
|
+
async function prompt(rl, question) {
|
|
826
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
827
|
+
}
|
|
828
|
+
async function changeset(options) {
|
|
829
|
+
const base = options.base ?? "main";
|
|
830
|
+
const apiKey = getApiKey();
|
|
831
|
+
if (!apiKey) {
|
|
832
|
+
console.error("Error: Not authenticated. Run `qc login` first.");
|
|
833
|
+
process.exit(1);
|
|
834
|
+
}
|
|
835
|
+
const { detectWorkspace: detectWorkspace2 } = await Promise.resolve().then(() => (init_monorepo(), monorepo_exports));
|
|
836
|
+
const workspace = detectWorkspace2();
|
|
837
|
+
if (!workspace) {
|
|
838
|
+
console.error(
|
|
839
|
+
"No workspace packages found. Is this a pnpm monorepo?"
|
|
840
|
+
);
|
|
841
|
+
process.exit(1);
|
|
842
|
+
}
|
|
843
|
+
const changedFiles = getChangedFilesSince(base);
|
|
844
|
+
if (changedFiles.length === 0) {
|
|
845
|
+
console.error(`No changes detected vs ${base}.`);
|
|
846
|
+
process.exit(1);
|
|
847
|
+
}
|
|
848
|
+
const packageMap = mapFilesToPackages(changedFiles, workspace);
|
|
849
|
+
const packageNames = Array.from(packageMap.values());
|
|
850
|
+
if (packageNames.length === 0) {
|
|
851
|
+
console.error("No workspace packages detected in changed files.");
|
|
852
|
+
process.exit(1);
|
|
853
|
+
}
|
|
854
|
+
const commits = getOnlineLog(base);
|
|
855
|
+
const diff = getFullDiff(base);
|
|
856
|
+
const commitCount = commits.split("\n").filter(Boolean).length;
|
|
857
|
+
console.error(
|
|
858
|
+
`Analyzing changes vs ${base}... ${commitCount} commit(s), ${packageNames.length} package(s) changed`
|
|
859
|
+
);
|
|
860
|
+
const client = new ApiClient({ apiKey });
|
|
861
|
+
let result;
|
|
862
|
+
const msg = (e) => e instanceof Error ? e.message : String(e);
|
|
863
|
+
const isTransient = (m) => /invalid json|no changeset|unexpected response|ai worker|timeout|502|503|504/i.test(m);
|
|
864
|
+
let attempts = 0;
|
|
865
|
+
while (true) {
|
|
866
|
+
try {
|
|
867
|
+
result = await client.generateChangeset({
|
|
868
|
+
diff,
|
|
869
|
+
packages: packageNames,
|
|
870
|
+
commits,
|
|
871
|
+
model: options.model
|
|
872
|
+
});
|
|
873
|
+
break;
|
|
874
|
+
} catch (err) {
|
|
875
|
+
const m = msg(err);
|
|
876
|
+
if (!isTransient(m)) {
|
|
877
|
+
console.error(m);
|
|
878
|
+
process.exit(1);
|
|
879
|
+
}
|
|
880
|
+
if (attempts === 0) {
|
|
881
|
+
attempts++;
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
console.error(m);
|
|
885
|
+
process.exit(1);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
const resultNames = new Set(result.packages.map((p) => p.name));
|
|
889
|
+
for (const name of packageNames) {
|
|
890
|
+
if (!resultNames.has(name)) {
|
|
891
|
+
result.packages.push({ name, bump: "patch", reason: "included in changeset" });
|
|
892
|
+
}
|
|
893
|
+
}
|
|
894
|
+
console.log("");
|
|
895
|
+
for (const pkg of result.packages) {
|
|
896
|
+
console.log(` ${pkg.name.padEnd(32)} ${pkg.bump.padEnd(7)} \u2014 ${pkg.reason}`);
|
|
897
|
+
}
|
|
898
|
+
console.log("");
|
|
899
|
+
console.log(`Summary: ${result.summary}`);
|
|
900
|
+
console.log("");
|
|
901
|
+
const rl = readline.createInterface({
|
|
902
|
+
input: process.stdin,
|
|
903
|
+
output: process.stdout
|
|
904
|
+
});
|
|
905
|
+
try {
|
|
906
|
+
const answer = (await prompt(rl, "Accept all? [Y/n/edit] > ")).trim().toLowerCase();
|
|
907
|
+
if (answer === "n") {
|
|
908
|
+
console.error("Aborted.");
|
|
909
|
+
process.exit(0);
|
|
910
|
+
}
|
|
911
|
+
if (answer === "edit") {
|
|
912
|
+
for (let i = 0; i < result.packages.length; i++) {
|
|
913
|
+
const pkg = result.packages[i];
|
|
914
|
+
const response = (await prompt(rl, ` ${pkg.name} [${pkg.bump}]: major/minor/patch? > `)).trim().toLowerCase();
|
|
915
|
+
if (response === "major" || response === "minor" || response === "patch") {
|
|
916
|
+
result.packages[i] = { ...pkg, bump: response };
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
} finally {
|
|
921
|
+
rl.close();
|
|
922
|
+
}
|
|
923
|
+
const slug = generateSlug();
|
|
924
|
+
const gitRoot = getGitRoot();
|
|
925
|
+
const changesetDir = (0, import_path5.join)(gitRoot, ".changeset");
|
|
926
|
+
if (!(0, import_fs5.existsSync)(changesetDir)) {
|
|
927
|
+
(0, import_fs5.mkdirSync)(changesetDir, { recursive: true });
|
|
928
|
+
}
|
|
929
|
+
const filePath = (0, import_path5.join)(changesetDir, `${slug}.md`);
|
|
930
|
+
const content = formatChangesetFile(result.packages, result.summary);
|
|
931
|
+
(0, import_fs5.writeFileSync)(filePath, content, "utf-8");
|
|
932
|
+
console.log(`
|
|
933
|
+
\u2713 Written .changeset/${slug}.md`);
|
|
934
|
+
}
|
|
935
|
+
var import_fs5, import_path5, readline, ADJECTIVES, ANIMALS, VERBS;
|
|
936
|
+
var init_changeset = __esm({
|
|
937
|
+
"src/commands/changeset.ts"() {
|
|
938
|
+
"use strict";
|
|
939
|
+
import_fs5 = require("fs");
|
|
940
|
+
import_path5 = require("path");
|
|
941
|
+
readline = __toESM(require("readline"));
|
|
942
|
+
init_config();
|
|
943
|
+
init_api();
|
|
944
|
+
init_git();
|
|
945
|
+
init_monorepo();
|
|
946
|
+
ADJECTIVES = [
|
|
947
|
+
"bouncy",
|
|
948
|
+
"brave",
|
|
949
|
+
"calm",
|
|
950
|
+
"clean",
|
|
951
|
+
"cool",
|
|
952
|
+
"damp",
|
|
953
|
+
"epic",
|
|
954
|
+
"fair",
|
|
955
|
+
"fast",
|
|
956
|
+
"firm",
|
|
957
|
+
"flat",
|
|
958
|
+
"free",
|
|
959
|
+
"glad",
|
|
960
|
+
"gold",
|
|
961
|
+
"good",
|
|
962
|
+
"gray",
|
|
963
|
+
"huge",
|
|
964
|
+
"keen",
|
|
965
|
+
"kind",
|
|
966
|
+
"lazy",
|
|
967
|
+
"lean",
|
|
968
|
+
"lush",
|
|
969
|
+
"mild",
|
|
970
|
+
"neat",
|
|
971
|
+
"nice",
|
|
972
|
+
"noble",
|
|
973
|
+
"pure",
|
|
974
|
+
"rare",
|
|
975
|
+
"rich",
|
|
976
|
+
"safe",
|
|
977
|
+
"sharp",
|
|
978
|
+
"slim",
|
|
979
|
+
"slow",
|
|
980
|
+
"soft",
|
|
981
|
+
"swift",
|
|
982
|
+
"tall",
|
|
983
|
+
"tame",
|
|
984
|
+
"tidy",
|
|
985
|
+
"tiny",
|
|
986
|
+
"tough",
|
|
987
|
+
"trim",
|
|
988
|
+
"true",
|
|
989
|
+
"vast",
|
|
990
|
+
"warm",
|
|
991
|
+
"wild",
|
|
992
|
+
"wise"
|
|
993
|
+
];
|
|
994
|
+
ANIMALS = [
|
|
995
|
+
"ant",
|
|
996
|
+
"bear",
|
|
997
|
+
"bee",
|
|
998
|
+
"bird",
|
|
999
|
+
"bug",
|
|
1000
|
+
"cat",
|
|
1001
|
+
"crab",
|
|
1002
|
+
"crow",
|
|
1003
|
+
"deer",
|
|
1004
|
+
"dog",
|
|
1005
|
+
"dove",
|
|
1006
|
+
"duck",
|
|
1007
|
+
"elk",
|
|
1008
|
+
"fish",
|
|
1009
|
+
"frog",
|
|
1010
|
+
"goat",
|
|
1011
|
+
"hawk",
|
|
1012
|
+
"lamb",
|
|
1013
|
+
"lark",
|
|
1014
|
+
"lion",
|
|
1015
|
+
"lynx",
|
|
1016
|
+
"mole",
|
|
1017
|
+
"moth",
|
|
1018
|
+
"mule",
|
|
1019
|
+
"owl",
|
|
1020
|
+
"pony",
|
|
1021
|
+
"puma",
|
|
1022
|
+
"raven",
|
|
1023
|
+
"slug",
|
|
1024
|
+
"snail",
|
|
1025
|
+
"swan",
|
|
1026
|
+
"toad",
|
|
1027
|
+
"vole",
|
|
1028
|
+
"wasp",
|
|
1029
|
+
"wolf",
|
|
1030
|
+
"wren",
|
|
1031
|
+
"yak"
|
|
1032
|
+
];
|
|
1033
|
+
VERBS = [
|
|
1034
|
+
"bite",
|
|
1035
|
+
"bolt",
|
|
1036
|
+
"burn",
|
|
1037
|
+
"buzz",
|
|
1038
|
+
"call",
|
|
1039
|
+
"cast",
|
|
1040
|
+
"chase",
|
|
1041
|
+
"chew",
|
|
1042
|
+
"claw",
|
|
1043
|
+
"climb",
|
|
1044
|
+
"crawl",
|
|
1045
|
+
"dart",
|
|
1046
|
+
"dash",
|
|
1047
|
+
"dive",
|
|
1048
|
+
"draw",
|
|
1049
|
+
"drift",
|
|
1050
|
+
"drop",
|
|
1051
|
+
"eat",
|
|
1052
|
+
"fall",
|
|
1053
|
+
"find",
|
|
1054
|
+
"flee",
|
|
1055
|
+
"flip",
|
|
1056
|
+
"flow",
|
|
1057
|
+
"fly",
|
|
1058
|
+
"glow",
|
|
1059
|
+
"gnaw",
|
|
1060
|
+
"growl",
|
|
1061
|
+
"howl",
|
|
1062
|
+
"hunt",
|
|
1063
|
+
"jump",
|
|
1064
|
+
"kick",
|
|
1065
|
+
"leap",
|
|
1066
|
+
"lick",
|
|
1067
|
+
"lift",
|
|
1068
|
+
"lurk",
|
|
1069
|
+
"pace",
|
|
1070
|
+
"peck",
|
|
1071
|
+
"play",
|
|
1072
|
+
"race",
|
|
1073
|
+
"roam",
|
|
1074
|
+
"roar",
|
|
1075
|
+
"roll",
|
|
1076
|
+
"run",
|
|
1077
|
+
"skim",
|
|
1078
|
+
"sniff",
|
|
1079
|
+
"soar",
|
|
1080
|
+
"spin",
|
|
1081
|
+
"swim",
|
|
1082
|
+
"wade",
|
|
1083
|
+
"walk"
|
|
1084
|
+
];
|
|
1085
|
+
}
|
|
1086
|
+
});
|
|
1087
|
+
|
|
737
1088
|
// src/commands/init.ts
|
|
738
1089
|
var init_exports = {};
|
|
739
1090
|
__export(init_exports, {
|
|
740
1091
|
init: () => init
|
|
741
1092
|
});
|
|
742
|
-
|
|
1093
|
+
function init(options) {
|
|
743
1094
|
let hooksDir;
|
|
744
1095
|
try {
|
|
745
1096
|
hooksDir = (0, import_child_process5.execFileSync)("git", ["rev-parse", "--git-path", "hooks"], {
|
|
@@ -749,12 +1100,12 @@ async function init(options) {
|
|
|
749
1100
|
console.error("Error: Not a git repository");
|
|
750
1101
|
process.exit(1);
|
|
751
1102
|
}
|
|
752
|
-
const hookPath = (0,
|
|
1103
|
+
const hookPath = (0, import_path6.join)(hooksDir, "prepare-commit-msg");
|
|
753
1104
|
if (options.uninstall) {
|
|
754
|
-
if ((0,
|
|
755
|
-
const content = (0,
|
|
1105
|
+
if ((0, import_fs6.existsSync)(hookPath)) {
|
|
1106
|
+
const content = (0, import_fs6.readFileSync)(hookPath, "utf-8");
|
|
756
1107
|
if (content.includes("QuikCommit")) {
|
|
757
|
-
(0,
|
|
1108
|
+
(0, import_fs6.unlinkSync)(hookPath);
|
|
758
1109
|
console.log("QuikCommit hook removed.");
|
|
759
1110
|
} else {
|
|
760
1111
|
console.log("Hook exists but was not installed by QuikCommit. Skipping.");
|
|
@@ -764,8 +1115,8 @@ async function init(options) {
|
|
|
764
1115
|
}
|
|
765
1116
|
return;
|
|
766
1117
|
}
|
|
767
|
-
if ((0,
|
|
768
|
-
const content = (0,
|
|
1118
|
+
if ((0, import_fs6.existsSync)(hookPath)) {
|
|
1119
|
+
const content = (0, import_fs6.readFileSync)(hookPath, "utf-8");
|
|
769
1120
|
if (content.includes("QuikCommit")) {
|
|
770
1121
|
console.log("QuikCommit hook is already installed.");
|
|
771
1122
|
return;
|
|
@@ -775,17 +1126,17 @@ async function init(options) {
|
|
|
775
1126
|
);
|
|
776
1127
|
process.exit(1);
|
|
777
1128
|
}
|
|
778
|
-
(0,
|
|
779
|
-
(0,
|
|
1129
|
+
(0, import_fs6.writeFileSync)(hookPath, HOOK_CONTENT);
|
|
1130
|
+
(0, import_fs6.chmodSync)(hookPath, 493);
|
|
780
1131
|
console.log("QuikCommit hook installed.");
|
|
781
1132
|
console.log("Now just run `git commit` and a message will be generated automatically.");
|
|
782
1133
|
}
|
|
783
|
-
var
|
|
1134
|
+
var import_fs6, import_path6, import_child_process5, HOOK_CONTENT;
|
|
784
1135
|
var init_init = __esm({
|
|
785
1136
|
"src/commands/init.ts"() {
|
|
786
1137
|
"use strict";
|
|
787
|
-
|
|
788
|
-
|
|
1138
|
+
import_fs6 = require("fs");
|
|
1139
|
+
import_path6 = require("path");
|
|
789
1140
|
import_child_process5 = require("child_process");
|
|
790
1141
|
HOOK_CONTENT = `#!/bin/sh
|
|
791
1142
|
# QuikCommit - auto-generate commit messages
|
|
@@ -830,7 +1181,7 @@ function mapCommitlintToRules(config2) {
|
|
|
830
1181
|
if (!config2 || typeof config2 !== "object") return null;
|
|
831
1182
|
const c = config2;
|
|
832
1183
|
const rules = {};
|
|
833
|
-
const
|
|
1184
|
+
const _ext = c.extends;
|
|
834
1185
|
const rulesConfig = c.rules;
|
|
835
1186
|
if (Array.isArray(rulesConfig?.["type-enum"]) && rulesConfig["type-enum"].length >= 3) {
|
|
836
1187
|
const [, , value] = rulesConfig["type-enum"];
|
|
@@ -860,10 +1211,10 @@ function detectLocalCommitlintRules() {
|
|
|
860
1211
|
"commitlint.config.mjs"
|
|
861
1212
|
];
|
|
862
1213
|
for (const file of files) {
|
|
863
|
-
const path = (0,
|
|
864
|
-
if (!(0,
|
|
1214
|
+
const path = (0, import_path7.join)(cwd, file);
|
|
1215
|
+
if (!(0, import_fs7.existsSync)(path)) continue;
|
|
865
1216
|
try {
|
|
866
|
-
const content = (0,
|
|
1217
|
+
const content = (0, import_fs7.readFileSync)(path, "utf-8");
|
|
867
1218
|
let parsed;
|
|
868
1219
|
if (file.endsWith(".json") || file === ".commitlintrc") {
|
|
869
1220
|
parsed = JSON.parse(content);
|
|
@@ -875,10 +1226,10 @@ function detectLocalCommitlintRules() {
|
|
|
875
1226
|
} catch {
|
|
876
1227
|
}
|
|
877
1228
|
}
|
|
878
|
-
const pkgPath = (0,
|
|
879
|
-
if ((0,
|
|
1229
|
+
const pkgPath = (0, import_path7.join)(cwd, "package.json");
|
|
1230
|
+
if ((0, import_fs7.existsSync)(pkgPath)) {
|
|
880
1231
|
try {
|
|
881
|
-
const content = (0,
|
|
1232
|
+
const content = (0, import_fs7.readFileSync)(pkgPath, "utf-8");
|
|
882
1233
|
const pkg = JSON.parse(content);
|
|
883
1234
|
if (pkg.commitlint) {
|
|
884
1235
|
const rules = mapCommitlintToRules(pkg.commitlint);
|
|
@@ -941,12 +1292,12 @@ async function team(subcommand, args) {
|
|
|
941
1292
|
process.exit(1);
|
|
942
1293
|
}
|
|
943
1294
|
}
|
|
944
|
-
var
|
|
1295
|
+
var import_fs7, import_path7;
|
|
945
1296
|
var init_team = __esm({
|
|
946
1297
|
"src/commands/team.ts"() {
|
|
947
1298
|
"use strict";
|
|
948
|
-
|
|
949
|
-
|
|
1299
|
+
import_fs7 = require("fs");
|
|
1300
|
+
import_path7 = require("path");
|
|
950
1301
|
init_api();
|
|
951
1302
|
init_config();
|
|
952
1303
|
}
|
|
@@ -957,7 +1308,7 @@ var config_exports = {};
|
|
|
957
1308
|
__export(config_exports, {
|
|
958
1309
|
config: () => config
|
|
959
1310
|
});
|
|
960
|
-
|
|
1311
|
+
function config(args) {
|
|
961
1312
|
if (args.length === 0) {
|
|
962
1313
|
showConfig();
|
|
963
1314
|
return;
|
|
@@ -971,7 +1322,7 @@ async function config(args) {
|
|
|
971
1322
|
console.error(" Keys: model, api_url, provider");
|
|
972
1323
|
process.exit(1);
|
|
973
1324
|
}
|
|
974
|
-
|
|
1325
|
+
setConfig(key, value);
|
|
975
1326
|
return;
|
|
976
1327
|
}
|
|
977
1328
|
if (sub === "reset") {
|
|
@@ -994,7 +1345,7 @@ function showConfig() {
|
|
|
994
1345
|
console.log(` excludes: ${cfg.excludes.join(", ")}`);
|
|
995
1346
|
}
|
|
996
1347
|
}
|
|
997
|
-
|
|
1348
|
+
function setConfig(key, value) {
|
|
998
1349
|
const cfg = getConfig();
|
|
999
1350
|
const updates = {};
|
|
1000
1351
|
if (key === "model") {
|
|
@@ -1072,9 +1423,9 @@ __export(local_exports, {
|
|
|
1072
1423
|
});
|
|
1073
1424
|
function getLegacyProvider() {
|
|
1074
1425
|
try {
|
|
1075
|
-
const p = (0,
|
|
1076
|
-
if ((0,
|
|
1077
|
-
const v = (0,
|
|
1426
|
+
const p = (0, import_path8.join)(CONFIG_PATH2, "provider");
|
|
1427
|
+
if ((0, import_fs8.existsSync)(p)) {
|
|
1428
|
+
const v = (0, import_fs8.readFileSync)(p, "utf-8").trim().toLowerCase();
|
|
1078
1429
|
if (["ollama", "lmstudio", "openrouter", "custom", "cloudflare"].includes(v)) {
|
|
1079
1430
|
return v;
|
|
1080
1431
|
}
|
|
@@ -1085,9 +1436,9 @@ function getLegacyProvider() {
|
|
|
1085
1436
|
}
|
|
1086
1437
|
function getLegacyBaseUrl(provider) {
|
|
1087
1438
|
try {
|
|
1088
|
-
const p = (0,
|
|
1089
|
-
if ((0,
|
|
1090
|
-
return (0,
|
|
1439
|
+
const p = (0, import_path8.join)(CONFIG_PATH2, "base_url");
|
|
1440
|
+
if ((0, import_fs8.existsSync)(p)) {
|
|
1441
|
+
return (0, import_fs8.readFileSync)(p, "utf-8").trim();
|
|
1091
1442
|
}
|
|
1092
1443
|
} catch {
|
|
1093
1444
|
}
|
|
@@ -1095,9 +1446,9 @@ function getLegacyBaseUrl(provider) {
|
|
|
1095
1446
|
}
|
|
1096
1447
|
function getLegacyModel(provider) {
|
|
1097
1448
|
try {
|
|
1098
|
-
const p = (0,
|
|
1099
|
-
if ((0,
|
|
1100
|
-
const v = (0,
|
|
1449
|
+
const p = (0, import_path8.join)(CONFIG_PATH2, "model");
|
|
1450
|
+
if ((0, import_fs8.existsSync)(p)) {
|
|
1451
|
+
const v = (0, import_fs8.readFileSync)(p, "utf-8").trim();
|
|
1101
1452
|
if (v) return v;
|
|
1102
1453
|
}
|
|
1103
1454
|
} catch {
|
|
@@ -1116,7 +1467,7 @@ function getLocalProviderConfig() {
|
|
|
1116
1467
|
return { provider, baseUrl, model, apiKey };
|
|
1117
1468
|
}
|
|
1118
1469
|
function buildUserPrompt(changes, diff, rules) {
|
|
1119
|
-
let
|
|
1470
|
+
let prompt2 = `Generate a commit message for these changes:
|
|
1120
1471
|
|
|
1121
1472
|
## File changes:
|
|
1122
1473
|
<file_changes>
|
|
@@ -1130,14 +1481,14 @@ ${diff}
|
|
|
1130
1481
|
|
|
1131
1482
|
`;
|
|
1132
1483
|
if (rules && Object.keys(rules).length > 0) {
|
|
1133
|
-
|
|
1484
|
+
prompt2 += `Rules: ${JSON.stringify(rules)}
|
|
1134
1485
|
|
|
1135
1486
|
`;
|
|
1136
1487
|
}
|
|
1137
|
-
|
|
1488
|
+
prompt2 += `Important:
|
|
1138
1489
|
- Follow conventional commit format: <type>(<scope>): <subject>
|
|
1139
1490
|
- Response should be the commit message only, no explanations`;
|
|
1140
|
-
return
|
|
1491
|
+
return prompt2;
|
|
1141
1492
|
}
|
|
1142
1493
|
function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiKey, rules) {
|
|
1143
1494
|
const headers = {
|
|
@@ -1147,7 +1498,7 @@ function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiK
|
|
|
1147
1498
|
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
1148
1499
|
}
|
|
1149
1500
|
if (provider === "openrouter") {
|
|
1150
|
-
headers["HTTP-Referer"] = "https://github.com/
|
|
1501
|
+
headers["HTTP-Referer"] = "https://github.com/Quikcommit-Internal/public";
|
|
1151
1502
|
headers["X-Title"] = "qc - AI Commit Message Generator";
|
|
1152
1503
|
}
|
|
1153
1504
|
let url;
|
|
@@ -1284,18 +1635,18 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
1284
1635
|
gitPush();
|
|
1285
1636
|
}
|
|
1286
1637
|
}
|
|
1287
|
-
var
|
|
1638
|
+
var import_fs8, import_path8, import_os4, CONFIG_PATH2, PROVIDER_URLS, DEFAULT_MODELS;
|
|
1288
1639
|
var init_local = __esm({
|
|
1289
1640
|
"src/local.ts"() {
|
|
1290
1641
|
"use strict";
|
|
1291
|
-
|
|
1292
|
-
|
|
1642
|
+
import_fs8 = require("fs");
|
|
1643
|
+
import_path8 = require("path");
|
|
1293
1644
|
import_os4 = require("os");
|
|
1294
1645
|
init_config();
|
|
1295
1646
|
init_dist();
|
|
1296
1647
|
init_git();
|
|
1297
1648
|
init_monorepo();
|
|
1298
|
-
CONFIG_PATH2 = (0,
|
|
1649
|
+
CONFIG_PATH2 = (0, import_path8.join)((0, import_os4.homedir)(), CONFIG_DIR);
|
|
1299
1650
|
PROVIDER_URLS = {
|
|
1300
1651
|
ollama: "http://localhost:11434",
|
|
1301
1652
|
lmstudio: "http://localhost:1234/v1",
|
|
@@ -1326,6 +1677,7 @@ Usage:
|
|
|
1326
1677
|
qc --push Commit and push to origin
|
|
1327
1678
|
qc pr Generate PR description from branch commits
|
|
1328
1679
|
qc changelog Generate changelog from commits since last tag
|
|
1680
|
+
qc changeset Automate pnpm changeset with AI
|
|
1329
1681
|
qc init Install prepare-commit-msg hook for auto-generation
|
|
1330
1682
|
qc login Sign in via browser
|
|
1331
1683
|
qc logout Clear local credentials
|
|
@@ -1337,7 +1689,7 @@ Options:
|
|
|
1337
1689
|
-m, --message-only Generate message only
|
|
1338
1690
|
-p, --push Commit and push after generating
|
|
1339
1691
|
--api-key <key> Use this API key (overrides credentials file)
|
|
1340
|
-
--base <branch> Base branch for qc pr (default: main)
|
|
1692
|
+
--base <branch> Base branch for qc pr, qc changeset (default: main)
|
|
1341
1693
|
--create Create PR with gh CLI after qc pr
|
|
1342
1694
|
--from <ref> Start ref for qc changelog (default: latest tag)
|
|
1343
1695
|
--to <ref> End ref for qc changelog (default: HEAD)
|
|
@@ -1411,6 +1763,8 @@ function parseArgs(args) {
|
|
|
1411
1763
|
command = "config";
|
|
1412
1764
|
} else if (arg === "upgrade") {
|
|
1413
1765
|
command = "upgrade";
|
|
1766
|
+
} else if (arg === "changeset") {
|
|
1767
|
+
command = "changeset";
|
|
1414
1768
|
} else if (arg === "--model" && i + 1 < args.length) {
|
|
1415
1769
|
model = args[++i];
|
|
1416
1770
|
} else if (arg === "--local" || arg === "--use-ollama" || arg === "--use-lmstudio" || arg === "--use-openrouter" || arg === "--use-cloudflare") {
|
|
@@ -1535,9 +1889,17 @@ async function main() {
|
|
|
1535
1889
|
});
|
|
1536
1890
|
return;
|
|
1537
1891
|
}
|
|
1892
|
+
if (command === "changeset") {
|
|
1893
|
+
const { changeset: changeset2 } = await Promise.resolve().then(() => (init_changeset(), changeset_exports));
|
|
1894
|
+
await changeset2({
|
|
1895
|
+
base: values.base,
|
|
1896
|
+
model: values.model ?? getConfig().model
|
|
1897
|
+
});
|
|
1898
|
+
return;
|
|
1899
|
+
}
|
|
1538
1900
|
if (command === "init") {
|
|
1539
1901
|
const { init: init2 } = await Promise.resolve().then(() => (init_init(), init_exports));
|
|
1540
|
-
|
|
1902
|
+
init2({ uninstall: values.uninstall });
|
|
1541
1903
|
return;
|
|
1542
1904
|
}
|
|
1543
1905
|
if (command === "team") {
|
|
@@ -1549,7 +1911,7 @@ async function main() {
|
|
|
1549
1911
|
if (command === "config") {
|
|
1550
1912
|
const { config: config2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
1551
1913
|
const positionals = argv.filter((a) => !a.startsWith("-") && a !== "config");
|
|
1552
|
-
|
|
1914
|
+
config2(positionals);
|
|
1553
1915
|
return;
|
|
1554
1916
|
}
|
|
1555
1917
|
if (command === "upgrade") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quikcommit/cli",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.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/
|
|
23
|
+
"url": "https://github.com/Quikcommit-Internal/public"
|
|
24
24
|
},
|
|
25
25
|
"publishConfig": {
|
|
26
26
|
"access": "public"
|
|
@@ -32,13 +32,14 @@
|
|
|
32
32
|
"vitest": "~4.0.18"
|
|
33
33
|
},
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"@quikcommit/shared": "
|
|
35
|
+
"@quikcommit/shared": "3.0.0"
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "node build.mjs",
|
|
39
39
|
"typecheck": "tsc --noEmit",
|
|
40
40
|
"start": "node dist/index.js",
|
|
41
41
|
"test": "vitest run",
|
|
42
|
-
"lint": "
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"lint:fix": "eslint . --fix"
|
|
43
44
|
}
|
|
44
45
|
}
|