@quikcommit/cli 7.0.0 → 8.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/dist/index.js +1362 -560
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -32,6 +32,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
32
32
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
33
33
|
mod
|
|
34
34
|
));
|
|
35
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
35
36
|
|
|
36
37
|
// ../shared/dist/types.js
|
|
37
38
|
var init_types = __esm({
|
|
@@ -61,6 +62,16 @@ var init_rules = __esm({
|
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
// ../shared/dist/tokens.js
|
|
66
|
+
function estimateTokens(text) {
|
|
67
|
+
return Math.ceil(text.length / 2.5);
|
|
68
|
+
}
|
|
69
|
+
var init_tokens = __esm({
|
|
70
|
+
"../shared/dist/tokens.js"() {
|
|
71
|
+
"use strict";
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
64
75
|
// ../shared/dist/index.js
|
|
65
76
|
var init_dist = __esm({
|
|
66
77
|
"../shared/dist/index.js"() {
|
|
@@ -68,6 +79,7 @@ var init_dist = __esm({
|
|
|
68
79
|
init_types();
|
|
69
80
|
init_constants();
|
|
70
81
|
init_rules();
|
|
82
|
+
init_tokens();
|
|
71
83
|
}
|
|
72
84
|
});
|
|
73
85
|
|
|
@@ -125,6 +137,149 @@ var init_config = __esm({
|
|
|
125
137
|
}
|
|
126
138
|
});
|
|
127
139
|
|
|
140
|
+
// src/commands/login.ts
|
|
141
|
+
var login_exports = {};
|
|
142
|
+
__export(login_exports, {
|
|
143
|
+
runLogin: () => runLogin
|
|
144
|
+
});
|
|
145
|
+
function openBrowser(url) {
|
|
146
|
+
try {
|
|
147
|
+
if ((0, import_os2.platform)() === "darwin") {
|
|
148
|
+
(0, import_child_process.execFileSync)("open", [url], { stdio: "pipe" });
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
if ((0, import_os2.platform)() === "linux") {
|
|
152
|
+
(0, import_child_process.execFileSync)("xdg-open", [url], { stdio: "pipe" });
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
if ((0, import_os2.platform)() === "win32") {
|
|
156
|
+
(0, import_child_process.execFileSync)("cmd", ["/c", "start", "", url], { stdio: "pipe" });
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
} catch {
|
|
160
|
+
}
|
|
161
|
+
return false;
|
|
162
|
+
}
|
|
163
|
+
async function runLogin() {
|
|
164
|
+
const codeRes = await fetch(`${API_URL}/api/auth/device/code`, {
|
|
165
|
+
method: "POST",
|
|
166
|
+
headers: { "Content-Type": "application/json" },
|
|
167
|
+
body: JSON.stringify({ client_id: CLIENT_ID })
|
|
168
|
+
});
|
|
169
|
+
if (!codeRes.ok) {
|
|
170
|
+
const err = await codeRes.json().catch(() => ({ error: codeRes.statusText }));
|
|
171
|
+
throw new Error(err.error ?? "Failed to start device flow");
|
|
172
|
+
}
|
|
173
|
+
const codeData = await codeRes.json();
|
|
174
|
+
const { device_code, user_code, verification_uri_complete, interval = 5 } = codeData;
|
|
175
|
+
if (!device_code || !user_code) {
|
|
176
|
+
throw new Error("Server did not return device codes");
|
|
177
|
+
}
|
|
178
|
+
console.log("Opening browser to sign in...");
|
|
179
|
+
console.log("");
|
|
180
|
+
console.log(` Your code: ${user_code}`);
|
|
181
|
+
console.log("");
|
|
182
|
+
const authUrl = verification_uri_complete ?? `${DASHBOARD_URL}/device?user_code=${encodeURIComponent(user_code)}`;
|
|
183
|
+
const opened = openBrowser(authUrl);
|
|
184
|
+
if (!opened) {
|
|
185
|
+
console.log("Could not open browser. Please visit:");
|
|
186
|
+
console.log(authUrl);
|
|
187
|
+
console.log("");
|
|
188
|
+
}
|
|
189
|
+
let frame = 0;
|
|
190
|
+
const spinner = setInterval(() => {
|
|
191
|
+
const elapsed = Math.floor((Date.now() - startTime) / 1e3);
|
|
192
|
+
process.stderr.write(
|
|
193
|
+
`\r${SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length]} Waiting for authorization... (${elapsed}s)`
|
|
194
|
+
);
|
|
195
|
+
}, 80);
|
|
196
|
+
let pollingInterval = interval * 1e3;
|
|
197
|
+
const startTime = Date.now();
|
|
198
|
+
try {
|
|
199
|
+
while (Date.now() - startTime < DEVICE_FLOW_TIMEOUT) {
|
|
200
|
+
await new Promise((r) => setTimeout(r, pollingInterval));
|
|
201
|
+
try {
|
|
202
|
+
const tokenRes = await fetch(`${API_URL}/api/auth/device/token`, {
|
|
203
|
+
method: "POST",
|
|
204
|
+
headers: { "Content-Type": "application/json" },
|
|
205
|
+
body: JSON.stringify({
|
|
206
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
207
|
+
device_code,
|
|
208
|
+
client_id: CLIENT_ID
|
|
209
|
+
})
|
|
210
|
+
});
|
|
211
|
+
const tokenData = await tokenRes.json();
|
|
212
|
+
if (tokenData.access_token) {
|
|
213
|
+
saveApiKey(tokenData.access_token);
|
|
214
|
+
process.stderr.write("\r\x1B[2K");
|
|
215
|
+
console.log("Successfully logged in!");
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
if (tokenData.error) {
|
|
219
|
+
switch (tokenData.error) {
|
|
220
|
+
case "authorization_pending":
|
|
221
|
+
break;
|
|
222
|
+
// continue polling
|
|
223
|
+
case "slow_down":
|
|
224
|
+
pollingInterval += 5e3;
|
|
225
|
+
break;
|
|
226
|
+
case "access_denied":
|
|
227
|
+
process.stderr.write("\r\x1B[2K");
|
|
228
|
+
console.error("Authorization was denied.");
|
|
229
|
+
process.exit(1);
|
|
230
|
+
break;
|
|
231
|
+
case "expired_token":
|
|
232
|
+
process.stderr.write("\r\x1B[2K");
|
|
233
|
+
console.error("Device code expired. Please try again.");
|
|
234
|
+
process.exit(1);
|
|
235
|
+
break;
|
|
236
|
+
default:
|
|
237
|
+
process.stderr.write("\r\x1B[2K");
|
|
238
|
+
console.error(`Error: ${tokenData.error_description ?? tokenData.error}`);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch {
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
process.stderr.write("\r\x1B[2K");
|
|
246
|
+
console.error("Login timed out. Please try again.");
|
|
247
|
+
process.exit(1);
|
|
248
|
+
} finally {
|
|
249
|
+
clearInterval(spinner);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
var import_child_process, import_os2, API_URL, DASHBOARD_URL, CLIENT_ID, SPINNER_FRAMES;
|
|
253
|
+
var init_login = __esm({
|
|
254
|
+
"src/commands/login.ts"() {
|
|
255
|
+
"use strict";
|
|
256
|
+
import_child_process = require("child_process");
|
|
257
|
+
import_os2 = require("os");
|
|
258
|
+
init_config();
|
|
259
|
+
init_dist();
|
|
260
|
+
API_URL = process.env.QC_API_URL ?? DEFAULT_API_URL;
|
|
261
|
+
DASHBOARD_URL = "https://app.quikcommit.dev";
|
|
262
|
+
CLIENT_ID = "qc-cli";
|
|
263
|
+
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// src/commands/logout.ts
|
|
268
|
+
var logout_exports = {};
|
|
269
|
+
__export(logout_exports, {
|
|
270
|
+
runLogout: () => runLogout
|
|
271
|
+
});
|
|
272
|
+
function runLogout() {
|
|
273
|
+
clearApiKey();
|
|
274
|
+
console.log("Logged out. Credentials cleared.");
|
|
275
|
+
}
|
|
276
|
+
var init_logout = __esm({
|
|
277
|
+
"src/commands/logout.ts"() {
|
|
278
|
+
"use strict";
|
|
279
|
+
init_config();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
128
283
|
// src/api.ts
|
|
129
284
|
var ApiClient;
|
|
130
285
|
var init_api = __esm({
|
|
@@ -163,8 +318,15 @@ var init_api = __esm({
|
|
|
163
318
|
}
|
|
164
319
|
return res.json();
|
|
165
320
|
}
|
|
166
|
-
async generateCommit(diff, changes, rules, model) {
|
|
167
|
-
const body = {
|
|
321
|
+
async generateCommit(diff, changes, rules, model, recentCommits, generationHints) {
|
|
322
|
+
const body = {
|
|
323
|
+
diff,
|
|
324
|
+
changes,
|
|
325
|
+
rules,
|
|
326
|
+
model,
|
|
327
|
+
recent_commits: recentCommits,
|
|
328
|
+
...generationHints && Object.keys(generationHints).length > 0 ? { generation_hints: generationHints } : {}
|
|
329
|
+
};
|
|
168
330
|
const data = await this.request(
|
|
169
331
|
"/v1/commit",
|
|
170
332
|
body
|
|
@@ -248,6 +410,37 @@ var init_api = __esm({
|
|
|
248
410
|
}
|
|
249
411
|
});
|
|
250
412
|
|
|
413
|
+
// src/commands/status.ts
|
|
414
|
+
var status_exports = {};
|
|
415
|
+
__export(status_exports, {
|
|
416
|
+
runStatus: () => runStatus
|
|
417
|
+
});
|
|
418
|
+
async function runStatus(apiKeyFlag) {
|
|
419
|
+
const apiKey = apiKeyFlag ?? getApiKey();
|
|
420
|
+
if (!apiKey) {
|
|
421
|
+
console.log("Not logged in. Run `qc login` to authenticate.");
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
console.log("Logged in: yes");
|
|
425
|
+
console.log(` API key: ...${apiKey.slice(-4)}`);
|
|
426
|
+
const client = new ApiClient({ apiKey });
|
|
427
|
+
const usage = await client.getUsage();
|
|
428
|
+
if (usage) {
|
|
429
|
+
console.log(`Plan: ${usage.plan}`);
|
|
430
|
+
console.log(`Usage: ${usage.commit_count}/${usage.limit} commits this period`);
|
|
431
|
+
console.log(`Remaining: ${usage.remaining}`);
|
|
432
|
+
} else {
|
|
433
|
+
console.log("Usage: (unable to fetch)");
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
var init_status = __esm({
|
|
437
|
+
"src/commands/status.ts"() {
|
|
438
|
+
"use strict";
|
|
439
|
+
init_config();
|
|
440
|
+
init_api();
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
|
|
251
444
|
// ../../node_modules/.pnpm/yaml@2.8.4/node_modules/yaml/dist/nodes/identity.js
|
|
252
445
|
var require_identity = __commonJS({
|
|
253
446
|
"../../node_modules/.pnpm/yaml@2.8.4/node_modules/yaml/dist/nodes/identity.js"(exports2) {
|
|
@@ -7564,7 +7757,7 @@ function validateRef(ref, name = "ref") {
|
|
|
7564
7757
|
}
|
|
7565
7758
|
function isGitRepo() {
|
|
7566
7759
|
try {
|
|
7567
|
-
(0,
|
|
7760
|
+
(0, import_child_process2.execFileSync)("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
7568
7761
|
stdio: "pipe"
|
|
7569
7762
|
});
|
|
7570
7763
|
return true;
|
|
@@ -7574,7 +7767,7 @@ function isGitRepo() {
|
|
|
7574
7767
|
}
|
|
7575
7768
|
function getGitRoot() {
|
|
7576
7769
|
try {
|
|
7577
|
-
return (0,
|
|
7770
|
+
return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
7578
7771
|
encoding: "utf-8"
|
|
7579
7772
|
}).trim();
|
|
7580
7773
|
} catch {
|
|
@@ -7590,37 +7783,37 @@ function getStagedDiff(excludes = []) {
|
|
|
7590
7783
|
args.push(`:(exclude)${pattern}`);
|
|
7591
7784
|
}
|
|
7592
7785
|
}
|
|
7593
|
-
return (0,
|
|
7786
|
+
return (0, import_child_process2.execFileSync)("git", args, {
|
|
7594
7787
|
encoding: "utf-8",
|
|
7595
7788
|
maxBuffer: 10 * 1024 * 1024
|
|
7596
7789
|
});
|
|
7597
7790
|
}
|
|
7598
7791
|
function getStagedFiles() {
|
|
7599
|
-
return (0,
|
|
7792
|
+
return (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
|
|
7600
7793
|
encoding: "utf-8"
|
|
7601
7794
|
});
|
|
7602
7795
|
}
|
|
7603
7796
|
function hasStagedChanges() {
|
|
7604
|
-
const output = (0,
|
|
7797
|
+
const output = (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
|
|
7605
7798
|
encoding: "utf-8"
|
|
7606
7799
|
});
|
|
7607
7800
|
return output.trim().length > 0;
|
|
7608
7801
|
}
|
|
7609
7802
|
function getUnstagedFiles() {
|
|
7610
|
-
const output = (0,
|
|
7803
|
+
const output = (0, import_child_process2.execFileSync)("git", ["status", "--porcelain"], {
|
|
7611
7804
|
encoding: "utf-8"
|
|
7612
7805
|
});
|
|
7613
7806
|
return output.trim().split("\n").filter(Boolean).filter((line) => !line.startsWith("??"));
|
|
7614
7807
|
}
|
|
7615
7808
|
function stageAll() {
|
|
7616
|
-
(0,
|
|
7809
|
+
(0, import_child_process2.execFileSync)("git", ["add", "-u"], { stdio: "pipe" });
|
|
7617
7810
|
}
|
|
7618
7811
|
function gitCommit(message) {
|
|
7619
|
-
const tmpDir = (0, import_fs2.mkdtempSync)((0, import_path2.join)((0,
|
|
7812
|
+
const tmpDir = (0, import_fs2.mkdtempSync)((0, import_path2.join)((0, import_os3.tmpdir)(), "qc-"));
|
|
7620
7813
|
const tmpFile = (0, import_path2.join)(tmpDir, "commit.txt");
|
|
7621
7814
|
(0, import_fs2.writeFileSync)(tmpFile, message, { mode: 384 });
|
|
7622
7815
|
try {
|
|
7623
|
-
(0,
|
|
7816
|
+
(0, import_child_process2.execFileSync)("git", ["commit", "-F", tmpFile], { stdio: "inherit" });
|
|
7624
7817
|
} finally {
|
|
7625
7818
|
try {
|
|
7626
7819
|
(0, import_fs2.unlinkSync)(tmpFile);
|
|
@@ -7630,11 +7823,11 @@ function gitCommit(message) {
|
|
|
7630
7823
|
}
|
|
7631
7824
|
}
|
|
7632
7825
|
function gitPush() {
|
|
7633
|
-
(0,
|
|
7826
|
+
(0, import_child_process2.execFileSync)("git", ["push"], { stdio: "inherit" });
|
|
7634
7827
|
}
|
|
7635
7828
|
function getBranchCommits(base = "main") {
|
|
7636
7829
|
validateRef(base, "base");
|
|
7637
|
-
const output = (0,
|
|
7830
|
+
const output = (0, import_child_process2.execFileSync)("git", ["log", `${base}..HEAD`, "--format=%s", "--max-count=1000"], {
|
|
7638
7831
|
encoding: "utf-8",
|
|
7639
7832
|
maxBuffer: 10 * 1024 * 1024
|
|
7640
7833
|
});
|
|
@@ -7642,19 +7835,19 @@ function getBranchCommits(base = "main") {
|
|
|
7642
7835
|
}
|
|
7643
7836
|
function getDiffStat(base = "main") {
|
|
7644
7837
|
validateRef(base, "base");
|
|
7645
|
-
return (0,
|
|
7838
|
+
return (0, import_child_process2.execFileSync)("git", ["diff", `${base}..HEAD`, "--stat"], {
|
|
7646
7839
|
encoding: "utf-8",
|
|
7647
7840
|
maxBuffer: 10 * 1024 * 1024
|
|
7648
7841
|
});
|
|
7649
7842
|
}
|
|
7650
7843
|
function getCurrentBranch() {
|
|
7651
|
-
return (0,
|
|
7844
|
+
return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
7652
7845
|
encoding: "utf-8"
|
|
7653
7846
|
}).trim();
|
|
7654
7847
|
}
|
|
7655
7848
|
function getLatestTag() {
|
|
7656
7849
|
try {
|
|
7657
|
-
return (0,
|
|
7850
|
+
return (0, import_child_process2.execFileSync)("git", ["describe", "--tags", "--abbrev=0"], {
|
|
7658
7851
|
encoding: "utf-8"
|
|
7659
7852
|
}).trim();
|
|
7660
7853
|
} catch {
|
|
@@ -7664,7 +7857,7 @@ function getLatestTag() {
|
|
|
7664
7857
|
function getCommitsSince(ref, to = "HEAD") {
|
|
7665
7858
|
validateRef(ref, "from ref");
|
|
7666
7859
|
validateRef(to, "to ref");
|
|
7667
|
-
const output = (0,
|
|
7860
|
+
const output = (0, import_child_process2.execFileSync)(
|
|
7668
7861
|
"git",
|
|
7669
7862
|
["log", `${ref}..${to}`, "--format=%H %s", "--max-count=1000"],
|
|
7670
7863
|
{ encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
|
|
@@ -7676,7 +7869,7 @@ function getCommitsSince(ref, to = "HEAD") {
|
|
|
7676
7869
|
}
|
|
7677
7870
|
function getChangedFilesSince(base = "main") {
|
|
7678
7871
|
validateRef(base, "base");
|
|
7679
|
-
const output = (0,
|
|
7872
|
+
const output = (0, import_child_process2.execFileSync)("git", ["diff", `${base}..HEAD`, "--name-only"], {
|
|
7680
7873
|
encoding: "utf-8",
|
|
7681
7874
|
maxBuffer: 10 * 1024 * 1024
|
|
7682
7875
|
});
|
|
@@ -7684,7 +7877,7 @@ function getChangedFilesSince(base = "main") {
|
|
|
7684
7877
|
}
|
|
7685
7878
|
function getOnlineLog(base = "main") {
|
|
7686
7879
|
validateRef(base, "base");
|
|
7687
|
-
return (0,
|
|
7880
|
+
return (0, import_child_process2.execFileSync)(
|
|
7688
7881
|
"git",
|
|
7689
7882
|
["log", `${base}..HEAD`, "--oneline", "--max-count=200"],
|
|
7690
7883
|
{
|
|
@@ -7695,19 +7888,65 @@ function getOnlineLog(base = "main") {
|
|
|
7695
7888
|
}
|
|
7696
7889
|
function getFullDiff(base = "main") {
|
|
7697
7890
|
validateRef(base, "base");
|
|
7698
|
-
return (0,
|
|
7891
|
+
return (0, import_child_process2.execFileSync)("git", ["diff", `${base}..HEAD`], {
|
|
7699
7892
|
encoding: "utf-8",
|
|
7700
7893
|
maxBuffer: 10 * 1024 * 1024
|
|
7701
7894
|
});
|
|
7702
7895
|
}
|
|
7703
|
-
|
|
7896
|
+
function getShortStagedFiles(max = 3) {
|
|
7897
|
+
const output = (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
|
|
7898
|
+
encoding: "utf-8"
|
|
7899
|
+
});
|
|
7900
|
+
const all = output.trim().split("\n").filter(Boolean);
|
|
7901
|
+
return { files: all.slice(0, max), total: all.length };
|
|
7902
|
+
}
|
|
7903
|
+
function getCommitHash() {
|
|
7904
|
+
return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--short", "HEAD"], {
|
|
7905
|
+
encoding: "utf-8"
|
|
7906
|
+
}).trim();
|
|
7907
|
+
}
|
|
7908
|
+
function getPushStats() {
|
|
7909
|
+
try {
|
|
7910
|
+
const branch = (0, import_child_process2.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
7911
|
+
encoding: "utf-8"
|
|
7912
|
+
}).trim();
|
|
7913
|
+
const countOutput = (0, import_child_process2.execFileSync)(
|
|
7914
|
+
"git",
|
|
7915
|
+
["rev-list", "--count", `origin/${branch}..HEAD`],
|
|
7916
|
+
{ encoding: "utf-8" }
|
|
7917
|
+
).trim();
|
|
7918
|
+
const parsedCount = parseInt(countOutput, 10);
|
|
7919
|
+
const commits = Number.isFinite(parsedCount) ? parsedCount : 0;
|
|
7920
|
+
const stat = (0, import_child_process2.execFileSync)(
|
|
7921
|
+
"git",
|
|
7922
|
+
["diff", "--shortstat", `origin/${branch}..HEAD`],
|
|
7923
|
+
{ encoding: "utf-8" }
|
|
7924
|
+
).trim();
|
|
7925
|
+
return { commits, stat };
|
|
7926
|
+
} catch {
|
|
7927
|
+
return null;
|
|
7928
|
+
}
|
|
7929
|
+
}
|
|
7930
|
+
function getRecentBranchCommits(count = 5) {
|
|
7931
|
+
try {
|
|
7932
|
+
const output = (0, import_child_process2.execFileSync)(
|
|
7933
|
+
"git",
|
|
7934
|
+
["log", "--format=%s%n%b%n---", `--max-count=${count}`, "HEAD"],
|
|
7935
|
+
{ encoding: "utf-8", maxBuffer: 1024 * 1024 }
|
|
7936
|
+
);
|
|
7937
|
+
return output.split("---\n").map((entry) => entry.trim()).filter(Boolean).slice(0, count);
|
|
7938
|
+
} catch {
|
|
7939
|
+
return [];
|
|
7940
|
+
}
|
|
7941
|
+
}
|
|
7942
|
+
var import_child_process2, import_fs2, import_path2, import_os3, SAFE_GIT_REF;
|
|
7704
7943
|
var init_git = __esm({
|
|
7705
7944
|
"src/git.ts"() {
|
|
7706
7945
|
"use strict";
|
|
7707
|
-
|
|
7946
|
+
import_child_process2 = require("child_process");
|
|
7708
7947
|
import_fs2 = require("fs");
|
|
7709
7948
|
import_path2 = require("path");
|
|
7710
|
-
|
|
7949
|
+
import_os3 = require("os");
|
|
7711
7950
|
SAFE_GIT_REF = /^[a-zA-Z0-9._\-/~:^@]+$/;
|
|
7712
7951
|
}
|
|
7713
7952
|
});
|
|
@@ -7774,7 +8013,7 @@ function mapRulesToCommitRules(rules) {
|
|
|
7774
8013
|
}
|
|
7775
8014
|
function tryNpxPrintConfig(root) {
|
|
7776
8015
|
try {
|
|
7777
|
-
const output = (0,
|
|
8016
|
+
const output = (0, import_child_process3.execFileSync)("npx", ["--no", "commitlint", "--print-config"], {
|
|
7778
8017
|
encoding: "utf-8",
|
|
7779
8018
|
cwd: root,
|
|
7780
8019
|
timeout: 1e4,
|
|
@@ -7791,7 +8030,7 @@ function tryNodeEval(configPath) {
|
|
|
7791
8030
|
const fileUrl = (0, import_node_url.pathToFileURL)(configPath).href;
|
|
7792
8031
|
const script = `import cfg from ${JSON.stringify(fileUrl)}; process.stdout.write(JSON.stringify(cfg.default ?? cfg));`;
|
|
7793
8032
|
try {
|
|
7794
|
-
const output = (0,
|
|
8033
|
+
const output = (0, import_child_process3.execFileSync)("node", ["--input-type=module"], {
|
|
7795
8034
|
input: script,
|
|
7796
8035
|
encoding: "utf-8",
|
|
7797
8036
|
timeout: 1e4,
|
|
@@ -7808,7 +8047,7 @@ function tryNodeEvalTs(configPath, root) {
|
|
|
7808
8047
|
const fileUrl = (0, import_node_url.pathToFileURL)(configPath).href;
|
|
7809
8048
|
const script = `import cfg from ${JSON.stringify(fileUrl)}; process.stdout.write(JSON.stringify(cfg.default ?? cfg));`;
|
|
7810
8049
|
try {
|
|
7811
|
-
const output = (0,
|
|
8050
|
+
const output = (0, import_child_process3.execFileSync)("node", ["--experimental-strip-types", "--input-type=module"], {
|
|
7812
8051
|
input: script,
|
|
7813
8052
|
encoding: "utf-8",
|
|
7814
8053
|
cwd: root,
|
|
@@ -7822,7 +8061,7 @@ function tryNodeEvalTs(configPath, root) {
|
|
|
7822
8061
|
}
|
|
7823
8062
|
try {
|
|
7824
8063
|
const tsxScript = `import cfg from ${JSON.stringify(fileUrl)}; console.log(JSON.stringify(cfg.default ?? cfg));`;
|
|
7825
|
-
const output = (0,
|
|
8064
|
+
const output = (0, import_child_process3.execFileSync)("npx", ["--no", "tsx", "-e", tsxScript], {
|
|
7826
8065
|
encoding: "utf-8",
|
|
7827
8066
|
cwd: root,
|
|
7828
8067
|
timeout: 15e3,
|
|
@@ -7875,11 +8114,11 @@ async function detectCommitlintRules() {
|
|
|
7875
8114
|
return void 0;
|
|
7876
8115
|
}
|
|
7877
8116
|
}
|
|
7878
|
-
var
|
|
8117
|
+
var import_child_process3, import_fs3, import_path3, import_node_url, import_yaml, CONFIG_FILES;
|
|
7879
8118
|
var init_commitlint = __esm({
|
|
7880
8119
|
"src/commitlint.ts"() {
|
|
7881
8120
|
"use strict";
|
|
7882
|
-
|
|
8121
|
+
import_child_process3 = require("child_process");
|
|
7883
8122
|
import_fs3 = require("fs");
|
|
7884
8123
|
import_path3 = require("path");
|
|
7885
8124
|
import_node_url = require("node:url");
|
|
@@ -7900,351 +8139,37 @@ var init_commitlint = __esm({
|
|
|
7900
8139
|
}
|
|
7901
8140
|
});
|
|
7902
8141
|
|
|
7903
|
-
// src/
|
|
7904
|
-
var
|
|
7905
|
-
__export(
|
|
7906
|
-
|
|
7907
|
-
detectWorkspace: () => detectWorkspace,
|
|
7908
|
-
getPackageForFile: () => getPackageForFile
|
|
8142
|
+
// src/commands/pr.ts
|
|
8143
|
+
var pr_exports = {};
|
|
8144
|
+
__export(pr_exports, {
|
|
8145
|
+
pr: () => pr
|
|
7909
8146
|
});
|
|
7910
|
-
function
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
} catch {
|
|
7918
|
-
return start;
|
|
7919
|
-
}
|
|
7920
|
-
}
|
|
7921
|
-
function detectWorkspace(cwd = findGitRoot(process.cwd())) {
|
|
7922
|
-
const pnpmWs = (0, import_path4.join)(cwd, "pnpm-workspace.yaml");
|
|
7923
|
-
if ((0, import_fs4.existsSync)(pnpmWs)) {
|
|
7924
|
-
const content = (0, import_fs4.readFileSync)(pnpmWs, "utf-8");
|
|
7925
|
-
const match = content.match(/packages:\s*\n((?:\s+-\s+.+\n?)*)/);
|
|
7926
|
-
if (match) {
|
|
7927
|
-
const packages = match[1].split("\n").map((l) => l.replace(/^\s+-\s+/, "").replace(/["']/g, "").trim()).filter(Boolean);
|
|
7928
|
-
return { type: "pnpm", packages, root: cwd };
|
|
7929
|
-
}
|
|
7930
|
-
}
|
|
7931
|
-
const lerna = (0, import_path4.join)(cwd, "lerna.json");
|
|
7932
|
-
if ((0, import_fs4.existsSync)(lerna)) {
|
|
8147
|
+
function findPullRequestTemplate(gitRoot) {
|
|
8148
|
+
const fileCandidates = [
|
|
8149
|
+
(0, import_path4.join)(gitRoot, ".github", "pull_request_template.md"),
|
|
8150
|
+
(0, import_path4.join)(gitRoot, ".github", "PULL_REQUEST_TEMPLATE.md"),
|
|
8151
|
+
(0, import_path4.join)(gitRoot, "pull_request_template.md")
|
|
8152
|
+
];
|
|
8153
|
+
for (const p of fileCandidates) {
|
|
7933
8154
|
try {
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
packages: config2.packages ?? ["packages/*"],
|
|
7938
|
-
root: cwd
|
|
7939
|
-
};
|
|
8155
|
+
if ((0, import_fs4.existsSync)(p) && (0, import_fs4.statSync)(p).isFile()) {
|
|
8156
|
+
return { path: p, content: (0, import_fs4.readFileSync)(p, "utf-8") };
|
|
8157
|
+
}
|
|
7940
8158
|
} catch {
|
|
7941
8159
|
}
|
|
7942
8160
|
}
|
|
7943
|
-
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
const pkgPath2 = (0, import_path4.join)(cwd, "package.json");
|
|
7952
|
-
if ((0, import_fs4.existsSync)(pkgPath2)) {
|
|
7953
|
-
try {
|
|
7954
|
-
const config2 = JSON.parse((0, import_fs4.readFileSync)(pkgPath2, "utf-8"));
|
|
7955
|
-
if (config2.workspaces) {
|
|
7956
|
-
const ws = Array.isArray(config2.workspaces) ? config2.workspaces : config2.workspaces.packages ?? [];
|
|
7957
|
-
return { type: "turbo", packages: ws, root: cwd };
|
|
8161
|
+
const multiDir = (0, import_path4.join)(gitRoot, ".github", "PULL_REQUEST_TEMPLATE");
|
|
8162
|
+
try {
|
|
8163
|
+
if ((0, import_fs4.existsSync)(multiDir) && (0, import_fs4.statSync)(multiDir).isDirectory()) {
|
|
8164
|
+
const names = (0, import_fs4.readdirSync)(multiDir).filter((f) => f.toLowerCase().endsWith(".md")).sort();
|
|
8165
|
+
if (names.length > 0) {
|
|
8166
|
+
const p = (0, import_path4.join)(multiDir, names[0]);
|
|
8167
|
+
if ((0, import_fs4.statSync)(p).isFile()) {
|
|
8168
|
+
return { path: p, content: (0, import_fs4.readFileSync)(p, "utf-8") };
|
|
7958
8169
|
}
|
|
7959
|
-
} catch {
|
|
7960
|
-
}
|
|
7961
|
-
}
|
|
7962
|
-
}
|
|
7963
|
-
const pkgPath = (0, import_path4.join)(cwd, "package.json");
|
|
7964
|
-
if ((0, import_fs4.existsSync)(pkgPath)) {
|
|
7965
|
-
try {
|
|
7966
|
-
const config2 = JSON.parse((0, import_fs4.readFileSync)(pkgPath, "utf-8"));
|
|
7967
|
-
if (config2.workspaces) {
|
|
7968
|
-
const ws = Array.isArray(config2.workspaces) ? config2.workspaces : config2.workspaces.packages ?? [];
|
|
7969
|
-
return { type: "npm", packages: ws, root: cwd };
|
|
7970
8170
|
}
|
|
7971
|
-
} catch {
|
|
7972
8171
|
}
|
|
7973
|
-
}
|
|
7974
|
-
return null;
|
|
7975
|
-
}
|
|
7976
|
-
function matchGlobPattern(rel, pattern) {
|
|
7977
|
-
const dir = pattern.replace(/\/?\*\*?$/, "").replace(/\/$/, "");
|
|
7978
|
-
if (!dir || dir === "*" || dir === "**") {
|
|
7979
|
-
const pkg = rel.split("/")[0];
|
|
7980
|
-
return pkg || null;
|
|
7981
|
-
}
|
|
7982
|
-
const starIdx = dir.indexOf("*");
|
|
7983
|
-
if (starIdx !== -1) {
|
|
7984
|
-
const prefix2 = dir.slice(0, starIdx);
|
|
7985
|
-
if (rel.startsWith(prefix2)) {
|
|
7986
|
-
const rest = rel.slice(prefix2.length);
|
|
7987
|
-
const pkg = rest.split("/")[0];
|
|
7988
|
-
return pkg || null;
|
|
7989
|
-
}
|
|
7990
|
-
return null;
|
|
7991
|
-
}
|
|
7992
|
-
const prefix = dir + "/";
|
|
7993
|
-
const hasGlob = /\*/.test(pattern);
|
|
7994
|
-
if (hasGlob) {
|
|
7995
|
-
if (rel.startsWith(prefix)) {
|
|
7996
|
-
const rest = rel.slice(prefix.length);
|
|
7997
|
-
const pkg = rest.split("/")[0];
|
|
7998
|
-
return pkg || null;
|
|
7999
|
-
}
|
|
8000
|
-
} else {
|
|
8001
|
-
if (rel === dir || rel.startsWith(prefix)) {
|
|
8002
|
-
const segments = dir.split("/").filter(Boolean);
|
|
8003
|
-
return segments[segments.length - 1] ?? null;
|
|
8004
|
-
}
|
|
8005
|
-
}
|
|
8006
|
-
return null;
|
|
8007
|
-
}
|
|
8008
|
-
function getPackageForFile(filePath, workspace) {
|
|
8009
|
-
const absPath = filePath.startsWith("/") ? filePath : (0, import_path4.join)(workspace.root, filePath);
|
|
8010
|
-
const rel = (0, import_path4.relative)(workspace.root, absPath);
|
|
8011
|
-
for (const pattern of workspace.packages) {
|
|
8012
|
-
const packageName = matchGlobPattern(rel, pattern);
|
|
8013
|
-
if (packageName) return packageName;
|
|
8014
|
-
}
|
|
8015
|
-
return null;
|
|
8016
|
-
}
|
|
8017
|
-
function autoDetectScope(stagedFiles, workspace) {
|
|
8018
|
-
const packages = /* @__PURE__ */ new Set();
|
|
8019
|
-
for (const file of stagedFiles) {
|
|
8020
|
-
const filePath = file.startsWith("/") ? file : (0, import_path4.join)(workspace.root, file);
|
|
8021
|
-
const pkg = getPackageForFile(filePath, workspace);
|
|
8022
|
-
if (pkg) packages.add(pkg);
|
|
8023
|
-
}
|
|
8024
|
-
if (packages.size === 1) return [...packages][0];
|
|
8025
|
-
if (packages.size > 1 && packages.size <= 3) return [...packages].join(",");
|
|
8026
|
-
if (packages.size > 3) {
|
|
8027
|
-
console.error(
|
|
8028
|
-
`[qc] Changes span ${packages.size} packages; skipping auto-scope detection.`
|
|
8029
|
-
);
|
|
8030
|
-
}
|
|
8031
|
-
return null;
|
|
8032
|
-
}
|
|
8033
|
-
var import_child_process3, import_fs4, import_path4;
|
|
8034
|
-
var init_monorepo = __esm({
|
|
8035
|
-
"src/monorepo.ts"() {
|
|
8036
|
-
"use strict";
|
|
8037
|
-
import_child_process3 = require("child_process");
|
|
8038
|
-
import_fs4 = require("fs");
|
|
8039
|
-
import_path4 = require("path");
|
|
8040
|
-
}
|
|
8041
|
-
});
|
|
8042
|
-
|
|
8043
|
-
// src/commands/login.ts
|
|
8044
|
-
var login_exports = {};
|
|
8045
|
-
__export(login_exports, {
|
|
8046
|
-
runLogin: () => runLogin
|
|
8047
|
-
});
|
|
8048
|
-
function openBrowser(url) {
|
|
8049
|
-
try {
|
|
8050
|
-
if ((0, import_os3.platform)() === "darwin") {
|
|
8051
|
-
(0, import_child_process4.execFileSync)("open", [url], { stdio: "pipe" });
|
|
8052
|
-
return true;
|
|
8053
|
-
}
|
|
8054
|
-
if ((0, import_os3.platform)() === "linux") {
|
|
8055
|
-
(0, import_child_process4.execFileSync)("xdg-open", [url], { stdio: "pipe" });
|
|
8056
|
-
return true;
|
|
8057
|
-
}
|
|
8058
|
-
if ((0, import_os3.platform)() === "win32") {
|
|
8059
|
-
(0, import_child_process4.execFileSync)("cmd", ["/c", "start", "", url], { stdio: "pipe" });
|
|
8060
|
-
return true;
|
|
8061
|
-
}
|
|
8062
|
-
} catch {
|
|
8063
|
-
}
|
|
8064
|
-
return false;
|
|
8065
|
-
}
|
|
8066
|
-
async function runLogin() {
|
|
8067
|
-
const codeRes = await fetch(`${API_URL}/api/auth/device/code`, {
|
|
8068
|
-
method: "POST",
|
|
8069
|
-
headers: { "Content-Type": "application/json" },
|
|
8070
|
-
body: JSON.stringify({ client_id: CLIENT_ID })
|
|
8071
|
-
});
|
|
8072
|
-
if (!codeRes.ok) {
|
|
8073
|
-
const err = await codeRes.json().catch(() => ({ error: codeRes.statusText }));
|
|
8074
|
-
throw new Error(err.error ?? "Failed to start device flow");
|
|
8075
|
-
}
|
|
8076
|
-
const codeData = await codeRes.json();
|
|
8077
|
-
const { device_code, user_code, verification_uri_complete, interval = 5 } = codeData;
|
|
8078
|
-
if (!device_code || !user_code) {
|
|
8079
|
-
throw new Error("Server did not return device codes");
|
|
8080
|
-
}
|
|
8081
|
-
console.log("Opening browser to sign in...");
|
|
8082
|
-
console.log("");
|
|
8083
|
-
console.log(` Your code: ${user_code}`);
|
|
8084
|
-
console.log("");
|
|
8085
|
-
const authUrl = verification_uri_complete ?? `${DASHBOARD_URL}/device?user_code=${encodeURIComponent(user_code)}`;
|
|
8086
|
-
const opened = openBrowser(authUrl);
|
|
8087
|
-
if (!opened) {
|
|
8088
|
-
console.log("Could not open browser. Please visit:");
|
|
8089
|
-
console.log(authUrl);
|
|
8090
|
-
console.log("");
|
|
8091
|
-
}
|
|
8092
|
-
let frame = 0;
|
|
8093
|
-
const spinner = setInterval(() => {
|
|
8094
|
-
const elapsed = Math.floor((Date.now() - startTime) / 1e3);
|
|
8095
|
-
process.stderr.write(
|
|
8096
|
-
`\r${SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length]} Waiting for authorization... (${elapsed}s)`
|
|
8097
|
-
);
|
|
8098
|
-
}, 80);
|
|
8099
|
-
let pollingInterval = interval * 1e3;
|
|
8100
|
-
const startTime = Date.now();
|
|
8101
|
-
try {
|
|
8102
|
-
while (Date.now() - startTime < DEVICE_FLOW_TIMEOUT) {
|
|
8103
|
-
await new Promise((r) => setTimeout(r, pollingInterval));
|
|
8104
|
-
try {
|
|
8105
|
-
const tokenRes = await fetch(`${API_URL}/api/auth/device/token`, {
|
|
8106
|
-
method: "POST",
|
|
8107
|
-
headers: { "Content-Type": "application/json" },
|
|
8108
|
-
body: JSON.stringify({
|
|
8109
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
8110
|
-
device_code,
|
|
8111
|
-
client_id: CLIENT_ID
|
|
8112
|
-
})
|
|
8113
|
-
});
|
|
8114
|
-
const tokenData = await tokenRes.json();
|
|
8115
|
-
if (tokenData.access_token) {
|
|
8116
|
-
saveApiKey(tokenData.access_token);
|
|
8117
|
-
process.stderr.write("\r\x1B[2K");
|
|
8118
|
-
console.log("Successfully logged in!");
|
|
8119
|
-
return;
|
|
8120
|
-
}
|
|
8121
|
-
if (tokenData.error) {
|
|
8122
|
-
switch (tokenData.error) {
|
|
8123
|
-
case "authorization_pending":
|
|
8124
|
-
break;
|
|
8125
|
-
// continue polling
|
|
8126
|
-
case "slow_down":
|
|
8127
|
-
pollingInterval += 5e3;
|
|
8128
|
-
break;
|
|
8129
|
-
case "access_denied":
|
|
8130
|
-
process.stderr.write("\r\x1B[2K");
|
|
8131
|
-
console.error("Authorization was denied.");
|
|
8132
|
-
process.exit(1);
|
|
8133
|
-
break;
|
|
8134
|
-
case "expired_token":
|
|
8135
|
-
process.stderr.write("\r\x1B[2K");
|
|
8136
|
-
console.error("Device code expired. Please try again.");
|
|
8137
|
-
process.exit(1);
|
|
8138
|
-
break;
|
|
8139
|
-
default:
|
|
8140
|
-
process.stderr.write("\r\x1B[2K");
|
|
8141
|
-
console.error(`Error: ${tokenData.error_description ?? tokenData.error}`);
|
|
8142
|
-
process.exit(1);
|
|
8143
|
-
}
|
|
8144
|
-
}
|
|
8145
|
-
} catch {
|
|
8146
|
-
}
|
|
8147
|
-
}
|
|
8148
|
-
process.stderr.write("\r\x1B[2K");
|
|
8149
|
-
console.error("Login timed out. Please try again.");
|
|
8150
|
-
process.exit(1);
|
|
8151
|
-
} finally {
|
|
8152
|
-
clearInterval(spinner);
|
|
8153
|
-
}
|
|
8154
|
-
}
|
|
8155
|
-
var import_child_process4, import_os3, API_URL, DASHBOARD_URL, CLIENT_ID, SPINNER_FRAMES;
|
|
8156
|
-
var init_login = __esm({
|
|
8157
|
-
"src/commands/login.ts"() {
|
|
8158
|
-
"use strict";
|
|
8159
|
-
import_child_process4 = require("child_process");
|
|
8160
|
-
import_os3 = require("os");
|
|
8161
|
-
init_config();
|
|
8162
|
-
init_dist();
|
|
8163
|
-
API_URL = process.env.QC_API_URL ?? DEFAULT_API_URL;
|
|
8164
|
-
DASHBOARD_URL = "https://app.quikcommit.dev";
|
|
8165
|
-
CLIENT_ID = "qc-cli";
|
|
8166
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
8167
|
-
}
|
|
8168
|
-
});
|
|
8169
|
-
|
|
8170
|
-
// src/commands/logout.ts
|
|
8171
|
-
var logout_exports = {};
|
|
8172
|
-
__export(logout_exports, {
|
|
8173
|
-
runLogout: () => runLogout
|
|
8174
|
-
});
|
|
8175
|
-
function runLogout() {
|
|
8176
|
-
clearApiKey();
|
|
8177
|
-
console.log("Logged out. Credentials cleared.");
|
|
8178
|
-
}
|
|
8179
|
-
var init_logout = __esm({
|
|
8180
|
-
"src/commands/logout.ts"() {
|
|
8181
|
-
"use strict";
|
|
8182
|
-
init_config();
|
|
8183
|
-
}
|
|
8184
|
-
});
|
|
8185
|
-
|
|
8186
|
-
// src/commands/status.ts
|
|
8187
|
-
var status_exports = {};
|
|
8188
|
-
__export(status_exports, {
|
|
8189
|
-
runStatus: () => runStatus
|
|
8190
|
-
});
|
|
8191
|
-
async function runStatus(apiKeyFlag) {
|
|
8192
|
-
const apiKey = apiKeyFlag ?? getApiKey();
|
|
8193
|
-
if (!apiKey) {
|
|
8194
|
-
console.log("Not logged in. Run `qc login` to authenticate.");
|
|
8195
|
-
return;
|
|
8196
|
-
}
|
|
8197
|
-
console.log("Logged in: yes");
|
|
8198
|
-
console.log(` API key: ...${apiKey.slice(-4)}`);
|
|
8199
|
-
const client = new ApiClient({ apiKey });
|
|
8200
|
-
const usage = await client.getUsage();
|
|
8201
|
-
if (usage) {
|
|
8202
|
-
console.log(`Plan: ${usage.plan}`);
|
|
8203
|
-
console.log(`Usage: ${usage.commit_count}/${usage.limit} commits this period`);
|
|
8204
|
-
console.log(`Remaining: ${usage.remaining}`);
|
|
8205
|
-
} else {
|
|
8206
|
-
console.log("Usage: (unable to fetch)");
|
|
8207
|
-
}
|
|
8208
|
-
}
|
|
8209
|
-
var init_status = __esm({
|
|
8210
|
-
"src/commands/status.ts"() {
|
|
8211
|
-
"use strict";
|
|
8212
|
-
init_config();
|
|
8213
|
-
init_api();
|
|
8214
|
-
}
|
|
8215
|
-
});
|
|
8216
|
-
|
|
8217
|
-
// src/commands/pr.ts
|
|
8218
|
-
var pr_exports = {};
|
|
8219
|
-
__export(pr_exports, {
|
|
8220
|
-
pr: () => pr
|
|
8221
|
-
});
|
|
8222
|
-
function findPullRequestTemplate(gitRoot) {
|
|
8223
|
-
const fileCandidates = [
|
|
8224
|
-
(0, import_path5.join)(gitRoot, ".github", "pull_request_template.md"),
|
|
8225
|
-
(0, import_path5.join)(gitRoot, ".github", "PULL_REQUEST_TEMPLATE.md"),
|
|
8226
|
-
(0, import_path5.join)(gitRoot, "pull_request_template.md")
|
|
8227
|
-
];
|
|
8228
|
-
for (const p of fileCandidates) {
|
|
8229
|
-
try {
|
|
8230
|
-
if ((0, import_fs5.existsSync)(p) && (0, import_fs5.statSync)(p).isFile()) {
|
|
8231
|
-
return { path: p, content: (0, import_fs5.readFileSync)(p, "utf-8") };
|
|
8232
|
-
}
|
|
8233
|
-
} catch {
|
|
8234
|
-
}
|
|
8235
|
-
}
|
|
8236
|
-
const multiDir = (0, import_path5.join)(gitRoot, ".github", "PULL_REQUEST_TEMPLATE");
|
|
8237
|
-
try {
|
|
8238
|
-
if ((0, import_fs5.existsSync)(multiDir) && (0, import_fs5.statSync)(multiDir).isDirectory()) {
|
|
8239
|
-
const names = (0, import_fs5.readdirSync)(multiDir).filter((f) => f.toLowerCase().endsWith(".md")).sort();
|
|
8240
|
-
if (names.length > 0) {
|
|
8241
|
-
const p = (0, import_path5.join)(multiDir, names[0]);
|
|
8242
|
-
if ((0, import_fs5.statSync)(p).isFile()) {
|
|
8243
|
-
return { path: p, content: (0, import_fs5.readFileSync)(p, "utf-8") };
|
|
8244
|
-
}
|
|
8245
|
-
}
|
|
8246
|
-
}
|
|
8247
|
-
} catch {
|
|
8172
|
+
} catch {
|
|
8248
8173
|
}
|
|
8249
8174
|
return void 0;
|
|
8250
8175
|
}
|
|
@@ -8257,7 +8182,7 @@ async function pr(options) {
|
|
|
8257
8182
|
let prTemplate;
|
|
8258
8183
|
if (templateHit) {
|
|
8259
8184
|
prTemplate = templateHit.content.substring(0, 16 * 1024);
|
|
8260
|
-
console.error(`[qc] Using PR template from ${(0,
|
|
8185
|
+
console.error(`[qc] Using PR template from ${(0, import_path4.relative)(gitRoot, templateHit.path)}`);
|
|
8261
8186
|
}
|
|
8262
8187
|
const currentBranch = getCurrentBranch().slice(0, MAX_PR_CURRENT_BRANCH_CHARS);
|
|
8263
8188
|
if (commits.length === 0) {
|
|
@@ -8293,7 +8218,7 @@ Title: ${trimmedTitle}
|
|
|
8293
8218
|
if (options.create) {
|
|
8294
8219
|
try {
|
|
8295
8220
|
const prTitle = trimmedTitle || result.message.split("\n").find((l) => l.trim()) || result.message.substring(0, 72).trim();
|
|
8296
|
-
(0,
|
|
8221
|
+
(0, import_child_process4.execFileSync)("gh", ["pr", "create", "--title", prTitle, "--body", result.message], {
|
|
8297
8222
|
stdio: "inherit"
|
|
8298
8223
|
});
|
|
8299
8224
|
} catch {
|
|
@@ -8302,13 +8227,13 @@ Title: ${trimmedTitle}
|
|
|
8302
8227
|
}
|
|
8303
8228
|
}
|
|
8304
8229
|
}
|
|
8305
|
-
var
|
|
8230
|
+
var import_child_process4, import_fs4, import_path4;
|
|
8306
8231
|
var init_pr = __esm({
|
|
8307
8232
|
"src/commands/pr.ts"() {
|
|
8308
8233
|
"use strict";
|
|
8309
|
-
|
|
8310
|
-
|
|
8311
|
-
|
|
8234
|
+
import_child_process4 = require("child_process");
|
|
8235
|
+
import_fs4 = require("fs");
|
|
8236
|
+
import_path4 = require("path");
|
|
8312
8237
|
init_dist();
|
|
8313
8238
|
init_config();
|
|
8314
8239
|
init_api();
|
|
@@ -8369,21 +8294,21 @@ async function changelog(options) {
|
|
|
8369
8294
|
`;
|
|
8370
8295
|
const changelogEntry = header + result.message;
|
|
8371
8296
|
if (options.write) {
|
|
8372
|
-
const path = (0,
|
|
8373
|
-
const existing = (0,
|
|
8297
|
+
const path = (0, import_path5.join)(getGitRoot(), "CHANGELOG.md");
|
|
8298
|
+
const existing = (0, import_fs5.existsSync)(path) ? (0, import_fs5.readFileSync)(path, "utf-8") : "";
|
|
8374
8299
|
const newContent = changelogEntry + (existing ? "\n\n" + existing : "");
|
|
8375
|
-
(0,
|
|
8300
|
+
(0, import_fs5.writeFileSync)(path, newContent);
|
|
8376
8301
|
console.error(`Wrote to ${path}`);
|
|
8377
8302
|
} else {
|
|
8378
8303
|
console.log(changelogEntry);
|
|
8379
8304
|
}
|
|
8380
8305
|
}
|
|
8381
|
-
var
|
|
8306
|
+
var import_fs5, import_path5, CONVENTIONAL_TYPE_RE;
|
|
8382
8307
|
var init_changelog = __esm({
|
|
8383
8308
|
"src/commands/changelog.ts"() {
|
|
8384
8309
|
"use strict";
|
|
8385
|
-
|
|
8386
|
-
|
|
8310
|
+
import_fs5 = require("fs");
|
|
8311
|
+
import_path5 = require("path");
|
|
8387
8312
|
init_config();
|
|
8388
8313
|
init_api();
|
|
8389
8314
|
init_git();
|
|
@@ -8391,6 +8316,146 @@ var init_changelog = __esm({
|
|
|
8391
8316
|
}
|
|
8392
8317
|
});
|
|
8393
8318
|
|
|
8319
|
+
// src/monorepo.ts
|
|
8320
|
+
var monorepo_exports = {};
|
|
8321
|
+
__export(monorepo_exports, {
|
|
8322
|
+
autoDetectScope: () => autoDetectScope,
|
|
8323
|
+
detectWorkspace: () => detectWorkspace,
|
|
8324
|
+
getPackageForFile: () => getPackageForFile
|
|
8325
|
+
});
|
|
8326
|
+
function findGitRoot(start) {
|
|
8327
|
+
try {
|
|
8328
|
+
return (0, import_child_process5.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
8329
|
+
encoding: "utf-8",
|
|
8330
|
+
cwd: start,
|
|
8331
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8332
|
+
}).trim();
|
|
8333
|
+
} catch {
|
|
8334
|
+
return start;
|
|
8335
|
+
}
|
|
8336
|
+
}
|
|
8337
|
+
function detectWorkspace(cwd = findGitRoot(process.cwd())) {
|
|
8338
|
+
const pnpmWs = (0, import_path6.join)(cwd, "pnpm-workspace.yaml");
|
|
8339
|
+
if ((0, import_fs6.existsSync)(pnpmWs)) {
|
|
8340
|
+
const content = (0, import_fs6.readFileSync)(pnpmWs, "utf-8");
|
|
8341
|
+
const match = content.match(/packages:\s*\n((?:\s+-\s+.+\n?)*)/);
|
|
8342
|
+
if (match) {
|
|
8343
|
+
const packages = match[1].split("\n").map((l) => l.replace(/^\s+-\s+/, "").replace(/["']/g, "").trim()).filter(Boolean);
|
|
8344
|
+
return { type: "pnpm", packages, root: cwd };
|
|
8345
|
+
}
|
|
8346
|
+
}
|
|
8347
|
+
const lerna = (0, import_path6.join)(cwd, "lerna.json");
|
|
8348
|
+
if ((0, import_fs6.existsSync)(lerna)) {
|
|
8349
|
+
try {
|
|
8350
|
+
const config2 = JSON.parse((0, import_fs6.readFileSync)(lerna, "utf-8"));
|
|
8351
|
+
return {
|
|
8352
|
+
type: "lerna",
|
|
8353
|
+
packages: config2.packages ?? ["packages/*"],
|
|
8354
|
+
root: cwd
|
|
8355
|
+
};
|
|
8356
|
+
} catch {
|
|
8357
|
+
}
|
|
8358
|
+
}
|
|
8359
|
+
if ((0, import_fs6.existsSync)((0, import_path6.join)(cwd, "nx.json"))) {
|
|
8360
|
+
return {
|
|
8361
|
+
type: "nx",
|
|
8362
|
+
packages: ["packages/*", "apps/*", "libs/*"],
|
|
8363
|
+
root: cwd
|
|
8364
|
+
};
|
|
8365
|
+
}
|
|
8366
|
+
if ((0, import_fs6.existsSync)((0, import_path6.join)(cwd, "turbo.json"))) {
|
|
8367
|
+
const pkgPath2 = (0, import_path6.join)(cwd, "package.json");
|
|
8368
|
+
if ((0, import_fs6.existsSync)(pkgPath2)) {
|
|
8369
|
+
try {
|
|
8370
|
+
const config2 = JSON.parse((0, import_fs6.readFileSync)(pkgPath2, "utf-8"));
|
|
8371
|
+
if (config2.workspaces) {
|
|
8372
|
+
const ws = Array.isArray(config2.workspaces) ? config2.workspaces : config2.workspaces.packages ?? [];
|
|
8373
|
+
return { type: "turbo", packages: ws, root: cwd };
|
|
8374
|
+
}
|
|
8375
|
+
} catch {
|
|
8376
|
+
}
|
|
8377
|
+
}
|
|
8378
|
+
}
|
|
8379
|
+
const pkgPath = (0, import_path6.join)(cwd, "package.json");
|
|
8380
|
+
if ((0, import_fs6.existsSync)(pkgPath)) {
|
|
8381
|
+
try {
|
|
8382
|
+
const config2 = JSON.parse((0, import_fs6.readFileSync)(pkgPath, "utf-8"));
|
|
8383
|
+
if (config2.workspaces) {
|
|
8384
|
+
const ws = Array.isArray(config2.workspaces) ? config2.workspaces : config2.workspaces.packages ?? [];
|
|
8385
|
+
return { type: "npm", packages: ws, root: cwd };
|
|
8386
|
+
}
|
|
8387
|
+
} catch {
|
|
8388
|
+
}
|
|
8389
|
+
}
|
|
8390
|
+
return null;
|
|
8391
|
+
}
|
|
8392
|
+
function matchGlobPattern(rel, pattern) {
|
|
8393
|
+
const dir = pattern.replace(/\/?\*\*?$/, "").replace(/\/$/, "");
|
|
8394
|
+
if (!dir || dir === "*" || dir === "**") {
|
|
8395
|
+
const pkg = rel.split("/")[0];
|
|
8396
|
+
return pkg || null;
|
|
8397
|
+
}
|
|
8398
|
+
const starIdx = dir.indexOf("*");
|
|
8399
|
+
if (starIdx !== -1) {
|
|
8400
|
+
const prefix2 = dir.slice(0, starIdx);
|
|
8401
|
+
if (rel.startsWith(prefix2)) {
|
|
8402
|
+
const rest = rel.slice(prefix2.length);
|
|
8403
|
+
const pkg = rest.split("/")[0];
|
|
8404
|
+
return pkg || null;
|
|
8405
|
+
}
|
|
8406
|
+
return null;
|
|
8407
|
+
}
|
|
8408
|
+
const prefix = dir + "/";
|
|
8409
|
+
const hasGlob = /\*/.test(pattern);
|
|
8410
|
+
if (hasGlob) {
|
|
8411
|
+
if (rel.startsWith(prefix)) {
|
|
8412
|
+
const rest = rel.slice(prefix.length);
|
|
8413
|
+
const pkg = rest.split("/")[0];
|
|
8414
|
+
return pkg || null;
|
|
8415
|
+
}
|
|
8416
|
+
} else {
|
|
8417
|
+
if (rel === dir || rel.startsWith(prefix)) {
|
|
8418
|
+
const segments = dir.split("/").filter(Boolean);
|
|
8419
|
+
return segments[segments.length - 1] ?? null;
|
|
8420
|
+
}
|
|
8421
|
+
}
|
|
8422
|
+
return null;
|
|
8423
|
+
}
|
|
8424
|
+
function getPackageForFile(filePath, workspace) {
|
|
8425
|
+
const absPath = filePath.startsWith("/") ? filePath : (0, import_path6.join)(workspace.root, filePath);
|
|
8426
|
+
const rel = (0, import_path6.relative)(workspace.root, absPath);
|
|
8427
|
+
for (const pattern of workspace.packages) {
|
|
8428
|
+
const packageName = matchGlobPattern(rel, pattern);
|
|
8429
|
+
if (packageName) return packageName;
|
|
8430
|
+
}
|
|
8431
|
+
return null;
|
|
8432
|
+
}
|
|
8433
|
+
function autoDetectScope(stagedFiles, workspace) {
|
|
8434
|
+
const packages = /* @__PURE__ */ new Set();
|
|
8435
|
+
for (const file of stagedFiles) {
|
|
8436
|
+
const filePath = file.startsWith("/") ? file : (0, import_path6.join)(workspace.root, file);
|
|
8437
|
+
const pkg = getPackageForFile(filePath, workspace);
|
|
8438
|
+
if (pkg) packages.add(pkg);
|
|
8439
|
+
}
|
|
8440
|
+
if (packages.size === 1) return [...packages][0];
|
|
8441
|
+
if (packages.size > 1 && packages.size <= 3) return [...packages].join(",");
|
|
8442
|
+
if (packages.size > 3) {
|
|
8443
|
+
console.error(
|
|
8444
|
+
`[qc] Changes span ${packages.size} packages; skipping auto-scope detection.`
|
|
8445
|
+
);
|
|
8446
|
+
}
|
|
8447
|
+
return null;
|
|
8448
|
+
}
|
|
8449
|
+
var import_child_process5, import_fs6, import_path6;
|
|
8450
|
+
var init_monorepo = __esm({
|
|
8451
|
+
"src/monorepo.ts"() {
|
|
8452
|
+
"use strict";
|
|
8453
|
+
import_child_process5 = require("child_process");
|
|
8454
|
+
import_fs6 = require("fs");
|
|
8455
|
+
import_path6 = require("path");
|
|
8456
|
+
}
|
|
8457
|
+
});
|
|
8458
|
+
|
|
8394
8459
|
// src/commands/changeset.ts
|
|
8395
8460
|
var changeset_exports = {};
|
|
8396
8461
|
__export(changeset_exports, {
|
|
@@ -9010,23 +9075,411 @@ async function upgrade() {
|
|
|
9010
9075
|
Opening ${BILLING_URL}
|
|
9011
9076
|
`);
|
|
9012
9077
|
try {
|
|
9013
|
-
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
9014
|
-
if (process.platform === "darwin") {
|
|
9015
|
-
execFileSync7("open", [BILLING_URL]);
|
|
9016
|
-
} else if (process.platform === "linux") {
|
|
9017
|
-
execFileSync7("xdg-open", [BILLING_URL]);
|
|
9018
|
-
} else if (process.platform === "win32") {
|
|
9019
|
-
execFileSync7("cmd", ["/c", "start", "", BILLING_URL]);
|
|
9078
|
+
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
9079
|
+
if (process.platform === "darwin") {
|
|
9080
|
+
execFileSync7("open", [BILLING_URL]);
|
|
9081
|
+
} else if (process.platform === "linux") {
|
|
9082
|
+
execFileSync7("xdg-open", [BILLING_URL]);
|
|
9083
|
+
} else if (process.platform === "win32") {
|
|
9084
|
+
execFileSync7("cmd", ["/c", "start", "", BILLING_URL]);
|
|
9085
|
+
}
|
|
9086
|
+
} catch {
|
|
9087
|
+
console.log(`Visit: ${BILLING_URL}`);
|
|
9088
|
+
}
|
|
9089
|
+
}
|
|
9090
|
+
var BILLING_URL;
|
|
9091
|
+
var init_upgrade = __esm({
|
|
9092
|
+
"src/commands/upgrade.ts"() {
|
|
9093
|
+
"use strict";
|
|
9094
|
+
BILLING_URL = "https://app.quikcommit.dev/billing";
|
|
9095
|
+
}
|
|
9096
|
+
});
|
|
9097
|
+
|
|
9098
|
+
// src/smart-diff.ts
|
|
9099
|
+
function sanitizeFilepath(path) {
|
|
9100
|
+
return path.replace(/[\x00-\x1F\x7F[\]`]/g, "_").slice(0, 200);
|
|
9101
|
+
}
|
|
9102
|
+
function classifyFile(filepath) {
|
|
9103
|
+
const basename = filepath.split("/").pop() ?? filepath;
|
|
9104
|
+
if (LOCK_FILES.has(basename)) return "lock";
|
|
9105
|
+
if (filepath.endsWith(".map")) return "sourcemap";
|
|
9106
|
+
if (VENDORED_PREFIXES.some((p) => filepath.startsWith(p))) return "vendored";
|
|
9107
|
+
if (GENERATED_PATTERNS.some((p) => p.test(filepath))) return "generated";
|
|
9108
|
+
return "code";
|
|
9109
|
+
}
|
|
9110
|
+
function parseDiffIntoFiles(diff) {
|
|
9111
|
+
const files = [];
|
|
9112
|
+
const parts = diff.split(/^(diff --git .+)$/m);
|
|
9113
|
+
for (let i = 1; i < parts.length; i += 2) {
|
|
9114
|
+
const header = parts[i];
|
|
9115
|
+
const content = parts[i + 1] ?? "";
|
|
9116
|
+
const match = header.match(/diff --git a\/(.+?) b\/(.+)/);
|
|
9117
|
+
const filepath = match?.[2] ?? "unknown";
|
|
9118
|
+
const lines = content.split("\n");
|
|
9119
|
+
let additions = 0;
|
|
9120
|
+
let deletions = 0;
|
|
9121
|
+
for (const line of lines) {
|
|
9122
|
+
if (line.startsWith("+") && !line.startsWith("+++")) additions++;
|
|
9123
|
+
else if (line.startsWith("-") && !line.startsWith("---")) deletions++;
|
|
9124
|
+
}
|
|
9125
|
+
files.push({ filepath, content: header + content, additions, deletions });
|
|
9126
|
+
}
|
|
9127
|
+
return files;
|
|
9128
|
+
}
|
|
9129
|
+
function isMinified(content) {
|
|
9130
|
+
const lines = content.split("\n").filter(
|
|
9131
|
+
(l) => (l.startsWith("+") || l.startsWith("-")) && !l.startsWith("+++") && !l.startsWith("---")
|
|
9132
|
+
);
|
|
9133
|
+
if (lines.length === 0) return false;
|
|
9134
|
+
return lines.some((l) => l.length > 500);
|
|
9135
|
+
}
|
|
9136
|
+
function preprocessDiff(diff) {
|
|
9137
|
+
const files = parseDiffIntoFiles(diff);
|
|
9138
|
+
if (files.length === 0) return { processedDiff: diff, summarized: [], tokensSaved: 0 };
|
|
9139
|
+
const kept = [];
|
|
9140
|
+
const summarized = [];
|
|
9141
|
+
let tokensSaved = 0;
|
|
9142
|
+
for (const file of files) {
|
|
9143
|
+
const classification = classifyFile(file.filepath);
|
|
9144
|
+
switch (classification) {
|
|
9145
|
+
case "sourcemap":
|
|
9146
|
+
tokensSaved += estimateTokens(file.content);
|
|
9147
|
+
summarized.push(file.filepath);
|
|
9148
|
+
break;
|
|
9149
|
+
case "lock":
|
|
9150
|
+
tokensSaved += estimateTokens(file.content);
|
|
9151
|
+
kept.push(`[lock file updated: ${sanitizeFilepath(file.filepath)} (+${file.additions} \u2212${file.deletions} lines)]
|
|
9152
|
+
`);
|
|
9153
|
+
summarized.push(file.filepath);
|
|
9154
|
+
break;
|
|
9155
|
+
case "generated":
|
|
9156
|
+
tokensSaved += estimateTokens(file.content);
|
|
9157
|
+
kept.push(`[generated: ${sanitizeFilepath(file.filepath)} (+${file.additions} \u2212${file.deletions})]
|
|
9158
|
+
`);
|
|
9159
|
+
summarized.push(file.filepath);
|
|
9160
|
+
break;
|
|
9161
|
+
case "vendored":
|
|
9162
|
+
tokensSaved += estimateTokens(file.content);
|
|
9163
|
+
kept.push(`[vendored: ${sanitizeFilepath(file.filepath)} updated]
|
|
9164
|
+
`);
|
|
9165
|
+
summarized.push(file.filepath);
|
|
9166
|
+
break;
|
|
9167
|
+
case "code":
|
|
9168
|
+
if (isMinified(file.content)) {
|
|
9169
|
+
tokensSaved += estimateTokens(file.content);
|
|
9170
|
+
const sizeKB = Math.round(file.content.length / 1024);
|
|
9171
|
+
kept.push(`[minified asset: ${sanitizeFilepath(file.filepath)} (${sizeKB} KB)]
|
|
9172
|
+
`);
|
|
9173
|
+
summarized.push(file.filepath);
|
|
9174
|
+
} else {
|
|
9175
|
+
kept.push(file.content);
|
|
9176
|
+
}
|
|
9177
|
+
break;
|
|
9178
|
+
}
|
|
9179
|
+
}
|
|
9180
|
+
return {
|
|
9181
|
+
processedDiff: kept.join(""),
|
|
9182
|
+
summarized,
|
|
9183
|
+
tokensSaved
|
|
9184
|
+
};
|
|
9185
|
+
}
|
|
9186
|
+
var LOCK_FILES, GENERATED_PATTERNS, VENDORED_PREFIXES;
|
|
9187
|
+
var init_smart_diff = __esm({
|
|
9188
|
+
"src/smart-diff.ts"() {
|
|
9189
|
+
"use strict";
|
|
9190
|
+
init_dist();
|
|
9191
|
+
LOCK_FILES = /* @__PURE__ */ new Set([
|
|
9192
|
+
"pnpm-lock.yaml",
|
|
9193
|
+
"package-lock.json",
|
|
9194
|
+
"yarn.lock",
|
|
9195
|
+
"Cargo.lock",
|
|
9196
|
+
"Gemfile.lock",
|
|
9197
|
+
"poetry.lock",
|
|
9198
|
+
"composer.lock",
|
|
9199
|
+
"bun.lockb",
|
|
9200
|
+
"shrinkwrap.json"
|
|
9201
|
+
]);
|
|
9202
|
+
GENERATED_PATTERNS = [
|
|
9203
|
+
/\.generated\.\w+$/,
|
|
9204
|
+
/\.g\.dart$/,
|
|
9205
|
+
/\.pb\.go$/,
|
|
9206
|
+
/\.pb\.ts$/,
|
|
9207
|
+
/(^|\/)\.prisma\/client\//,
|
|
9208
|
+
/\/generated\//
|
|
9209
|
+
];
|
|
9210
|
+
VENDORED_PREFIXES = ["vendor/", "third_party/", "node_modules/"];
|
|
9211
|
+
}
|
|
9212
|
+
});
|
|
9213
|
+
|
|
9214
|
+
// ../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js
|
|
9215
|
+
var require_picocolors = __commonJS({
|
|
9216
|
+
"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports2, module2) {
|
|
9217
|
+
var p = process || {};
|
|
9218
|
+
var argv = p.argv || [];
|
|
9219
|
+
var env = p.env || {};
|
|
9220
|
+
var isColorSupported = !(!!env.NO_COLOR || argv.includes("--no-color")) && (!!env.FORCE_COLOR || argv.includes("--color") || p.platform === "win32" || (p.stdout || {}).isTTY && env.TERM !== "dumb" || !!env.CI);
|
|
9221
|
+
var formatter = (open, close, replace = open) => (input) => {
|
|
9222
|
+
let string = "" + input, index = string.indexOf(close, open.length);
|
|
9223
|
+
return ~index ? open + replaceClose(string, close, replace, index) + close : open + string + close;
|
|
9224
|
+
};
|
|
9225
|
+
var replaceClose = (string, close, replace, index) => {
|
|
9226
|
+
let result = "", cursor = 0;
|
|
9227
|
+
do {
|
|
9228
|
+
result += string.substring(cursor, index) + replace;
|
|
9229
|
+
cursor = index + close.length;
|
|
9230
|
+
index = string.indexOf(close, cursor);
|
|
9231
|
+
} while (~index);
|
|
9232
|
+
return result + string.substring(cursor);
|
|
9233
|
+
};
|
|
9234
|
+
var createColors = (enabled = isColorSupported) => {
|
|
9235
|
+
let f = enabled ? formatter : () => String;
|
|
9236
|
+
return {
|
|
9237
|
+
isColorSupported: enabled,
|
|
9238
|
+
reset: f("\x1B[0m", "\x1B[0m"),
|
|
9239
|
+
bold: f("\x1B[1m", "\x1B[22m", "\x1B[22m\x1B[1m"),
|
|
9240
|
+
dim: f("\x1B[2m", "\x1B[22m", "\x1B[22m\x1B[2m"),
|
|
9241
|
+
italic: f("\x1B[3m", "\x1B[23m"),
|
|
9242
|
+
underline: f("\x1B[4m", "\x1B[24m"),
|
|
9243
|
+
inverse: f("\x1B[7m", "\x1B[27m"),
|
|
9244
|
+
hidden: f("\x1B[8m", "\x1B[28m"),
|
|
9245
|
+
strikethrough: f("\x1B[9m", "\x1B[29m"),
|
|
9246
|
+
black: f("\x1B[30m", "\x1B[39m"),
|
|
9247
|
+
red: f("\x1B[31m", "\x1B[39m"),
|
|
9248
|
+
green: f("\x1B[32m", "\x1B[39m"),
|
|
9249
|
+
yellow: f("\x1B[33m", "\x1B[39m"),
|
|
9250
|
+
blue: f("\x1B[34m", "\x1B[39m"),
|
|
9251
|
+
magenta: f("\x1B[35m", "\x1B[39m"),
|
|
9252
|
+
cyan: f("\x1B[36m", "\x1B[39m"),
|
|
9253
|
+
white: f("\x1B[37m", "\x1B[39m"),
|
|
9254
|
+
gray: f("\x1B[90m", "\x1B[39m"),
|
|
9255
|
+
bgBlack: f("\x1B[40m", "\x1B[49m"),
|
|
9256
|
+
bgRed: f("\x1B[41m", "\x1B[49m"),
|
|
9257
|
+
bgGreen: f("\x1B[42m", "\x1B[49m"),
|
|
9258
|
+
bgYellow: f("\x1B[43m", "\x1B[49m"),
|
|
9259
|
+
bgBlue: f("\x1B[44m", "\x1B[49m"),
|
|
9260
|
+
bgMagenta: f("\x1B[45m", "\x1B[49m"),
|
|
9261
|
+
bgCyan: f("\x1B[46m", "\x1B[49m"),
|
|
9262
|
+
bgWhite: f("\x1B[47m", "\x1B[49m"),
|
|
9263
|
+
blackBright: f("\x1B[90m", "\x1B[39m"),
|
|
9264
|
+
redBright: f("\x1B[91m", "\x1B[39m"),
|
|
9265
|
+
greenBright: f("\x1B[92m", "\x1B[39m"),
|
|
9266
|
+
yellowBright: f("\x1B[93m", "\x1B[39m"),
|
|
9267
|
+
blueBright: f("\x1B[94m", "\x1B[39m"),
|
|
9268
|
+
magentaBright: f("\x1B[95m", "\x1B[39m"),
|
|
9269
|
+
cyanBright: f("\x1B[96m", "\x1B[39m"),
|
|
9270
|
+
whiteBright: f("\x1B[97m", "\x1B[39m"),
|
|
9271
|
+
bgBlackBright: f("\x1B[100m", "\x1B[49m"),
|
|
9272
|
+
bgRedBright: f("\x1B[101m", "\x1B[49m"),
|
|
9273
|
+
bgGreenBright: f("\x1B[102m", "\x1B[49m"),
|
|
9274
|
+
bgYellowBright: f("\x1B[103m", "\x1B[49m"),
|
|
9275
|
+
bgBlueBright: f("\x1B[104m", "\x1B[49m"),
|
|
9276
|
+
bgMagentaBright: f("\x1B[105m", "\x1B[49m"),
|
|
9277
|
+
bgCyanBright: f("\x1B[106m", "\x1B[49m"),
|
|
9278
|
+
bgWhiteBright: f("\x1B[107m", "\x1B[49m")
|
|
9279
|
+
};
|
|
9280
|
+
};
|
|
9281
|
+
module2.exports = createColors();
|
|
9282
|
+
module2.exports.createColors = createColors;
|
|
9283
|
+
}
|
|
9284
|
+
});
|
|
9285
|
+
|
|
9286
|
+
// src/ui.ts
|
|
9287
|
+
function hasCliNoColor() {
|
|
9288
|
+
try {
|
|
9289
|
+
return process.argv.slice(2).includes("--no-color");
|
|
9290
|
+
} catch {
|
|
9291
|
+
return false;
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
function createUI(options) {
|
|
9295
|
+
const isColor = options.isTTY && !options.noColor;
|
|
9296
|
+
const wrap = (fn) => (s) => isColor ? fn(s) : s;
|
|
9297
|
+
const format = {
|
|
9298
|
+
step: (msg) => `${isColor ? import_picocolors.default.dim("\u203A") : "\u203A"} ${isColor ? import_picocolors.default.dim(msg) : msg}`,
|
|
9299
|
+
success: (msg) => `${isColor ? import_picocolors.default.green("\u2713") : "\u2713"} ${msg}`,
|
|
9300
|
+
error: (msg) => `${isColor ? import_picocolors.default.red("\u2717") : "\u2717"} ${msg}`,
|
|
9301
|
+
dim: wrap(import_picocolors.default.dim),
|
|
9302
|
+
bold: wrap(import_picocolors.default.bold),
|
|
9303
|
+
commitType: wrap(import_picocolors.default.cyan),
|
|
9304
|
+
commitScope: wrap(import_picocolors.default.yellow)
|
|
9305
|
+
};
|
|
9306
|
+
function createSpinner(message, write = (s) => process.stderr.write(s)) {
|
|
9307
|
+
let frame = 0;
|
|
9308
|
+
let interval = null;
|
|
9309
|
+
return {
|
|
9310
|
+
start() {
|
|
9311
|
+
if (interval) return;
|
|
9312
|
+
if (!options.isTTY) return;
|
|
9313
|
+
interval = setInterval(() => {
|
|
9314
|
+
const f = SPINNER_FRAMES2[frame++ % SPINNER_FRAMES2.length];
|
|
9315
|
+
write(`\r${format.step(message)} ${isColor ? import_picocolors.default.cyan(f) : f}`);
|
|
9316
|
+
}, 80);
|
|
9317
|
+
},
|
|
9318
|
+
stop(finalMessage) {
|
|
9319
|
+
if (interval) {
|
|
9320
|
+
clearInterval(interval);
|
|
9321
|
+
interval = null;
|
|
9322
|
+
}
|
|
9323
|
+
if (options.isTTY) {
|
|
9324
|
+
write("\r\x1B[2K");
|
|
9325
|
+
}
|
|
9326
|
+
if (finalMessage) {
|
|
9327
|
+
write(finalMessage + "\n");
|
|
9328
|
+
}
|
|
9329
|
+
}
|
|
9330
|
+
};
|
|
9331
|
+
}
|
|
9332
|
+
const log = {
|
|
9333
|
+
step: (msg) => process.stderr.write(format.step(msg) + "\n"),
|
|
9334
|
+
success: (msg) => process.stderr.write(format.success(msg) + "\n"),
|
|
9335
|
+
error: (msg) => process.stderr.write(format.error(msg) + "\n"),
|
|
9336
|
+
dim: (msg) => process.stderr.write(format.dim(msg) + "\n")
|
|
9337
|
+
};
|
|
9338
|
+
return { isColor, format, spinner: createSpinner, log };
|
|
9339
|
+
}
|
|
9340
|
+
function getUI() {
|
|
9341
|
+
if (!_defaultUI) {
|
|
9342
|
+
_defaultUI = createUI({
|
|
9343
|
+
isTTY: !!process.stderr.isTTY,
|
|
9344
|
+
noColor: !!process.env.NO_COLOR || hasCliNoColor()
|
|
9345
|
+
});
|
|
9346
|
+
}
|
|
9347
|
+
return _defaultUI;
|
|
9348
|
+
}
|
|
9349
|
+
var import_picocolors, SPINNER_FRAMES2, _defaultUI, ui;
|
|
9350
|
+
var init_ui = __esm({
|
|
9351
|
+
"src/ui.ts"() {
|
|
9352
|
+
"use strict";
|
|
9353
|
+
import_picocolors = __toESM(require_picocolors());
|
|
9354
|
+
SPINNER_FRAMES2 = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
9355
|
+
ui = new Proxy({}, {
|
|
9356
|
+
get(_target, prop) {
|
|
9357
|
+
return getUI()[prop];
|
|
9358
|
+
}
|
|
9359
|
+
});
|
|
9360
|
+
}
|
|
9361
|
+
});
|
|
9362
|
+
|
|
9363
|
+
// src/commit-helpers.ts
|
|
9364
|
+
function applyCliTypeScopeToRules(rules, type, scope) {
|
|
9365
|
+
let next = { ...rules };
|
|
9366
|
+
if (type) {
|
|
9367
|
+
next = { ...next, types: [type] };
|
|
9368
|
+
}
|
|
9369
|
+
if (scope) {
|
|
9370
|
+
next = { ...next, scopes: [scope] };
|
|
9371
|
+
}
|
|
9372
|
+
return next;
|
|
9373
|
+
}
|
|
9374
|
+
function generationHintsFromArgs(split, forceBody) {
|
|
9375
|
+
const h = {};
|
|
9376
|
+
if (split) h.split = true;
|
|
9377
|
+
if (forceBody) h.force_body = true;
|
|
9378
|
+
return Object.keys(h).length > 0 ? h : void 0;
|
|
9379
|
+
}
|
|
9380
|
+
function splitCommitMessageForDisplay(message) {
|
|
9381
|
+
const t = message.replace(/\r\n/g, "\n").trimEnd();
|
|
9382
|
+
const doubleNl = t.indexOf("\n\n");
|
|
9383
|
+
if (doubleNl !== -1) {
|
|
9384
|
+
const head = t.slice(0, doubleNl);
|
|
9385
|
+
const subject = head.split("\n")[0]?.trim() ?? "";
|
|
9386
|
+
return { subject, body: t.slice(doubleNl + 2).trimEnd() };
|
|
9387
|
+
}
|
|
9388
|
+
const firstNl = t.indexOf("\n");
|
|
9389
|
+
if (firstNl === -1) {
|
|
9390
|
+
return { subject: t.trim(), body: "" };
|
|
9391
|
+
}
|
|
9392
|
+
return {
|
|
9393
|
+
subject: t.slice(0, firstNl).trim(),
|
|
9394
|
+
body: t.slice(firstNl + 1).trimEnd()
|
|
9395
|
+
};
|
|
9396
|
+
}
|
|
9397
|
+
function formatVerboseCommitDiagnostics(diagnostics, roundTripMs) {
|
|
9398
|
+
const lines = [`api_round_trip_ms: ${roundTripMs}`];
|
|
9399
|
+
if (diagnostics !== void 0) {
|
|
9400
|
+
lines.push(JSON.stringify(diagnostics, null, 2));
|
|
9401
|
+
}
|
|
9402
|
+
return lines.join("\n");
|
|
9403
|
+
}
|
|
9404
|
+
async function interactiveRefineMessage(initial, opts) {
|
|
9405
|
+
if (opts.skip) return { action: "accept", message: initial };
|
|
9406
|
+
const rl = import_promises.default.createInterface({ input: process.stdin, output: process.stderr });
|
|
9407
|
+
try {
|
|
9408
|
+
process.stderr.write(`
|
|
9409
|
+
${initial}
|
|
9410
|
+
|
|
9411
|
+
`);
|
|
9412
|
+
const choice = (await rl.question("Keep? [Y/n/e]: ")).trim().toLowerCase();
|
|
9413
|
+
if (choice === "n") {
|
|
9414
|
+
return { action: "abort" };
|
|
9415
|
+
}
|
|
9416
|
+
if (choice === "e") {
|
|
9417
|
+
process.stderr.write("Enter new message (end with a line containing only .):\n");
|
|
9418
|
+
const lines = [];
|
|
9419
|
+
while (true) {
|
|
9420
|
+
const line = await rl.question("");
|
|
9421
|
+
if (line === ".") break;
|
|
9422
|
+
lines.push(line);
|
|
9423
|
+
}
|
|
9424
|
+
const edited = lines.join("\n").trim();
|
|
9425
|
+
return { action: "edit", message: edited.length > 0 ? edited : initial };
|
|
9426
|
+
}
|
|
9427
|
+
return { action: "accept", message: initial };
|
|
9428
|
+
} finally {
|
|
9429
|
+
rl.close();
|
|
9430
|
+
}
|
|
9431
|
+
}
|
|
9432
|
+
async function confirmCommit(prompt2, opts) {
|
|
9433
|
+
if (opts.skip) return { action: "commit" };
|
|
9434
|
+
const rl = import_promises.default.createInterface({ input: process.stdin, output: process.stderr });
|
|
9435
|
+
try {
|
|
9436
|
+
const ans = (await rl.question(prompt2)).trim().toLowerCase();
|
|
9437
|
+
if (ans !== "y" && ans !== "yes") {
|
|
9438
|
+
return { action: "abort" };
|
|
9020
9439
|
}
|
|
9021
|
-
|
|
9022
|
-
|
|
9440
|
+
return { action: "commit" };
|
|
9441
|
+
} finally {
|
|
9442
|
+
rl.close();
|
|
9023
9443
|
}
|
|
9024
9444
|
}
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9445
|
+
function shouldSkipTTYInteraction(hookMode) {
|
|
9446
|
+
return hookMode === true || process.stdin.isTTY !== true;
|
|
9447
|
+
}
|
|
9448
|
+
function logVerboseDiagnostics(dim, verbose, quiet, diagnostics, roundTripMs) {
|
|
9449
|
+
if (!verbose || quiet) return;
|
|
9450
|
+
process.stderr.write(
|
|
9451
|
+
`
|
|
9452
|
+
${formatVerboseCommitDiagnostics(diagnostics, roundTripMs)}
|
|
9453
|
+
`
|
|
9454
|
+
);
|
|
9455
|
+
dim("(verbose diagnostics on stderr)");
|
|
9456
|
+
}
|
|
9457
|
+
function createSilentLog() {
|
|
9458
|
+
return {
|
|
9459
|
+
step: () => {
|
|
9460
|
+
},
|
|
9461
|
+
success: () => {
|
|
9462
|
+
},
|
|
9463
|
+
error: (msg) => console.error(msg),
|
|
9464
|
+
dim: () => {
|
|
9465
|
+
}
|
|
9466
|
+
};
|
|
9467
|
+
}
|
|
9468
|
+
function displayCommitMessage(message, log) {
|
|
9469
|
+
const { subject, body } = splitCommitMessageForDisplay(message);
|
|
9470
|
+
log.success(subject);
|
|
9471
|
+
if (body) {
|
|
9472
|
+
for (const line of body.split("\n")) {
|
|
9473
|
+
log.dim(` ${line}`);
|
|
9474
|
+
}
|
|
9475
|
+
process.stderr.write("\n");
|
|
9476
|
+
}
|
|
9477
|
+
}
|
|
9478
|
+
var import_promises;
|
|
9479
|
+
var init_commit_helpers = __esm({
|
|
9480
|
+
"src/commit-helpers.ts"() {
|
|
9028
9481
|
"use strict";
|
|
9029
|
-
|
|
9482
|
+
import_promises = __toESM(require("node:readline/promises"));
|
|
9030
9483
|
}
|
|
9031
9484
|
});
|
|
9032
9485
|
|
|
@@ -9081,7 +9534,7 @@ function getLocalProviderConfig() {
|
|
|
9081
9534
|
if (provider === "openrouter" && !apiKey) return null;
|
|
9082
9535
|
return { provider, baseUrl, model, apiKey };
|
|
9083
9536
|
}
|
|
9084
|
-
function buildUserPrompt(changes, diff, rules) {
|
|
9537
|
+
function buildUserPrompt(changes, diff, rules, recentCommits, hints) {
|
|
9085
9538
|
let prompt2 = `Generate a commit message for these changes:
|
|
9086
9539
|
|
|
9087
9540
|
## File changes:
|
|
@@ -9095,6 +9548,23 @@ ${diff}
|
|
|
9095
9548
|
</diff>
|
|
9096
9549
|
|
|
9097
9550
|
`;
|
|
9551
|
+
if (recentCommits && recentCommits.length > 0) {
|
|
9552
|
+
const history = recentCommits.slice(0, 10).join("\n");
|
|
9553
|
+
prompt2 += `Recent commits on this branch (match style when appropriate):
|
|
9554
|
+
${history}
|
|
9555
|
+
|
|
9556
|
+
`;
|
|
9557
|
+
}
|
|
9558
|
+
if (hints?.split) {
|
|
9559
|
+
prompt2 += `MULTI-COMMIT MODE: If changes span multiple logical commits, focus the message on the primary change and mention other slices in the body.
|
|
9560
|
+
|
|
9561
|
+
`;
|
|
9562
|
+
}
|
|
9563
|
+
if (hints?.force_body) {
|
|
9564
|
+
prompt2 += `The user requires a BODY section after the subject line, even for small changes.
|
|
9565
|
+
|
|
9566
|
+
`;
|
|
9567
|
+
}
|
|
9098
9568
|
if (rules && Object.keys(rules).length > 0) {
|
|
9099
9569
|
prompt2 += `Rules: ${JSON.stringify(rules)}
|
|
9100
9570
|
|
|
@@ -9105,7 +9575,7 @@ ${diff}
|
|
|
9105
9575
|
- Response should be the commit message only, no explanations`;
|
|
9106
9576
|
return prompt2;
|
|
9107
9577
|
}
|
|
9108
|
-
function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiKey, rules) {
|
|
9578
|
+
function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiKey, rules, recentCommits, hints) {
|
|
9109
9579
|
const headers = {
|
|
9110
9580
|
"Content-Type": "application/json"
|
|
9111
9581
|
};
|
|
@@ -9157,10 +9627,18 @@ function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiK
|
|
|
9157
9627
|
]
|
|
9158
9628
|
};
|
|
9159
9629
|
return { url, body, headers };
|
|
9160
|
-
case "cloudflare":
|
|
9630
|
+
case "cloudflare": {
|
|
9161
9631
|
url = `${baseUrl.replace(/\/$/, "")}/commit`;
|
|
9162
|
-
|
|
9632
|
+
const payload = { diff, changes, rules };
|
|
9633
|
+
if (recentCommits && recentCommits.length > 0) {
|
|
9634
|
+
payload.recent_commits = recentCommits.slice(0, 10);
|
|
9635
|
+
}
|
|
9636
|
+
if (hints && Object.keys(hints).length > 0) {
|
|
9637
|
+
payload.generation_hints = hints;
|
|
9638
|
+
}
|
|
9639
|
+
body = payload;
|
|
9163
9640
|
return { url, body, headers: { "Content-Type": "application/json" } };
|
|
9641
|
+
}
|
|
9164
9642
|
default:
|
|
9165
9643
|
throw new Error(`Unknown provider: ${provider}`);
|
|
9166
9644
|
}
|
|
@@ -9182,7 +9660,9 @@ function parseResponse(provider, data) {
|
|
|
9182
9660
|
return "";
|
|
9183
9661
|
}
|
|
9184
9662
|
}
|
|
9185
|
-
async function runLocalCommit(
|
|
9663
|
+
async function runLocalCommit(args) {
|
|
9664
|
+
const silent = !!(args.hookMode || args.quiet);
|
|
9665
|
+
const log = silent ? createSilentLog() : getUI().log;
|
|
9186
9666
|
if (!isGitRepo()) {
|
|
9187
9667
|
throw new Error("Not a git repository.");
|
|
9188
9668
|
}
|
|
@@ -9196,11 +9676,19 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9196
9676
|
);
|
|
9197
9677
|
}
|
|
9198
9678
|
const config2 = getConfig();
|
|
9199
|
-
const excludes = config2.excludes ?? [];
|
|
9200
|
-
|
|
9679
|
+
const excludes = [...config2.excludes ?? [], ...args.exclude];
|
|
9680
|
+
let diff = getStagedDiff(excludes);
|
|
9201
9681
|
const changes = getStagedFiles();
|
|
9202
|
-
|
|
9203
|
-
|
|
9682
|
+
if (!args.noSmartDiff) {
|
|
9683
|
+
const smartResult = preprocessDiff(diff);
|
|
9684
|
+
diff = smartResult.processedDiff;
|
|
9685
|
+
if (smartResult.summarized.length > 0 && !silent) {
|
|
9686
|
+
log.step(
|
|
9687
|
+
`smart-diff: ${smartResult.summarized.length} file(s) summarized (saved ~${Math.round(smartResult.tokensSaved / 1e3)}K tokens)`
|
|
9688
|
+
);
|
|
9689
|
+
}
|
|
9690
|
+
}
|
|
9691
|
+
let rules = { ...await detectCommitlintRules(), ...config2.rules ?? {} };
|
|
9204
9692
|
const workspace = detectWorkspace();
|
|
9205
9693
|
if (workspace) {
|
|
9206
9694
|
const stagedFiles = changes.trim().split("\n").filter(Boolean);
|
|
@@ -9210,7 +9698,20 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9210
9698
|
rules = { ...rules, scopes };
|
|
9211
9699
|
}
|
|
9212
9700
|
}
|
|
9213
|
-
|
|
9701
|
+
rules = applyCliTypeScopeToRules(rules, args.type, args.scope);
|
|
9702
|
+
const recentCommits = args.noContext ? void 0 : getRecentBranchCommits(5);
|
|
9703
|
+
const generationHints = generationHintsFromArgs(args.split, args.forceBody);
|
|
9704
|
+
const skipInteractive = silent || args.quiet || shouldSkipTTYInteraction(args.hookMode);
|
|
9705
|
+
const skipConfirm = args.dryRun || args.messageOnly || silent || args.quiet || shouldSkipTTYInteraction(args.hookMode);
|
|
9706
|
+
const model = args.model ?? local.model;
|
|
9707
|
+
const modelDisplay = model ?? local.model ?? "default";
|
|
9708
|
+
const userContent = buildUserPrompt(
|
|
9709
|
+
changes,
|
|
9710
|
+
diff,
|
|
9711
|
+
Object.keys(rules).length > 0 ? rules : void 0,
|
|
9712
|
+
recentCommits,
|
|
9713
|
+
generationHints
|
|
9714
|
+
);
|
|
9214
9715
|
const { url, body, headers } = buildRequest(
|
|
9215
9716
|
local.provider,
|
|
9216
9717
|
local.baseUrl,
|
|
@@ -9219,18 +9720,29 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9219
9720
|
changes,
|
|
9220
9721
|
model,
|
|
9221
9722
|
local.apiKey,
|
|
9222
|
-
rules
|
|
9723
|
+
rules,
|
|
9724
|
+
recentCommits,
|
|
9725
|
+
generationHints
|
|
9223
9726
|
);
|
|
9224
9727
|
if (!url || url.includes("YOUR-WORKER")) {
|
|
9225
9728
|
throw new Error(
|
|
9226
9729
|
"Cloudflare provider requires api_url. Run: qc config set api_url https://your-worker.workers.dev"
|
|
9227
9730
|
);
|
|
9228
9731
|
}
|
|
9229
|
-
const
|
|
9230
|
-
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9732
|
+
const spinner = getUI().spinner(`generating commit (${modelDisplay} via ${local.provider})...`);
|
|
9733
|
+
if (!silent) spinner.start();
|
|
9734
|
+
const t0 = Date.now();
|
|
9735
|
+
let res;
|
|
9736
|
+
try {
|
|
9737
|
+
res = await fetch(url, {
|
|
9738
|
+
method: "POST",
|
|
9739
|
+
headers,
|
|
9740
|
+
body: JSON.stringify(body)
|
|
9741
|
+
});
|
|
9742
|
+
} finally {
|
|
9743
|
+
spinner.stop();
|
|
9744
|
+
}
|
|
9745
|
+
const roundTripMs = Date.now() - t0;
|
|
9234
9746
|
if (!res.ok) {
|
|
9235
9747
|
const text = await res.text();
|
|
9236
9748
|
throw new Error(`Provider error (${res.status}): ${text}`);
|
|
@@ -9241,12 +9753,37 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9241
9753
|
if (!message) {
|
|
9242
9754
|
throw new Error("Failed to generate commit message.");
|
|
9243
9755
|
}
|
|
9244
|
-
|
|
9756
|
+
const diagnostics = local.provider === "cloudflare" && typeof data === "object" && data !== null ? data.diagnostics : void 0;
|
|
9757
|
+
logVerboseDiagnostics((msg) => log.dim(msg), args.verbose, args.quiet, diagnostics, roundTripMs);
|
|
9758
|
+
if (args.interactive) {
|
|
9759
|
+
if (shouldSkipTTYInteraction(args.hookMode)) {
|
|
9760
|
+
if (!silent) log.dim("(--interactive ignored: not running in a TTY)");
|
|
9761
|
+
} else {
|
|
9762
|
+
const refineResult = await interactiveRefineMessage(message, { skip: skipInteractive });
|
|
9763
|
+
if (refineResult.action === "abort") {
|
|
9764
|
+
process.exit(0);
|
|
9765
|
+
}
|
|
9766
|
+
message = refineResult.message;
|
|
9767
|
+
}
|
|
9768
|
+
}
|
|
9769
|
+
if (args.messageOnly) {
|
|
9245
9770
|
console.log(message);
|
|
9246
9771
|
return;
|
|
9247
9772
|
}
|
|
9773
|
+
if (!silent) {
|
|
9774
|
+
displayCommitMessage(message, log);
|
|
9775
|
+
}
|
|
9776
|
+
if (args.dryRun) {
|
|
9777
|
+
return;
|
|
9778
|
+
}
|
|
9779
|
+
if (args.confirm) {
|
|
9780
|
+
const confirmResult = await confirmCommit("Proceed with commit? [y/N]: ", { skip: skipConfirm });
|
|
9781
|
+
if (confirmResult.action === "abort") {
|
|
9782
|
+
process.exit(0);
|
|
9783
|
+
}
|
|
9784
|
+
}
|
|
9248
9785
|
gitCommit(message);
|
|
9249
|
-
if (push) {
|
|
9786
|
+
if (args.push) {
|
|
9250
9787
|
gitPush();
|
|
9251
9788
|
}
|
|
9252
9789
|
}
|
|
@@ -9261,6 +9798,10 @@ var init_local = __esm({
|
|
|
9261
9798
|
init_dist();
|
|
9262
9799
|
init_git();
|
|
9263
9800
|
init_monorepo();
|
|
9801
|
+
init_commitlint();
|
|
9802
|
+
init_smart_diff();
|
|
9803
|
+
init_ui();
|
|
9804
|
+
init_commit_helpers();
|
|
9264
9805
|
CONFIG_PATH2 = (0, import_path10.join)((0, import_os4.homedir)(), CONFIG_DIR);
|
|
9265
9806
|
PROVIDER_URLS = {
|
|
9266
9807
|
ollama: "http://localhost:11434",
|
|
@@ -9279,166 +9820,55 @@ var init_local = __esm({
|
|
|
9279
9820
|
}
|
|
9280
9821
|
});
|
|
9281
9822
|
|
|
9282
|
-
// src/
|
|
9283
|
-
|
|
9284
|
-
|
|
9285
|
-
|
|
9286
|
-
|
|
9287
|
-
|
|
9288
|
-
|
|
9289
|
-
|
|
9290
|
-
|
|
9291
|
-
qc Generate commit message and commit (default)
|
|
9292
|
-
qc --message-only Generate message only, print to stdout
|
|
9293
|
-
qc --push Commit and push to origin
|
|
9294
|
-
qc pr Generate PR description from branch commits
|
|
9295
|
-
qc changelog Generate changelog from commits since last tag
|
|
9296
|
-
qc changeset Automate pnpm changeset with AI
|
|
9297
|
-
qc init Install prepare-commit-msg hook for auto-generation
|
|
9298
|
-
qc login Sign in via browser
|
|
9299
|
-
qc logout Clear local credentials
|
|
9300
|
-
qc status Show auth, plan, usage
|
|
9301
|
-
qc team Team management (info, rules, invite)
|
|
9302
|
-
|
|
9303
|
-
Options:
|
|
9304
|
-
-h, --help Show this help
|
|
9305
|
-
-a, --all Stage all tracked changes before generating
|
|
9306
|
-
-m, --message-only Generate message only
|
|
9307
|
-
-p, --push Commit and push after generating
|
|
9308
|
-
--api-key <key> Use this API key (overrides credentials file)
|
|
9309
|
-
--base <branch> Base branch for qc pr, qc changeset (default: main)
|
|
9310
|
-
--create Create PR with gh CLI after qc pr
|
|
9311
|
-
--from <ref> Start ref for qc changelog (default: latest tag)
|
|
9312
|
-
--to <ref> End ref for qc changelog (default: HEAD)
|
|
9313
|
-
--write Prepend changelog to CHANGELOG.md
|
|
9314
|
-
--version <ver> Version label for changelog header (default: derived from --to or "<from>-next")
|
|
9315
|
-
--uninstall Remove Quikcommit hook (qc init --uninstall)
|
|
9316
|
-
--model <id> Use specific model (e.g. qwen25-coder-32b, llama-3.3-70b)
|
|
9317
|
-
|
|
9318
|
-
Commands:
|
|
9319
|
-
qc config Show current config
|
|
9320
|
-
qc config set <k> <v> Set config (model, api_url)
|
|
9321
|
-
qc config reset Reset to defaults
|
|
9322
|
-
qc upgrade Open billing page in browser
|
|
9323
|
-
`;
|
|
9324
|
-
function parseArgs(args) {
|
|
9325
|
-
let command = "commit";
|
|
9326
|
-
let all = false;
|
|
9327
|
-
let messageOnly = false;
|
|
9328
|
-
let push = false;
|
|
9329
|
-
let apiKey;
|
|
9330
|
-
let model;
|
|
9331
|
-
let local = false;
|
|
9332
|
-
let base;
|
|
9333
|
-
let create = false;
|
|
9334
|
-
let from;
|
|
9335
|
-
let to;
|
|
9336
|
-
let write = false;
|
|
9337
|
-
let version;
|
|
9338
|
-
let uninstall = false;
|
|
9339
|
-
let hookMode = false;
|
|
9340
|
-
for (let i = 0; i < args.length; i++) {
|
|
9341
|
-
const arg = args[i];
|
|
9342
|
-
if (arg === "-h" || arg === "--help") {
|
|
9343
|
-
command = "help";
|
|
9344
|
-
} else if (arg === "-a" || arg === "--all") {
|
|
9345
|
-
all = true;
|
|
9346
|
-
} else if (arg === "-m" || arg === "--message-only") {
|
|
9347
|
-
messageOnly = true;
|
|
9348
|
-
} else if (arg === "-p" || arg === "--push") {
|
|
9349
|
-
push = true;
|
|
9350
|
-
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
9351
|
-
apiKey = args[++i];
|
|
9352
|
-
} else if (arg === "--base" && i + 1 < args.length) {
|
|
9353
|
-
base = args[++i];
|
|
9354
|
-
} else if (arg === "--create") {
|
|
9355
|
-
create = true;
|
|
9356
|
-
} else if (arg === "--from" && i + 1 < args.length) {
|
|
9357
|
-
from = args[++i];
|
|
9358
|
-
} else if (arg === "--to" && i + 1 < args.length) {
|
|
9359
|
-
to = args[++i];
|
|
9360
|
-
} else if (arg === "--write") {
|
|
9361
|
-
write = true;
|
|
9362
|
-
} else if (arg === "--version" && i + 1 < args.length) {
|
|
9363
|
-
version = args[++i];
|
|
9364
|
-
} else if (arg === "--uninstall") {
|
|
9365
|
-
uninstall = true;
|
|
9366
|
-
} else if (arg === "--hook-mode") {
|
|
9367
|
-
hookMode = true;
|
|
9368
|
-
} else if (arg === "login") {
|
|
9369
|
-
command = "login";
|
|
9370
|
-
} else if (arg === "logout") {
|
|
9371
|
-
command = "logout";
|
|
9372
|
-
} else if (arg === "status") {
|
|
9373
|
-
command = "status";
|
|
9374
|
-
} else if (arg === "pr") {
|
|
9375
|
-
command = "pr";
|
|
9376
|
-
} else if (arg === "changelog") {
|
|
9377
|
-
command = "changelog";
|
|
9378
|
-
} else if (arg === "init") {
|
|
9379
|
-
command = "init";
|
|
9380
|
-
} else if (arg === "team") {
|
|
9381
|
-
command = "team";
|
|
9382
|
-
} else if (arg === "config") {
|
|
9383
|
-
command = "config";
|
|
9384
|
-
} else if (arg === "upgrade") {
|
|
9385
|
-
command = "upgrade";
|
|
9386
|
-
} else if (arg === "changeset") {
|
|
9387
|
-
command = "changeset";
|
|
9388
|
-
} else if (arg === "--model" && i + 1 < args.length) {
|
|
9389
|
-
model = args[++i];
|
|
9390
|
-
} else if (arg === "--local" || arg === "--use-ollama" || arg === "--use-lmstudio" || arg === "--use-openrouter" || arg === "--use-cloudflare") {
|
|
9391
|
-
local = true;
|
|
9392
|
-
if (arg === "--use-ollama") {
|
|
9393
|
-
saveConfig({ ...getConfig(), provider: "ollama", apiUrl: "http://localhost:11434", model: "codellama" });
|
|
9394
|
-
} else if (arg === "--use-lmstudio") {
|
|
9395
|
-
saveConfig({ ...getConfig(), provider: "lmstudio", apiUrl: "http://localhost:1234/v1", model: "default" });
|
|
9396
|
-
} else if (arg === "--use-openrouter") {
|
|
9397
|
-
saveConfig({ ...getConfig(), provider: "openrouter", apiUrl: "https://openrouter.ai/api/v1", model: "google/gemini-flash-1.5-8b" });
|
|
9398
|
-
} else if (arg === "--use-cloudflare") {
|
|
9399
|
-
saveConfig({
|
|
9400
|
-
...getConfig(),
|
|
9401
|
-
provider: "cloudflare",
|
|
9402
|
-
apiUrl: "https://YOUR-WORKER.workers.dev",
|
|
9403
|
-
model: "@cf/qwen/qwen2.5-coder-32b-instruct"
|
|
9404
|
-
});
|
|
9405
|
-
console.error(
|
|
9406
|
-
"[qc] Cloudflare provider set. Run: qc config set api_url https://your-worker.workers.dev"
|
|
9407
|
-
);
|
|
9408
|
-
}
|
|
9409
|
-
}
|
|
9410
|
-
}
|
|
9411
|
-
return { command, all, messageOnly, push, apiKey, base, create, from, to, write, version, uninstall, hookMode, model, local };
|
|
9412
|
-
}
|
|
9413
|
-
async function runCommit(messageOnly, push, apiKeyFlag, hookMode = false, modelFlag, stageAll_) {
|
|
9414
|
-
const log = hookMode ? () => {
|
|
9415
|
-
} : (msg) => console.error(msg);
|
|
9823
|
+
// src/commands/commit.ts
|
|
9824
|
+
var commit_exports = {};
|
|
9825
|
+
__export(commit_exports, {
|
|
9826
|
+
runCommit: () => runCommit
|
|
9827
|
+
});
|
|
9828
|
+
async function runCommit(args) {
|
|
9829
|
+
const { messageOnly, push, apiKey: apiKeyFlag, hookMode, model: modelFlag, all } = args;
|
|
9830
|
+
const silent = !!(hookMode || args.quiet);
|
|
9831
|
+
const log = silent ? createSilentLog() : getUI().log;
|
|
9416
9832
|
if (!isGitRepo()) {
|
|
9417
|
-
log("
|
|
9833
|
+
log.error("Not a git repository.");
|
|
9418
9834
|
process.exit(1);
|
|
9419
9835
|
}
|
|
9420
9836
|
const config2 = getConfig();
|
|
9421
|
-
if (
|
|
9837
|
+
if (all || config2.autoStage) {
|
|
9422
9838
|
stageAll();
|
|
9839
|
+
const { files, total } = getShortStagedFiles();
|
|
9840
|
+
const fileList = total > 3 ? `${files.join(", ")}, +${total - 3} more` : files.join(", ");
|
|
9841
|
+
log.step(`staging working tree (${total} file(s))...`);
|
|
9842
|
+
if (fileList) log.dim(` ${fileList}`);
|
|
9423
9843
|
}
|
|
9424
9844
|
if (!hasStagedChanges()) {
|
|
9425
9845
|
const unstaged = getUnstagedFiles();
|
|
9426
9846
|
if (unstaged.length > 0) {
|
|
9427
|
-
log("
|
|
9847
|
+
log.error("No staged changes. Use `qc -a` to stage tracked files, or `git add` manually.");
|
|
9428
9848
|
} else {
|
|
9429
|
-
log("
|
|
9849
|
+
log.error("No changes to commit.");
|
|
9430
9850
|
}
|
|
9431
9851
|
process.exit(1);
|
|
9432
9852
|
}
|
|
9433
9853
|
const apiKey = apiKeyFlag ?? getApiKey();
|
|
9434
9854
|
if (!apiKey) {
|
|
9435
|
-
log("
|
|
9855
|
+
log.error("Not authenticated. Run `qc login` first.");
|
|
9436
9856
|
process.exit(1);
|
|
9437
9857
|
}
|
|
9438
9858
|
const model = modelFlag ?? config2.model;
|
|
9439
|
-
const excludes = config2.excludes ?? [];
|
|
9859
|
+
const excludes = [...config2.excludes ?? [], ...args.exclude];
|
|
9440
9860
|
const diff = getStagedDiff(excludes);
|
|
9441
9861
|
const changes = getStagedFiles();
|
|
9862
|
+
let processedDiff = diff;
|
|
9863
|
+
if (!args.noSmartDiff) {
|
|
9864
|
+
const smartResult = preprocessDiff(diff);
|
|
9865
|
+
processedDiff = smartResult.processedDiff;
|
|
9866
|
+
if (smartResult.summarized.length > 0) {
|
|
9867
|
+
log.step(
|
|
9868
|
+
`smart-diff: ${smartResult.summarized.length} file(s) summarized (saved ~${Math.round(smartResult.tokensSaved / 1e3)}K tokens)`
|
|
9869
|
+
);
|
|
9870
|
+
}
|
|
9871
|
+
}
|
|
9442
9872
|
const commitlintRules = await detectCommitlintRules();
|
|
9443
9873
|
let rules = { ...commitlintRules, ...config2.rules ?? {} };
|
|
9444
9874
|
const workspace = detectWorkspace();
|
|
@@ -9455,7 +9885,7 @@ async function runCommit(messageOnly, push, apiKeyFlag, hookMode = false, modelF
|
|
|
9455
9885
|
try {
|
|
9456
9886
|
const teamRules = await client.getTeamRules();
|
|
9457
9887
|
if (teamRules && Object.keys(teamRules).length > 0) {
|
|
9458
|
-
log("
|
|
9888
|
+
log.step("using team rules from org");
|
|
9459
9889
|
rules = { ...rules, ...teamRules };
|
|
9460
9890
|
if (monorepoScopes && teamRules.scopes && teamRules.scopes.length > 0) {
|
|
9461
9891
|
const allowed = new Set(teamRules.scopes);
|
|
@@ -9465,24 +9895,393 @@ async function runCommit(messageOnly, push, apiKeyFlag, hookMode = false, modelF
|
|
|
9465
9895
|
}
|
|
9466
9896
|
} catch {
|
|
9467
9897
|
}
|
|
9468
|
-
|
|
9898
|
+
rules = applyCliTypeScopeToRules(rules, args.type, args.scope);
|
|
9899
|
+
const recentCommits = args.noContext ? void 0 : getRecentBranchCommits(5);
|
|
9900
|
+
const generationHints = generationHintsFromArgs(args.split, args.forceBody);
|
|
9901
|
+
const skipInteractive = silent || args.quiet || shouldSkipTTYInteraction(args.hookMode);
|
|
9902
|
+
const skipConfirm = args.dryRun || messageOnly || silent || args.quiet || shouldSkipTTYInteraction(args.hookMode);
|
|
9903
|
+
const modelDisplay = model ?? "default";
|
|
9904
|
+
const spinner = getUI().spinner(`generating commit (${modelDisplay})...`);
|
|
9905
|
+
if (!silent) spinner.start();
|
|
9906
|
+
const t0 = Date.now();
|
|
9907
|
+
let generatedMessage;
|
|
9908
|
+
let diagnostics;
|
|
9909
|
+
try {
|
|
9910
|
+
({ message: generatedMessage, diagnostics } = await client.generateCommit(
|
|
9911
|
+
processedDiff,
|
|
9912
|
+
changes,
|
|
9913
|
+
rules,
|
|
9914
|
+
model,
|
|
9915
|
+
recentCommits,
|
|
9916
|
+
generationHints
|
|
9917
|
+
));
|
|
9918
|
+
} finally {
|
|
9919
|
+
spinner.stop();
|
|
9920
|
+
}
|
|
9921
|
+
const roundTripMs = Date.now() - t0;
|
|
9922
|
+
logVerboseDiagnostics((msg) => log.dim(msg), args.verbose, args.quiet, diagnostics, roundTripMs);
|
|
9923
|
+
let message = generatedMessage;
|
|
9924
|
+
if (args.interactive) {
|
|
9925
|
+
if (shouldSkipTTYInteraction(args.hookMode)) {
|
|
9926
|
+
if (!silent) log.dim("(--interactive ignored: not running in a TTY)");
|
|
9927
|
+
} else {
|
|
9928
|
+
const refineResult = await interactiveRefineMessage(message, { skip: skipInteractive });
|
|
9929
|
+
if (refineResult.action === "abort") {
|
|
9930
|
+
process.exit(0);
|
|
9931
|
+
}
|
|
9932
|
+
message = refineResult.message;
|
|
9933
|
+
}
|
|
9934
|
+
}
|
|
9469
9935
|
if (messageOnly) {
|
|
9470
9936
|
console.log(message);
|
|
9471
9937
|
return;
|
|
9472
9938
|
}
|
|
9939
|
+
displayCommitMessage(message, log);
|
|
9940
|
+
if (args.dryRun) {
|
|
9941
|
+
return;
|
|
9942
|
+
}
|
|
9943
|
+
if (args.confirm) {
|
|
9944
|
+
const confirmResult = await confirmCommit("Proceed with commit? [y/N]: ", { skip: skipConfirm });
|
|
9945
|
+
if (confirmResult.action === "abort") {
|
|
9946
|
+
process.exit(0);
|
|
9947
|
+
}
|
|
9948
|
+
}
|
|
9473
9949
|
gitCommit(message);
|
|
9950
|
+
const hash = getCommitHash();
|
|
9951
|
+
const branch = getCurrentBranch();
|
|
9952
|
+
log.step(`[${branch} ${hash}] committed`);
|
|
9474
9953
|
if (push) {
|
|
9954
|
+
log.step(`pushing to origin/${branch}...`);
|
|
9475
9955
|
gitPush();
|
|
9956
|
+
const stats = getPushStats();
|
|
9957
|
+
if (stats) {
|
|
9958
|
+
log.success(`pushed ${stats.commits} commit(s) \xB7 ${stats.stat}`);
|
|
9959
|
+
} else {
|
|
9960
|
+
log.success("pushed");
|
|
9961
|
+
}
|
|
9962
|
+
}
|
|
9963
|
+
}
|
|
9964
|
+
var init_commit = __esm({
|
|
9965
|
+
"src/commands/commit.ts"() {
|
|
9966
|
+
"use strict";
|
|
9967
|
+
init_config();
|
|
9968
|
+
init_api();
|
|
9969
|
+
init_commitlint();
|
|
9970
|
+
init_git();
|
|
9971
|
+
init_monorepo();
|
|
9972
|
+
init_ui();
|
|
9973
|
+
init_smart_diff();
|
|
9974
|
+
init_commit_helpers();
|
|
9975
|
+
}
|
|
9976
|
+
});
|
|
9977
|
+
|
|
9978
|
+
// src/index.ts
|
|
9979
|
+
var index_exports = {};
|
|
9980
|
+
__export(index_exports, {
|
|
9981
|
+
parseArgs: () => parseArgs
|
|
9982
|
+
});
|
|
9983
|
+
module.exports = __toCommonJS(index_exports);
|
|
9984
|
+
init_config();
|
|
9985
|
+
var HELP = `Quikcommit - AI-powered conventional commit messages
|
|
9986
|
+
|
|
9987
|
+
Usage:
|
|
9988
|
+
qc Generate commit message and commit (default)
|
|
9989
|
+
qc pr Generate PR description from branch commits
|
|
9990
|
+
qc changelog Generate changelog from commits since last tag
|
|
9991
|
+
qc changeset Automate pnpm changeset with AI
|
|
9992
|
+
qc init Install prepare-commit-msg hook
|
|
9993
|
+
qc login Sign in via browser
|
|
9994
|
+
qc logout Clear local credentials
|
|
9995
|
+
qc status Show auth, plan, usage
|
|
9996
|
+
qc team Team management (info, rules, invite)
|
|
9997
|
+
qc config Show/set config
|
|
9998
|
+
|
|
9999
|
+
Flags:
|
|
10000
|
+
-p, --push Commit and push
|
|
10001
|
+
-a, --all Stage all tracked changes first
|
|
10002
|
+
-m, --message-only Print message only (stdout, no commit)
|
|
10003
|
+
-v, --verbose Show diagnostics (model, token estimates, rules) + API round-trip ms on stderr
|
|
10004
|
+
-q, --quiet Minimal output
|
|
10005
|
+
-n, --dry-run Show message without committing
|
|
10006
|
+
-i, --interactive Interactive refinement mode
|
|
10007
|
+
-s, --split Multi-commit split mode
|
|
10008
|
+
-b, --body Force include body
|
|
10009
|
+
-l, --local Use local provider
|
|
10010
|
+
-c, --confirm Ask before committing
|
|
10011
|
+
-t, --type <type> Force commit type
|
|
10012
|
+
-S, --scope <scope> Force scope
|
|
10013
|
+
-e, --exclude <pat> Exclude files from diff (repeatable)
|
|
10014
|
+
|
|
10015
|
+
--no-context Skip commit history context
|
|
10016
|
+
--no-smart-diff Skip smart diff preprocessing
|
|
10017
|
+
--no-color Disable colors
|
|
10018
|
+
--model <id> Use specific model
|
|
10019
|
+
--base <branch> Base branch for pr/changeset (default: main)
|
|
10020
|
+
--create Create PR with gh CLI (qc pr --create)
|
|
10021
|
+
--from <ref> Start ref for changelog
|
|
10022
|
+
--to <ref> End ref for changelog
|
|
10023
|
+
--write Write changelog to CHANGELOG.md
|
|
10024
|
+
--hook-mode Silent mode for git hooks
|
|
10025
|
+
|
|
10026
|
+
Compose short flags: qc -ap (stage all + push), qc -apv (+ verbose)
|
|
10027
|
+
|
|
10028
|
+
Examples:
|
|
10029
|
+
qc # generate and commit
|
|
10030
|
+
qc -p # commit and push
|
|
10031
|
+
qc -ap # stage all, commit, push
|
|
10032
|
+
qc -m | pbcopy # copy message to clipboard
|
|
10033
|
+
qc -n # preview without committing
|
|
10034
|
+
qc -e "*.lock" # exclude lock files
|
|
10035
|
+
qc -t fix -S auth # force type and scope
|
|
10036
|
+
`;
|
|
10037
|
+
var SHORT_FLAGS = {
|
|
10038
|
+
p: "push",
|
|
10039
|
+
a: "all",
|
|
10040
|
+
m: "messageOnly",
|
|
10041
|
+
v: "verbose",
|
|
10042
|
+
q: "quiet",
|
|
10043
|
+
n: "dryRun",
|
|
10044
|
+
i: "interactive",
|
|
10045
|
+
s: "split",
|
|
10046
|
+
b: "forceBody",
|
|
10047
|
+
l: "local",
|
|
10048
|
+
c: "confirm"
|
|
10049
|
+
};
|
|
10050
|
+
var SHORT_FLAGS_WITH_VALUE = {
|
|
10051
|
+
t: "type",
|
|
10052
|
+
S: "scope",
|
|
10053
|
+
e: "exclude"
|
|
10054
|
+
};
|
|
10055
|
+
function parseArgs(args) {
|
|
10056
|
+
const result = {
|
|
10057
|
+
command: "commit",
|
|
10058
|
+
all: false,
|
|
10059
|
+
messageOnly: false,
|
|
10060
|
+
push: false,
|
|
10061
|
+
verbose: false,
|
|
10062
|
+
quiet: false,
|
|
10063
|
+
dryRun: false,
|
|
10064
|
+
interactive: false,
|
|
10065
|
+
split: false,
|
|
10066
|
+
forceBody: false,
|
|
10067
|
+
confirm: false,
|
|
10068
|
+
noContext: false,
|
|
10069
|
+
noSmartDiff: false,
|
|
10070
|
+
local: false,
|
|
10071
|
+
exclude: [],
|
|
10072
|
+
positionals: []
|
|
10073
|
+
};
|
|
10074
|
+
let subcommandSeen = false;
|
|
10075
|
+
for (let i = 0; i < args.length; i++) {
|
|
10076
|
+
const arg = args[i];
|
|
10077
|
+
if (arg === void 0) continue;
|
|
10078
|
+
if (arg.startsWith("-") && !arg.startsWith("--") && arg.length > 2) {
|
|
10079
|
+
const chars = [...arg.slice(1)];
|
|
10080
|
+
for (let j = 0; j < chars.length; j++) {
|
|
10081
|
+
const ch = chars[j];
|
|
10082
|
+
if (!ch) continue;
|
|
10083
|
+
if (SHORT_FLAGS[ch]) {
|
|
10084
|
+
const key = SHORT_FLAGS[ch];
|
|
10085
|
+
result[key] = true;
|
|
10086
|
+
} else if (SHORT_FLAGS_WITH_VALUE[ch]) {
|
|
10087
|
+
if (j < chars.length - 1) {
|
|
10088
|
+
throw new Error(`Flag -${ch} requires a value and must be last in a composed group`);
|
|
10089
|
+
}
|
|
10090
|
+
const val = args[++i];
|
|
10091
|
+
if (!val || val.startsWith("-") && val.length > 1) throw new Error(`Flag -${ch} requires a value`);
|
|
10092
|
+
const key = SHORT_FLAGS_WITH_VALUE[ch];
|
|
10093
|
+
if (key === "exclude") {
|
|
10094
|
+
result.exclude.push(val);
|
|
10095
|
+
} else {
|
|
10096
|
+
result[key] = val;
|
|
10097
|
+
}
|
|
10098
|
+
} else if (ch === "h") {
|
|
10099
|
+
result.command = "help";
|
|
10100
|
+
} else {
|
|
10101
|
+
throw new Error(`Unknown flag: -${ch}`);
|
|
10102
|
+
}
|
|
10103
|
+
}
|
|
10104
|
+
continue;
|
|
10105
|
+
}
|
|
10106
|
+
if (arg.length === 2 && arg.startsWith("-") && !arg.startsWith("--")) {
|
|
10107
|
+
const ch = arg[1];
|
|
10108
|
+
if (!ch) continue;
|
|
10109
|
+
if (SHORT_FLAGS[ch]) {
|
|
10110
|
+
result[SHORT_FLAGS[ch]] = true;
|
|
10111
|
+
continue;
|
|
10112
|
+
}
|
|
10113
|
+
if (SHORT_FLAGS_WITH_VALUE[ch]) {
|
|
10114
|
+
const val = args[++i];
|
|
10115
|
+
if (!val || val.startsWith("-") && val.length > 1) {
|
|
10116
|
+
throw new Error(`Flag -${ch} requires a value`);
|
|
10117
|
+
}
|
|
10118
|
+
const key = SHORT_FLAGS_WITH_VALUE[ch];
|
|
10119
|
+
if (key === "exclude") {
|
|
10120
|
+
result.exclude.push(val);
|
|
10121
|
+
} else {
|
|
10122
|
+
result[key] = val;
|
|
10123
|
+
}
|
|
10124
|
+
continue;
|
|
10125
|
+
}
|
|
10126
|
+
if (ch === "h") {
|
|
10127
|
+
result.command = "help";
|
|
10128
|
+
continue;
|
|
10129
|
+
}
|
|
10130
|
+
throw new Error(`Unknown flag: -${ch}`);
|
|
10131
|
+
}
|
|
10132
|
+
if (arg === "--help") {
|
|
10133
|
+
result.command = "help";
|
|
10134
|
+
} else if (arg === "--all") {
|
|
10135
|
+
result.all = true;
|
|
10136
|
+
} else if (arg === "--message-only") {
|
|
10137
|
+
result.messageOnly = true;
|
|
10138
|
+
} else if (arg === "--push") {
|
|
10139
|
+
result.push = true;
|
|
10140
|
+
} else if (arg === "--verbose") {
|
|
10141
|
+
result.verbose = true;
|
|
10142
|
+
} else if (arg === "--quiet") {
|
|
10143
|
+
result.quiet = true;
|
|
10144
|
+
} else if (arg === "--dry-run") {
|
|
10145
|
+
result.dryRun = true;
|
|
10146
|
+
} else if (arg === "--interactive") {
|
|
10147
|
+
result.interactive = true;
|
|
10148
|
+
} else if (arg === "--split") {
|
|
10149
|
+
result.split = true;
|
|
10150
|
+
} else if (arg === "--body") {
|
|
10151
|
+
result.forceBody = true;
|
|
10152
|
+
} else if (arg === "--confirm") {
|
|
10153
|
+
result.confirm = true;
|
|
10154
|
+
} else if (arg === "--no-confirm") {
|
|
10155
|
+
result.confirm = false;
|
|
10156
|
+
} else if (arg === "--no-context") {
|
|
10157
|
+
result.noContext = true;
|
|
10158
|
+
} else if (arg === "--no-smart-diff") {
|
|
10159
|
+
result.noSmartDiff = true;
|
|
10160
|
+
} else if (arg === "--local" || arg === "--use-ollama" || arg === "--use-lmstudio" || arg === "--use-openrouter" || arg === "--use-cloudflare") {
|
|
10161
|
+
result.local = true;
|
|
10162
|
+
if (arg === "--use-ollama") {
|
|
10163
|
+
result.setProvider = "ollama";
|
|
10164
|
+
} else if (arg === "--use-lmstudio") {
|
|
10165
|
+
result.setProvider = "lmstudio";
|
|
10166
|
+
} else if (arg === "--use-openrouter") {
|
|
10167
|
+
result.setProvider = "openrouter";
|
|
10168
|
+
} else if (arg === "--use-cloudflare") {
|
|
10169
|
+
result.setProvider = "cloudflare";
|
|
10170
|
+
}
|
|
10171
|
+
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
10172
|
+
result.apiKey = args[++i];
|
|
10173
|
+
} else if (arg === "--base" && i + 1 < args.length) {
|
|
10174
|
+
result.base = args[++i];
|
|
10175
|
+
} else if (arg === "--create") {
|
|
10176
|
+
result.create = true;
|
|
10177
|
+
} else if (arg === "--from" && i + 1 < args.length) {
|
|
10178
|
+
result.from = args[++i];
|
|
10179
|
+
} else if (arg === "--to" && i + 1 < args.length) {
|
|
10180
|
+
result.to = args[++i];
|
|
10181
|
+
} else if (arg === "--write") {
|
|
10182
|
+
result.write = true;
|
|
10183
|
+
} else if (arg === "--version" && i + 1 < args.length) {
|
|
10184
|
+
result.version = args[++i];
|
|
10185
|
+
} else if (arg === "--uninstall") {
|
|
10186
|
+
result.uninstall = true;
|
|
10187
|
+
} else if (arg === "--hook-mode") {
|
|
10188
|
+
result.hookMode = true;
|
|
10189
|
+
} else if (arg === "--model" && i + 1 < args.length) {
|
|
10190
|
+
result.model = args[++i];
|
|
10191
|
+
} else if (arg === "--type" && i + 1 < args.length) {
|
|
10192
|
+
result.type = args[++i];
|
|
10193
|
+
} else if (arg === "--scope" && i + 1 < args.length) {
|
|
10194
|
+
result.scope = args[++i];
|
|
10195
|
+
} else if (arg === "--exclude" && i + 1 < args.length) {
|
|
10196
|
+
const ex = args[++i];
|
|
10197
|
+
if (ex) result.exclude.push(ex);
|
|
10198
|
+
} else if (arg === "--no-color") {
|
|
10199
|
+
} else if (arg === "login") {
|
|
10200
|
+
result.command = "login";
|
|
10201
|
+
subcommandSeen = true;
|
|
10202
|
+
} else if (arg === "logout") {
|
|
10203
|
+
result.command = "logout";
|
|
10204
|
+
subcommandSeen = true;
|
|
10205
|
+
} else if (arg === "status") {
|
|
10206
|
+
result.command = "status";
|
|
10207
|
+
subcommandSeen = true;
|
|
10208
|
+
} else if (arg === "pr") {
|
|
10209
|
+
result.command = "pr";
|
|
10210
|
+
subcommandSeen = true;
|
|
10211
|
+
} else if (arg === "changelog") {
|
|
10212
|
+
result.command = "changelog";
|
|
10213
|
+
subcommandSeen = true;
|
|
10214
|
+
} else if (arg === "init") {
|
|
10215
|
+
result.command = "init";
|
|
10216
|
+
subcommandSeen = true;
|
|
10217
|
+
} else if (arg === "team") {
|
|
10218
|
+
result.command = "team";
|
|
10219
|
+
subcommandSeen = true;
|
|
10220
|
+
} else if (arg === "config") {
|
|
10221
|
+
result.command = "config";
|
|
10222
|
+
subcommandSeen = true;
|
|
10223
|
+
} else if (arg === "upgrade") {
|
|
10224
|
+
result.command = "upgrade";
|
|
10225
|
+
subcommandSeen = true;
|
|
10226
|
+
} else if (arg === "changeset") {
|
|
10227
|
+
result.command = "changeset";
|
|
10228
|
+
subcommandSeen = true;
|
|
10229
|
+
} else if (subcommandSeen && !arg.startsWith("-")) {
|
|
10230
|
+
result.positionals.push(arg);
|
|
10231
|
+
}
|
|
10232
|
+
}
|
|
10233
|
+
if (result.messageOnly && result.push) {
|
|
10234
|
+
throw new Error("Cannot combine --message-only (-m) with --push (-p)");
|
|
10235
|
+
}
|
|
10236
|
+
if (result.quiet && result.verbose) {
|
|
10237
|
+
throw new Error("Cannot combine --quiet (-q) with --verbose (-v)");
|
|
9476
10238
|
}
|
|
10239
|
+
if (result.dryRun && result.push) {
|
|
10240
|
+
throw new Error("Cannot combine --dry-run (-n) with --push (-p). Pick one.");
|
|
10241
|
+
}
|
|
10242
|
+
return result;
|
|
9477
10243
|
}
|
|
9478
10244
|
async function main() {
|
|
9479
10245
|
const argv = process.argv.slice(2);
|
|
9480
|
-
|
|
9481
|
-
|
|
10246
|
+
let values;
|
|
10247
|
+
try {
|
|
10248
|
+
values = parseArgs(argv);
|
|
10249
|
+
} catch (err) {
|
|
10250
|
+
console.error(err instanceof Error ? err.message : String(err));
|
|
10251
|
+
process.exit(1);
|
|
10252
|
+
}
|
|
10253
|
+
const { command, apiKey } = values;
|
|
9482
10254
|
if (command === "help") {
|
|
9483
10255
|
console.log(HELP);
|
|
9484
10256
|
return;
|
|
9485
10257
|
}
|
|
10258
|
+
if (values.setProvider) {
|
|
10259
|
+
switch (values.setProvider) {
|
|
10260
|
+
case "ollama":
|
|
10261
|
+
saveConfig({ ...getConfig(), provider: "ollama", apiUrl: "http://localhost:11434", model: "codellama" });
|
|
10262
|
+
break;
|
|
10263
|
+
case "lmstudio":
|
|
10264
|
+
saveConfig({ ...getConfig(), provider: "lmstudio", apiUrl: "http://localhost:1234/v1", model: "default" });
|
|
10265
|
+
break;
|
|
10266
|
+
case "openrouter":
|
|
10267
|
+
saveConfig({
|
|
10268
|
+
...getConfig(),
|
|
10269
|
+
provider: "openrouter",
|
|
10270
|
+
apiUrl: "https://openrouter.ai/api/v1",
|
|
10271
|
+
model: "google/gemini-flash-1.5-8b"
|
|
10272
|
+
});
|
|
10273
|
+
break;
|
|
10274
|
+
case "cloudflare":
|
|
10275
|
+
saveConfig({
|
|
10276
|
+
...getConfig(),
|
|
10277
|
+
provider: "cloudflare",
|
|
10278
|
+
apiUrl: "https://YOUR-WORKER.workers.dev",
|
|
10279
|
+
model: "@cf/qwen/qwen2.5-coder-32b-instruct"
|
|
10280
|
+
});
|
|
10281
|
+
console.error("[qc] Cloudflare provider set. Run: qc config set api_url https://your-worker.workers.dev");
|
|
10282
|
+
break;
|
|
10283
|
+
}
|
|
10284
|
+
}
|
|
9486
10285
|
if (command === "login") {
|
|
9487
10286
|
const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
9488
10287
|
await runLogin2();
|
|
@@ -9533,14 +10332,12 @@ async function main() {
|
|
|
9533
10332
|
}
|
|
9534
10333
|
if (command === "team") {
|
|
9535
10334
|
const { team: team2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
9536
|
-
|
|
9537
|
-
await team2(positionals[0], positionals.slice(1));
|
|
10335
|
+
await team2(values.positionals[0], values.positionals.slice(1));
|
|
9538
10336
|
return;
|
|
9539
10337
|
}
|
|
9540
10338
|
if (command === "config") {
|
|
9541
10339
|
const { config: config2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
9542
|
-
|
|
9543
|
-
config2(positionals);
|
|
10340
|
+
config2(values.positionals);
|
|
9544
10341
|
return;
|
|
9545
10342
|
}
|
|
9546
10343
|
if (command === "upgrade") {
|
|
@@ -9550,7 +10347,7 @@ async function main() {
|
|
|
9550
10347
|
}
|
|
9551
10348
|
if (values.local) {
|
|
9552
10349
|
const { runLocalCommit: runLocalCommit2 } = await Promise.resolve().then(() => (init_local(), local_exports));
|
|
9553
|
-
await runLocalCommit2(
|
|
10350
|
+
await runLocalCommit2(values);
|
|
9554
10351
|
return;
|
|
9555
10352
|
}
|
|
9556
10353
|
const apiKeyToUse = apiKey ?? getApiKey();
|
|
@@ -9558,11 +10355,12 @@ async function main() {
|
|
|
9558
10355
|
const { getLocalProviderConfig: getLocalProviderConfig2 } = await Promise.resolve().then(() => (init_local(), local_exports));
|
|
9559
10356
|
if (getLocalProviderConfig2()) {
|
|
9560
10357
|
const { runLocalCommit: runLocalCommit2 } = await Promise.resolve().then(() => (init_local(), local_exports));
|
|
9561
|
-
await runLocalCommit2(
|
|
10358
|
+
await runLocalCommit2(values);
|
|
9562
10359
|
return;
|
|
9563
10360
|
}
|
|
9564
10361
|
}
|
|
9565
|
-
|
|
10362
|
+
const { runCommit: runCommit2 } = await Promise.resolve().then(() => (init_commit(), commit_exports));
|
|
10363
|
+
await runCommit2(values);
|
|
9566
10364
|
}
|
|
9567
10365
|
main().catch((err) => {
|
|
9568
10366
|
const args = process.argv.slice(2);
|
|
@@ -9572,3 +10370,7 @@ main().catch((err) => {
|
|
|
9572
10370
|
}
|
|
9573
10371
|
process.exit(1);
|
|
9574
10372
|
});
|
|
10373
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
10374
|
+
0 && (module.exports = {
|
|
10375
|
+
parseArgs
|
|
10376
|
+
});
|