@quikcommit/cli 6.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 +1377 -542
- 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({
|
|
@@ -41,7 +42,7 @@ var init_types = __esm({
|
|
|
41
42
|
});
|
|
42
43
|
|
|
43
44
|
// ../shared/dist/constants.js
|
|
44
|
-
var CONFIG_DIR, CREDENTIALS_FILE, CONFIG_FILE, DEFAULT_API_URL, MAX_PR_CURRENT_BRANCH_CHARS,
|
|
45
|
+
var CONFIG_DIR, CREDENTIALS_FILE, CONFIG_FILE, DEFAULT_API_URL, MAX_PR_CURRENT_BRANCH_CHARS, DEVICE_FLOW_TIMEOUT;
|
|
45
46
|
var init_constants = __esm({
|
|
46
47
|
"../shared/dist/constants.js"() {
|
|
47
48
|
"use strict";
|
|
@@ -50,7 +51,6 @@ var init_constants = __esm({
|
|
|
50
51
|
CONFIG_FILE = "config.json";
|
|
51
52
|
DEFAULT_API_URL = "https://api.quikcommit.dev";
|
|
52
53
|
MAX_PR_CURRENT_BRANCH_CHARS = 256;
|
|
53
|
-
DEVICE_POLL_INTERVAL = 1e3;
|
|
54
54
|
DEVICE_FLOW_TIMEOUT = 6e5;
|
|
55
55
|
}
|
|
56
56
|
});
|
|
@@ -62,6 +62,16 @@ var init_rules = __esm({
|
|
|
62
62
|
}
|
|
63
63
|
});
|
|
64
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
|
+
|
|
65
75
|
// ../shared/dist/index.js
|
|
66
76
|
var init_dist = __esm({
|
|
67
77
|
"../shared/dist/index.js"() {
|
|
@@ -69,6 +79,7 @@ var init_dist = __esm({
|
|
|
69
79
|
init_types();
|
|
70
80
|
init_constants();
|
|
71
81
|
init_rules();
|
|
82
|
+
init_tokens();
|
|
72
83
|
}
|
|
73
84
|
});
|
|
74
85
|
|
|
@@ -126,6 +137,149 @@ var init_config = __esm({
|
|
|
126
137
|
}
|
|
127
138
|
});
|
|
128
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
|
+
|
|
129
283
|
// src/api.ts
|
|
130
284
|
var ApiClient;
|
|
131
285
|
var init_api = __esm({
|
|
@@ -164,8 +318,15 @@ var init_api = __esm({
|
|
|
164
318
|
}
|
|
165
319
|
return res.json();
|
|
166
320
|
}
|
|
167
|
-
async generateCommit(diff, changes, rules, model) {
|
|
168
|
-
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
|
+
};
|
|
169
330
|
const data = await this.request(
|
|
170
331
|
"/v1/commit",
|
|
171
332
|
body
|
|
@@ -249,6 +410,37 @@ var init_api = __esm({
|
|
|
249
410
|
}
|
|
250
411
|
});
|
|
251
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
|
+
|
|
252
444
|
// ../../node_modules/.pnpm/yaml@2.8.4/node_modules/yaml/dist/nodes/identity.js
|
|
253
445
|
var require_identity = __commonJS({
|
|
254
446
|
"../../node_modules/.pnpm/yaml@2.8.4/node_modules/yaml/dist/nodes/identity.js"(exports2) {
|
|
@@ -7565,7 +7757,7 @@ function validateRef(ref, name = "ref") {
|
|
|
7565
7757
|
}
|
|
7566
7758
|
function isGitRepo() {
|
|
7567
7759
|
try {
|
|
7568
|
-
(0,
|
|
7760
|
+
(0, import_child_process2.execFileSync)("git", ["rev-parse", "--is-inside-work-tree"], {
|
|
7569
7761
|
stdio: "pipe"
|
|
7570
7762
|
});
|
|
7571
7763
|
return true;
|
|
@@ -7575,7 +7767,7 @@ function isGitRepo() {
|
|
|
7575
7767
|
}
|
|
7576
7768
|
function getGitRoot() {
|
|
7577
7769
|
try {
|
|
7578
|
-
return (0,
|
|
7770
|
+
return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--show-toplevel"], {
|
|
7579
7771
|
encoding: "utf-8"
|
|
7580
7772
|
}).trim();
|
|
7581
7773
|
} catch {
|
|
@@ -7591,37 +7783,37 @@ function getStagedDiff(excludes = []) {
|
|
|
7591
7783
|
args.push(`:(exclude)${pattern}`);
|
|
7592
7784
|
}
|
|
7593
7785
|
}
|
|
7594
|
-
return (0,
|
|
7786
|
+
return (0, import_child_process2.execFileSync)("git", args, {
|
|
7595
7787
|
encoding: "utf-8",
|
|
7596
7788
|
maxBuffer: 10 * 1024 * 1024
|
|
7597
7789
|
});
|
|
7598
7790
|
}
|
|
7599
7791
|
function getStagedFiles() {
|
|
7600
|
-
return (0,
|
|
7792
|
+
return (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
|
|
7601
7793
|
encoding: "utf-8"
|
|
7602
7794
|
});
|
|
7603
7795
|
}
|
|
7604
7796
|
function hasStagedChanges() {
|
|
7605
|
-
const output = (0,
|
|
7797
|
+
const output = (0, import_child_process2.execFileSync)("git", ["diff", "--cached", "--name-only"], {
|
|
7606
7798
|
encoding: "utf-8"
|
|
7607
7799
|
});
|
|
7608
7800
|
return output.trim().length > 0;
|
|
7609
7801
|
}
|
|
7610
7802
|
function getUnstagedFiles() {
|
|
7611
|
-
const output = (0,
|
|
7803
|
+
const output = (0, import_child_process2.execFileSync)("git", ["status", "--porcelain"], {
|
|
7612
7804
|
encoding: "utf-8"
|
|
7613
7805
|
});
|
|
7614
7806
|
return output.trim().split("\n").filter(Boolean).filter((line) => !line.startsWith("??"));
|
|
7615
7807
|
}
|
|
7616
7808
|
function stageAll() {
|
|
7617
|
-
(0,
|
|
7809
|
+
(0, import_child_process2.execFileSync)("git", ["add", "-u"], { stdio: "pipe" });
|
|
7618
7810
|
}
|
|
7619
7811
|
function gitCommit(message) {
|
|
7620
|
-
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-"));
|
|
7621
7813
|
const tmpFile = (0, import_path2.join)(tmpDir, "commit.txt");
|
|
7622
7814
|
(0, import_fs2.writeFileSync)(tmpFile, message, { mode: 384 });
|
|
7623
7815
|
try {
|
|
7624
|
-
(0,
|
|
7816
|
+
(0, import_child_process2.execFileSync)("git", ["commit", "-F", tmpFile], { stdio: "inherit" });
|
|
7625
7817
|
} finally {
|
|
7626
7818
|
try {
|
|
7627
7819
|
(0, import_fs2.unlinkSync)(tmpFile);
|
|
@@ -7631,11 +7823,11 @@ function gitCommit(message) {
|
|
|
7631
7823
|
}
|
|
7632
7824
|
}
|
|
7633
7825
|
function gitPush() {
|
|
7634
|
-
(0,
|
|
7826
|
+
(0, import_child_process2.execFileSync)("git", ["push"], { stdio: "inherit" });
|
|
7635
7827
|
}
|
|
7636
7828
|
function getBranchCommits(base = "main") {
|
|
7637
7829
|
validateRef(base, "base");
|
|
7638
|
-
const output = (0,
|
|
7830
|
+
const output = (0, import_child_process2.execFileSync)("git", ["log", `${base}..HEAD`, "--format=%s", "--max-count=1000"], {
|
|
7639
7831
|
encoding: "utf-8",
|
|
7640
7832
|
maxBuffer: 10 * 1024 * 1024
|
|
7641
7833
|
});
|
|
@@ -7643,19 +7835,19 @@ function getBranchCommits(base = "main") {
|
|
|
7643
7835
|
}
|
|
7644
7836
|
function getDiffStat(base = "main") {
|
|
7645
7837
|
validateRef(base, "base");
|
|
7646
|
-
return (0,
|
|
7838
|
+
return (0, import_child_process2.execFileSync)("git", ["diff", `${base}..HEAD`, "--stat"], {
|
|
7647
7839
|
encoding: "utf-8",
|
|
7648
7840
|
maxBuffer: 10 * 1024 * 1024
|
|
7649
7841
|
});
|
|
7650
7842
|
}
|
|
7651
7843
|
function getCurrentBranch() {
|
|
7652
|
-
return (0,
|
|
7844
|
+
return (0, import_child_process2.execFileSync)("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
7653
7845
|
encoding: "utf-8"
|
|
7654
7846
|
}).trim();
|
|
7655
7847
|
}
|
|
7656
7848
|
function getLatestTag() {
|
|
7657
7849
|
try {
|
|
7658
|
-
return (0,
|
|
7850
|
+
return (0, import_child_process2.execFileSync)("git", ["describe", "--tags", "--abbrev=0"], {
|
|
7659
7851
|
encoding: "utf-8"
|
|
7660
7852
|
}).trim();
|
|
7661
7853
|
} catch {
|
|
@@ -7665,7 +7857,7 @@ function getLatestTag() {
|
|
|
7665
7857
|
function getCommitsSince(ref, to = "HEAD") {
|
|
7666
7858
|
validateRef(ref, "from ref");
|
|
7667
7859
|
validateRef(to, "to ref");
|
|
7668
|
-
const output = (0,
|
|
7860
|
+
const output = (0, import_child_process2.execFileSync)(
|
|
7669
7861
|
"git",
|
|
7670
7862
|
["log", `${ref}..${to}`, "--format=%H %s", "--max-count=1000"],
|
|
7671
7863
|
{ encoding: "utf-8", maxBuffer: 10 * 1024 * 1024 }
|
|
@@ -7677,7 +7869,7 @@ function getCommitsSince(ref, to = "HEAD") {
|
|
|
7677
7869
|
}
|
|
7678
7870
|
function getChangedFilesSince(base = "main") {
|
|
7679
7871
|
validateRef(base, "base");
|
|
7680
|
-
const output = (0,
|
|
7872
|
+
const output = (0, import_child_process2.execFileSync)("git", ["diff", `${base}..HEAD`, "--name-only"], {
|
|
7681
7873
|
encoding: "utf-8",
|
|
7682
7874
|
maxBuffer: 10 * 1024 * 1024
|
|
7683
7875
|
});
|
|
@@ -7685,7 +7877,7 @@ function getChangedFilesSince(base = "main") {
|
|
|
7685
7877
|
}
|
|
7686
7878
|
function getOnlineLog(base = "main") {
|
|
7687
7879
|
validateRef(base, "base");
|
|
7688
|
-
return (0,
|
|
7880
|
+
return (0, import_child_process2.execFileSync)(
|
|
7689
7881
|
"git",
|
|
7690
7882
|
["log", `${base}..HEAD`, "--oneline", "--max-count=200"],
|
|
7691
7883
|
{
|
|
@@ -7696,19 +7888,65 @@ function getOnlineLog(base = "main") {
|
|
|
7696
7888
|
}
|
|
7697
7889
|
function getFullDiff(base = "main") {
|
|
7698
7890
|
validateRef(base, "base");
|
|
7699
|
-
return (0,
|
|
7891
|
+
return (0, import_child_process2.execFileSync)("git", ["diff", `${base}..HEAD`], {
|
|
7700
7892
|
encoding: "utf-8",
|
|
7701
7893
|
maxBuffer: 10 * 1024 * 1024
|
|
7702
7894
|
});
|
|
7703
7895
|
}
|
|
7704
|
-
|
|
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;
|
|
7705
7943
|
var init_git = __esm({
|
|
7706
7944
|
"src/git.ts"() {
|
|
7707
7945
|
"use strict";
|
|
7708
|
-
|
|
7946
|
+
import_child_process2 = require("child_process");
|
|
7709
7947
|
import_fs2 = require("fs");
|
|
7710
7948
|
import_path2 = require("path");
|
|
7711
|
-
|
|
7949
|
+
import_os3 = require("os");
|
|
7712
7950
|
SAFE_GIT_REF = /^[a-zA-Z0-9._\-/~:^@]+$/;
|
|
7713
7951
|
}
|
|
7714
7952
|
});
|
|
@@ -7775,7 +8013,7 @@ function mapRulesToCommitRules(rules) {
|
|
|
7775
8013
|
}
|
|
7776
8014
|
function tryNpxPrintConfig(root) {
|
|
7777
8015
|
try {
|
|
7778
|
-
const output = (0,
|
|
8016
|
+
const output = (0, import_child_process3.execFileSync)("npx", ["--no", "commitlint", "--print-config"], {
|
|
7779
8017
|
encoding: "utf-8",
|
|
7780
8018
|
cwd: root,
|
|
7781
8019
|
timeout: 1e4,
|
|
@@ -7792,7 +8030,7 @@ function tryNodeEval(configPath) {
|
|
|
7792
8030
|
const fileUrl = (0, import_node_url.pathToFileURL)(configPath).href;
|
|
7793
8031
|
const script = `import cfg from ${JSON.stringify(fileUrl)}; process.stdout.write(JSON.stringify(cfg.default ?? cfg));`;
|
|
7794
8032
|
try {
|
|
7795
|
-
const output = (0,
|
|
8033
|
+
const output = (0, import_child_process3.execFileSync)("node", ["--input-type=module"], {
|
|
7796
8034
|
input: script,
|
|
7797
8035
|
encoding: "utf-8",
|
|
7798
8036
|
timeout: 1e4,
|
|
@@ -7809,7 +8047,7 @@ function tryNodeEvalTs(configPath, root) {
|
|
|
7809
8047
|
const fileUrl = (0, import_node_url.pathToFileURL)(configPath).href;
|
|
7810
8048
|
const script = `import cfg from ${JSON.stringify(fileUrl)}; process.stdout.write(JSON.stringify(cfg.default ?? cfg));`;
|
|
7811
8049
|
try {
|
|
7812
|
-
const output = (0,
|
|
8050
|
+
const output = (0, import_child_process3.execFileSync)("node", ["--experimental-strip-types", "--input-type=module"], {
|
|
7813
8051
|
input: script,
|
|
7814
8052
|
encoding: "utf-8",
|
|
7815
8053
|
cwd: root,
|
|
@@ -7823,7 +8061,7 @@ function tryNodeEvalTs(configPath, root) {
|
|
|
7823
8061
|
}
|
|
7824
8062
|
try {
|
|
7825
8063
|
const tsxScript = `import cfg from ${JSON.stringify(fileUrl)}; console.log(JSON.stringify(cfg.default ?? cfg));`;
|
|
7826
|
-
const output = (0,
|
|
8064
|
+
const output = (0, import_child_process3.execFileSync)("npx", ["--no", "tsx", "-e", tsxScript], {
|
|
7827
8065
|
encoding: "utf-8",
|
|
7828
8066
|
cwd: root,
|
|
7829
8067
|
timeout: 15e3,
|
|
@@ -7876,11 +8114,11 @@ async function detectCommitlintRules() {
|
|
|
7876
8114
|
return void 0;
|
|
7877
8115
|
}
|
|
7878
8116
|
}
|
|
7879
|
-
var
|
|
8117
|
+
var import_child_process3, import_fs3, import_path3, import_node_url, import_yaml, CONFIG_FILES;
|
|
7880
8118
|
var init_commitlint = __esm({
|
|
7881
8119
|
"src/commitlint.ts"() {
|
|
7882
8120
|
"use strict";
|
|
7883
|
-
|
|
8121
|
+
import_child_process3 = require("child_process");
|
|
7884
8122
|
import_fs3 = require("fs");
|
|
7885
8123
|
import_path3 = require("path");
|
|
7886
8124
|
import_node_url = require("node:url");
|
|
@@ -7901,335 +8139,55 @@ var init_commitlint = __esm({
|
|
|
7901
8139
|
}
|
|
7902
8140
|
});
|
|
7903
8141
|
|
|
7904
|
-
// src/
|
|
7905
|
-
var
|
|
7906
|
-
__export(
|
|
7907
|
-
|
|
7908
|
-
detectWorkspace: () => detectWorkspace,
|
|
7909
|
-
getPackageForFile: () => getPackageForFile
|
|
8142
|
+
// src/commands/pr.ts
|
|
8143
|
+
var pr_exports = {};
|
|
8144
|
+
__export(pr_exports, {
|
|
8145
|
+
pr: () => pr
|
|
7910
8146
|
});
|
|
7911
|
-
function
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
7917
|
-
|
|
7918
|
-
} catch {
|
|
7919
|
-
return start;
|
|
7920
|
-
}
|
|
7921
|
-
}
|
|
7922
|
-
function detectWorkspace(cwd = findGitRoot(process.cwd())) {
|
|
7923
|
-
const pnpmWs = (0, import_path4.join)(cwd, "pnpm-workspace.yaml");
|
|
7924
|
-
if ((0, import_fs4.existsSync)(pnpmWs)) {
|
|
7925
|
-
const content = (0, import_fs4.readFileSync)(pnpmWs, "utf-8");
|
|
7926
|
-
const match = content.match(/packages:\s*\n((?:\s+-\s+.+\n?)*)/);
|
|
7927
|
-
if (match) {
|
|
7928
|
-
const packages = match[1].split("\n").map((l) => l.replace(/^\s+-\s+/, "").replace(/["']/g, "").trim()).filter(Boolean);
|
|
7929
|
-
return { type: "pnpm", packages, root: cwd };
|
|
7930
|
-
}
|
|
7931
|
-
}
|
|
7932
|
-
const lerna = (0, import_path4.join)(cwd, "lerna.json");
|
|
7933
|
-
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) {
|
|
7934
8154
|
try {
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
7938
|
-
packages: config2.packages ?? ["packages/*"],
|
|
7939
|
-
root: cwd
|
|
7940
|
-
};
|
|
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
|
+
}
|
|
7941
8158
|
} catch {
|
|
7942
8159
|
}
|
|
7943
8160
|
}
|
|
7944
|
-
|
|
7945
|
-
|
|
7946
|
-
|
|
7947
|
-
|
|
7948
|
-
|
|
7949
|
-
|
|
7950
|
-
|
|
7951
|
-
|
|
7952
|
-
const pkgPath2 = (0, import_path4.join)(cwd, "package.json");
|
|
7953
|
-
if ((0, import_fs4.existsSync)(pkgPath2)) {
|
|
7954
|
-
try {
|
|
7955
|
-
const config2 = JSON.parse((0, import_fs4.readFileSync)(pkgPath2, "utf-8"));
|
|
7956
|
-
if (config2.workspaces) {
|
|
7957
|
-
const ws = Array.isArray(config2.workspaces) ? config2.workspaces : config2.workspaces.packages ?? [];
|
|
7958
|
-
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") };
|
|
7959
8169
|
}
|
|
7960
|
-
} catch {
|
|
7961
|
-
}
|
|
7962
|
-
}
|
|
7963
|
-
}
|
|
7964
|
-
const pkgPath = (0, import_path4.join)(cwd, "package.json");
|
|
7965
|
-
if ((0, import_fs4.existsSync)(pkgPath)) {
|
|
7966
|
-
try {
|
|
7967
|
-
const config2 = JSON.parse((0, import_fs4.readFileSync)(pkgPath, "utf-8"));
|
|
7968
|
-
if (config2.workspaces) {
|
|
7969
|
-
const ws = Array.isArray(config2.workspaces) ? config2.workspaces : config2.workspaces.packages ?? [];
|
|
7970
|
-
return { type: "npm", packages: ws, root: cwd };
|
|
7971
8170
|
}
|
|
7972
|
-
} catch {
|
|
7973
8171
|
}
|
|
8172
|
+
} catch {
|
|
7974
8173
|
}
|
|
7975
|
-
return
|
|
8174
|
+
return void 0;
|
|
7976
8175
|
}
|
|
7977
|
-
function
|
|
7978
|
-
const
|
|
7979
|
-
|
|
7980
|
-
|
|
7981
|
-
|
|
8176
|
+
async function pr(options) {
|
|
8177
|
+
const base = options.base ?? "main";
|
|
8178
|
+
const commits = getBranchCommits(base);
|
|
8179
|
+
const diffStat = getDiffStat(base);
|
|
8180
|
+
const gitRoot = getGitRoot();
|
|
8181
|
+
const templateHit = findPullRequestTemplate(gitRoot);
|
|
8182
|
+
let prTemplate;
|
|
8183
|
+
if (templateHit) {
|
|
8184
|
+
prTemplate = templateHit.content.substring(0, 16 * 1024);
|
|
8185
|
+
console.error(`[qc] Using PR template from ${(0, import_path4.relative)(gitRoot, templateHit.path)}`);
|
|
7982
8186
|
}
|
|
7983
|
-
const
|
|
7984
|
-
if (
|
|
7985
|
-
|
|
7986
|
-
|
|
7987
|
-
const rest = rel.slice(prefix2.length);
|
|
7988
|
-
const pkg = rest.split("/")[0];
|
|
7989
|
-
return pkg || null;
|
|
7990
|
-
}
|
|
7991
|
-
return null;
|
|
7992
|
-
}
|
|
7993
|
-
const prefix = dir + "/";
|
|
7994
|
-
const hasGlob = /\*/.test(pattern);
|
|
7995
|
-
if (hasGlob) {
|
|
7996
|
-
if (rel.startsWith(prefix)) {
|
|
7997
|
-
const rest = rel.slice(prefix.length);
|
|
7998
|
-
const pkg = rest.split("/")[0];
|
|
7999
|
-
return pkg || null;
|
|
8000
|
-
}
|
|
8001
|
-
} else {
|
|
8002
|
-
if (rel === dir || rel.startsWith(prefix)) {
|
|
8003
|
-
const segments = dir.split("/").filter(Boolean);
|
|
8004
|
-
return segments[segments.length - 1] ?? null;
|
|
8005
|
-
}
|
|
8006
|
-
}
|
|
8007
|
-
return null;
|
|
8008
|
-
}
|
|
8009
|
-
function getPackageForFile(filePath, workspace) {
|
|
8010
|
-
const absPath = filePath.startsWith("/") ? filePath : (0, import_path4.join)(workspace.root, filePath);
|
|
8011
|
-
const rel = (0, import_path4.relative)(workspace.root, absPath);
|
|
8012
|
-
for (const pattern of workspace.packages) {
|
|
8013
|
-
const packageName = matchGlobPattern(rel, pattern);
|
|
8014
|
-
if (packageName) return packageName;
|
|
8015
|
-
}
|
|
8016
|
-
return null;
|
|
8017
|
-
}
|
|
8018
|
-
function autoDetectScope(stagedFiles, workspace) {
|
|
8019
|
-
const packages = /* @__PURE__ */ new Set();
|
|
8020
|
-
for (const file of stagedFiles) {
|
|
8021
|
-
const filePath = file.startsWith("/") ? file : (0, import_path4.join)(workspace.root, file);
|
|
8022
|
-
const pkg = getPackageForFile(filePath, workspace);
|
|
8023
|
-
if (pkg) packages.add(pkg);
|
|
8024
|
-
}
|
|
8025
|
-
if (packages.size === 1) return [...packages][0];
|
|
8026
|
-
if (packages.size > 1 && packages.size <= 3) return [...packages].join(",");
|
|
8027
|
-
if (packages.size > 3) {
|
|
8028
|
-
console.error(
|
|
8029
|
-
`[qc] Changes span ${packages.size} packages; skipping auto-scope detection.`
|
|
8030
|
-
);
|
|
8031
|
-
}
|
|
8032
|
-
return null;
|
|
8033
|
-
}
|
|
8034
|
-
var import_child_process3, import_fs4, import_path4;
|
|
8035
|
-
var init_monorepo = __esm({
|
|
8036
|
-
"src/monorepo.ts"() {
|
|
8037
|
-
"use strict";
|
|
8038
|
-
import_child_process3 = require("child_process");
|
|
8039
|
-
import_fs4 = require("fs");
|
|
8040
|
-
import_path4 = require("path");
|
|
8041
|
-
}
|
|
8042
|
-
});
|
|
8043
|
-
|
|
8044
|
-
// src/commands/login.ts
|
|
8045
|
-
var login_exports = {};
|
|
8046
|
-
__export(login_exports, {
|
|
8047
|
-
runLogin: () => runLogin
|
|
8048
|
-
});
|
|
8049
|
-
function openBrowser(url) {
|
|
8050
|
-
try {
|
|
8051
|
-
if ((0, import_os3.platform)() === "darwin") {
|
|
8052
|
-
(0, import_child_process4.execFileSync)("open", [url], { stdio: "pipe" });
|
|
8053
|
-
return true;
|
|
8054
|
-
}
|
|
8055
|
-
if ((0, import_os3.platform)() === "linux") {
|
|
8056
|
-
(0, import_child_process4.execFileSync)("xdg-open", [url], { stdio: "pipe" });
|
|
8057
|
-
return true;
|
|
8058
|
-
}
|
|
8059
|
-
if ((0, import_os3.platform)() === "win32") {
|
|
8060
|
-
(0, import_child_process4.execFileSync)("cmd", ["/c", "start", "", url], { stdio: "pipe" });
|
|
8061
|
-
return true;
|
|
8062
|
-
}
|
|
8063
|
-
} catch {
|
|
8064
|
-
}
|
|
8065
|
-
return false;
|
|
8066
|
-
}
|
|
8067
|
-
async function runLogin() {
|
|
8068
|
-
const startRes = await fetch(`${API_URL}/v1/auth/device/start`, {
|
|
8069
|
-
method: "POST",
|
|
8070
|
-
headers: { "Content-Type": "application/json" },
|
|
8071
|
-
body: JSON.stringify({})
|
|
8072
|
-
});
|
|
8073
|
-
if (!startRes.ok) {
|
|
8074
|
-
const err = await startRes.json().catch(() => ({ error: startRes.statusText }));
|
|
8075
|
-
throw new Error(err.error ?? "Failed to start device flow");
|
|
8076
|
-
}
|
|
8077
|
-
const startData = await startRes.json();
|
|
8078
|
-
const code = startData.device_code;
|
|
8079
|
-
if (!code) {
|
|
8080
|
-
throw new Error("Server did not return a device_code");
|
|
8081
|
-
}
|
|
8082
|
-
console.log("Opening browser to sign in...");
|
|
8083
|
-
console.log("");
|
|
8084
|
-
const authUrl = `${DASHBOARD_URL}/auth/cli?code=${encodeURIComponent(code)}`;
|
|
8085
|
-
const opened = openBrowser(authUrl);
|
|
8086
|
-
if (!opened) {
|
|
8087
|
-
console.log("Could not open browser. Please visit:");
|
|
8088
|
-
console.log(authUrl);
|
|
8089
|
-
console.log("");
|
|
8090
|
-
}
|
|
8091
|
-
let frame = 0;
|
|
8092
|
-
const spinner = setInterval(() => {
|
|
8093
|
-
const elapsed = Math.floor((Date.now() - startTime) / 1e3);
|
|
8094
|
-
process.stderr.write(
|
|
8095
|
-
`\r${SPINNER_FRAMES[frame++ % SPINNER_FRAMES.length]} Waiting for authorization... (${elapsed}s)`
|
|
8096
|
-
);
|
|
8097
|
-
}, 80);
|
|
8098
|
-
const startTime = Date.now();
|
|
8099
|
-
try {
|
|
8100
|
-
while (Date.now() - startTime < DEVICE_FLOW_TIMEOUT) {
|
|
8101
|
-
try {
|
|
8102
|
-
const res = await fetch(
|
|
8103
|
-
`${API_URL}/v1/auth/device/poll?code=${encodeURIComponent(code)}`
|
|
8104
|
-
);
|
|
8105
|
-
const data = await res.json();
|
|
8106
|
-
if (data.status === "complete" && data.api_key) {
|
|
8107
|
-
saveApiKey(data.api_key);
|
|
8108
|
-
process.stderr.write("\r\x1B[2K");
|
|
8109
|
-
console.log("Successfully logged in!");
|
|
8110
|
-
return;
|
|
8111
|
-
}
|
|
8112
|
-
} catch {
|
|
8113
|
-
}
|
|
8114
|
-
await new Promise((r) => setTimeout(r, DEVICE_POLL_INTERVAL));
|
|
8115
|
-
}
|
|
8116
|
-
process.stderr.write("\r\x1B[2K");
|
|
8117
|
-
console.error("Login timed out. Please try again.");
|
|
8118
|
-
process.exit(1);
|
|
8119
|
-
} finally {
|
|
8120
|
-
clearInterval(spinner);
|
|
8121
|
-
}
|
|
8122
|
-
}
|
|
8123
|
-
var import_child_process4, import_os3, API_URL, DASHBOARD_URL, SPINNER_FRAMES;
|
|
8124
|
-
var init_login = __esm({
|
|
8125
|
-
"src/commands/login.ts"() {
|
|
8126
|
-
"use strict";
|
|
8127
|
-
import_child_process4 = require("child_process");
|
|
8128
|
-
import_os3 = require("os");
|
|
8129
|
-
init_config();
|
|
8130
|
-
init_dist();
|
|
8131
|
-
API_URL = process.env.QC_API_URL ?? DEFAULT_API_URL;
|
|
8132
|
-
DASHBOARD_URL = "https://app.quikcommit.dev";
|
|
8133
|
-
SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
8134
|
-
}
|
|
8135
|
-
});
|
|
8136
|
-
|
|
8137
|
-
// src/commands/logout.ts
|
|
8138
|
-
var logout_exports = {};
|
|
8139
|
-
__export(logout_exports, {
|
|
8140
|
-
runLogout: () => runLogout
|
|
8141
|
-
});
|
|
8142
|
-
function runLogout() {
|
|
8143
|
-
clearApiKey();
|
|
8144
|
-
console.log("Logged out. Credentials cleared.");
|
|
8145
|
-
}
|
|
8146
|
-
var init_logout = __esm({
|
|
8147
|
-
"src/commands/logout.ts"() {
|
|
8148
|
-
"use strict";
|
|
8149
|
-
init_config();
|
|
8150
|
-
}
|
|
8151
|
-
});
|
|
8152
|
-
|
|
8153
|
-
// src/commands/status.ts
|
|
8154
|
-
var status_exports = {};
|
|
8155
|
-
__export(status_exports, {
|
|
8156
|
-
runStatus: () => runStatus
|
|
8157
|
-
});
|
|
8158
|
-
async function runStatus(apiKeyFlag) {
|
|
8159
|
-
const apiKey = apiKeyFlag ?? getApiKey();
|
|
8160
|
-
if (!apiKey) {
|
|
8161
|
-
console.log("Not logged in. Run `qc login` to authenticate.");
|
|
8162
|
-
return;
|
|
8163
|
-
}
|
|
8164
|
-
console.log("Logged in: yes");
|
|
8165
|
-
console.log(` API key: ...${apiKey.slice(-4)}`);
|
|
8166
|
-
const client = new ApiClient({ apiKey });
|
|
8167
|
-
const usage = await client.getUsage();
|
|
8168
|
-
if (usage) {
|
|
8169
|
-
console.log(`Plan: ${usage.plan}`);
|
|
8170
|
-
console.log(`Usage: ${usage.commit_count}/${usage.limit} commits this period`);
|
|
8171
|
-
console.log(`Remaining: ${usage.remaining}`);
|
|
8172
|
-
} else {
|
|
8173
|
-
console.log("Usage: (unable to fetch)");
|
|
8174
|
-
}
|
|
8175
|
-
}
|
|
8176
|
-
var init_status = __esm({
|
|
8177
|
-
"src/commands/status.ts"() {
|
|
8178
|
-
"use strict";
|
|
8179
|
-
init_config();
|
|
8180
|
-
init_api();
|
|
8181
|
-
}
|
|
8182
|
-
});
|
|
8183
|
-
|
|
8184
|
-
// src/commands/pr.ts
|
|
8185
|
-
var pr_exports = {};
|
|
8186
|
-
__export(pr_exports, {
|
|
8187
|
-
pr: () => pr
|
|
8188
|
-
});
|
|
8189
|
-
function findPullRequestTemplate(gitRoot) {
|
|
8190
|
-
const fileCandidates = [
|
|
8191
|
-
(0, import_path5.join)(gitRoot, ".github", "pull_request_template.md"),
|
|
8192
|
-
(0, import_path5.join)(gitRoot, ".github", "PULL_REQUEST_TEMPLATE.md"),
|
|
8193
|
-
(0, import_path5.join)(gitRoot, "pull_request_template.md")
|
|
8194
|
-
];
|
|
8195
|
-
for (const p of fileCandidates) {
|
|
8196
|
-
try {
|
|
8197
|
-
if ((0, import_fs5.existsSync)(p) && (0, import_fs5.statSync)(p).isFile()) {
|
|
8198
|
-
return { path: p, content: (0, import_fs5.readFileSync)(p, "utf-8") };
|
|
8199
|
-
}
|
|
8200
|
-
} catch {
|
|
8201
|
-
}
|
|
8202
|
-
}
|
|
8203
|
-
const multiDir = (0, import_path5.join)(gitRoot, ".github", "PULL_REQUEST_TEMPLATE");
|
|
8204
|
-
try {
|
|
8205
|
-
if ((0, import_fs5.existsSync)(multiDir) && (0, import_fs5.statSync)(multiDir).isDirectory()) {
|
|
8206
|
-
const names = (0, import_fs5.readdirSync)(multiDir).filter((f) => f.toLowerCase().endsWith(".md")).sort();
|
|
8207
|
-
if (names.length > 0) {
|
|
8208
|
-
const p = (0, import_path5.join)(multiDir, names[0]);
|
|
8209
|
-
if ((0, import_fs5.statSync)(p).isFile()) {
|
|
8210
|
-
return { path: p, content: (0, import_fs5.readFileSync)(p, "utf-8") };
|
|
8211
|
-
}
|
|
8212
|
-
}
|
|
8213
|
-
}
|
|
8214
|
-
} catch {
|
|
8215
|
-
}
|
|
8216
|
-
return void 0;
|
|
8217
|
-
}
|
|
8218
|
-
async function pr(options) {
|
|
8219
|
-
const base = options.base ?? "main";
|
|
8220
|
-
const commits = getBranchCommits(base);
|
|
8221
|
-
const diffStat = getDiffStat(base);
|
|
8222
|
-
const gitRoot = getGitRoot();
|
|
8223
|
-
const templateHit = findPullRequestTemplate(gitRoot);
|
|
8224
|
-
let prTemplate;
|
|
8225
|
-
if (templateHit) {
|
|
8226
|
-
prTemplate = templateHit.content.substring(0, 16 * 1024);
|
|
8227
|
-
console.error(`[qc] Using PR template from ${(0, import_path5.relative)(gitRoot, templateHit.path)}`);
|
|
8228
|
-
}
|
|
8229
|
-
const currentBranch = getCurrentBranch().slice(0, MAX_PR_CURRENT_BRANCH_CHARS);
|
|
8230
|
-
if (commits.length === 0) {
|
|
8231
|
-
console.error(`No commits found on this branch vs ${base}`);
|
|
8232
|
-
process.exit(1);
|
|
8187
|
+
const currentBranch = getCurrentBranch().slice(0, MAX_PR_CURRENT_BRANCH_CHARS);
|
|
8188
|
+
if (commits.length === 0) {
|
|
8189
|
+
console.error(`No commits found on this branch vs ${base}`);
|
|
8190
|
+
process.exit(1);
|
|
8233
8191
|
}
|
|
8234
8192
|
const commitlintRules = await detectCommitlintRules();
|
|
8235
8193
|
console.error(`Generating PR description from ${commits.length} commits...`);
|
|
@@ -8260,7 +8218,7 @@ Title: ${trimmedTitle}
|
|
|
8260
8218
|
if (options.create) {
|
|
8261
8219
|
try {
|
|
8262
8220
|
const prTitle = trimmedTitle || result.message.split("\n").find((l) => l.trim()) || result.message.substring(0, 72).trim();
|
|
8263
|
-
(0,
|
|
8221
|
+
(0, import_child_process4.execFileSync)("gh", ["pr", "create", "--title", prTitle, "--body", result.message], {
|
|
8264
8222
|
stdio: "inherit"
|
|
8265
8223
|
});
|
|
8266
8224
|
} catch {
|
|
@@ -8269,13 +8227,13 @@ Title: ${trimmedTitle}
|
|
|
8269
8227
|
}
|
|
8270
8228
|
}
|
|
8271
8229
|
}
|
|
8272
|
-
var
|
|
8230
|
+
var import_child_process4, import_fs4, import_path4;
|
|
8273
8231
|
var init_pr = __esm({
|
|
8274
8232
|
"src/commands/pr.ts"() {
|
|
8275
8233
|
"use strict";
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8234
|
+
import_child_process4 = require("child_process");
|
|
8235
|
+
import_fs4 = require("fs");
|
|
8236
|
+
import_path4 = require("path");
|
|
8279
8237
|
init_dist();
|
|
8280
8238
|
init_config();
|
|
8281
8239
|
init_api();
|
|
@@ -8336,21 +8294,21 @@ async function changelog(options) {
|
|
|
8336
8294
|
`;
|
|
8337
8295
|
const changelogEntry = header + result.message;
|
|
8338
8296
|
if (options.write) {
|
|
8339
|
-
const path = (0,
|
|
8340
|
-
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") : "";
|
|
8341
8299
|
const newContent = changelogEntry + (existing ? "\n\n" + existing : "");
|
|
8342
|
-
(0,
|
|
8300
|
+
(0, import_fs5.writeFileSync)(path, newContent);
|
|
8343
8301
|
console.error(`Wrote to ${path}`);
|
|
8344
8302
|
} else {
|
|
8345
8303
|
console.log(changelogEntry);
|
|
8346
8304
|
}
|
|
8347
8305
|
}
|
|
8348
|
-
var
|
|
8306
|
+
var import_fs5, import_path5, CONVENTIONAL_TYPE_RE;
|
|
8349
8307
|
var init_changelog = __esm({
|
|
8350
8308
|
"src/commands/changelog.ts"() {
|
|
8351
8309
|
"use strict";
|
|
8352
|
-
|
|
8353
|
-
|
|
8310
|
+
import_fs5 = require("fs");
|
|
8311
|
+
import_path5 = require("path");
|
|
8354
8312
|
init_config();
|
|
8355
8313
|
init_api();
|
|
8356
8314
|
init_git();
|
|
@@ -8358,6 +8316,146 @@ var init_changelog = __esm({
|
|
|
8358
8316
|
}
|
|
8359
8317
|
});
|
|
8360
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
|
+
|
|
8361
8459
|
// src/commands/changeset.ts
|
|
8362
8460
|
var changeset_exports = {};
|
|
8363
8461
|
__export(changeset_exports, {
|
|
@@ -8977,23 +9075,411 @@ async function upgrade() {
|
|
|
8977
9075
|
Opening ${BILLING_URL}
|
|
8978
9076
|
`);
|
|
8979
9077
|
try {
|
|
8980
|
-
const { execFileSync: execFileSync7 } = await import("child_process");
|
|
8981
|
-
if (process.platform === "darwin") {
|
|
8982
|
-
execFileSync7("open", [BILLING_URL]);
|
|
8983
|
-
} else if (process.platform === "linux") {
|
|
8984
|
-
execFileSync7("xdg-open", [BILLING_URL]);
|
|
8985
|
-
} else if (process.platform === "win32") {
|
|
8986
|
-
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" };
|
|
8987
9439
|
}
|
|
8988
|
-
|
|
8989
|
-
|
|
9440
|
+
return { action: "commit" };
|
|
9441
|
+
} finally {
|
|
9442
|
+
rl.close();
|
|
8990
9443
|
}
|
|
8991
9444
|
}
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
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"() {
|
|
8995
9481
|
"use strict";
|
|
8996
|
-
|
|
9482
|
+
import_promises = __toESM(require("node:readline/promises"));
|
|
8997
9483
|
}
|
|
8998
9484
|
});
|
|
8999
9485
|
|
|
@@ -9048,7 +9534,7 @@ function getLocalProviderConfig() {
|
|
|
9048
9534
|
if (provider === "openrouter" && !apiKey) return null;
|
|
9049
9535
|
return { provider, baseUrl, model, apiKey };
|
|
9050
9536
|
}
|
|
9051
|
-
function buildUserPrompt(changes, diff, rules) {
|
|
9537
|
+
function buildUserPrompt(changes, diff, rules, recentCommits, hints) {
|
|
9052
9538
|
let prompt2 = `Generate a commit message for these changes:
|
|
9053
9539
|
|
|
9054
9540
|
## File changes:
|
|
@@ -9062,6 +9548,23 @@ ${diff}
|
|
|
9062
9548
|
</diff>
|
|
9063
9549
|
|
|
9064
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
|
+
}
|
|
9065
9568
|
if (rules && Object.keys(rules).length > 0) {
|
|
9066
9569
|
prompt2 += `Rules: ${JSON.stringify(rules)}
|
|
9067
9570
|
|
|
@@ -9072,7 +9575,7 @@ ${diff}
|
|
|
9072
9575
|
- Response should be the commit message only, no explanations`;
|
|
9073
9576
|
return prompt2;
|
|
9074
9577
|
}
|
|
9075
|
-
function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiKey, rules) {
|
|
9578
|
+
function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiKey, rules, recentCommits, hints) {
|
|
9076
9579
|
const headers = {
|
|
9077
9580
|
"Content-Type": "application/json"
|
|
9078
9581
|
};
|
|
@@ -9124,10 +9627,18 @@ function buildRequest(provider, baseUrl, userContent, diff, changes, model, apiK
|
|
|
9124
9627
|
]
|
|
9125
9628
|
};
|
|
9126
9629
|
return { url, body, headers };
|
|
9127
|
-
case "cloudflare":
|
|
9630
|
+
case "cloudflare": {
|
|
9128
9631
|
url = `${baseUrl.replace(/\/$/, "")}/commit`;
|
|
9129
|
-
|
|
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;
|
|
9130
9640
|
return { url, body, headers: { "Content-Type": "application/json" } };
|
|
9641
|
+
}
|
|
9131
9642
|
default:
|
|
9132
9643
|
throw new Error(`Unknown provider: ${provider}`);
|
|
9133
9644
|
}
|
|
@@ -9149,7 +9660,9 @@ function parseResponse(provider, data) {
|
|
|
9149
9660
|
return "";
|
|
9150
9661
|
}
|
|
9151
9662
|
}
|
|
9152
|
-
async function runLocalCommit(
|
|
9663
|
+
async function runLocalCommit(args) {
|
|
9664
|
+
const silent = !!(args.hookMode || args.quiet);
|
|
9665
|
+
const log = silent ? createSilentLog() : getUI().log;
|
|
9153
9666
|
if (!isGitRepo()) {
|
|
9154
9667
|
throw new Error("Not a git repository.");
|
|
9155
9668
|
}
|
|
@@ -9163,11 +9676,19 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9163
9676
|
);
|
|
9164
9677
|
}
|
|
9165
9678
|
const config2 = getConfig();
|
|
9166
|
-
const excludes = config2.excludes ?? [];
|
|
9167
|
-
|
|
9679
|
+
const excludes = [...config2.excludes ?? [], ...args.exclude];
|
|
9680
|
+
let diff = getStagedDiff(excludes);
|
|
9168
9681
|
const changes = getStagedFiles();
|
|
9169
|
-
|
|
9170
|
-
|
|
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 ?? {} };
|
|
9171
9692
|
const workspace = detectWorkspace();
|
|
9172
9693
|
if (workspace) {
|
|
9173
9694
|
const stagedFiles = changes.trim().split("\n").filter(Boolean);
|
|
@@ -9177,7 +9698,20 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9177
9698
|
rules = { ...rules, scopes };
|
|
9178
9699
|
}
|
|
9179
9700
|
}
|
|
9180
|
-
|
|
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
|
+
);
|
|
9181
9715
|
const { url, body, headers } = buildRequest(
|
|
9182
9716
|
local.provider,
|
|
9183
9717
|
local.baseUrl,
|
|
@@ -9186,18 +9720,29 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9186
9720
|
changes,
|
|
9187
9721
|
model,
|
|
9188
9722
|
local.apiKey,
|
|
9189
|
-
rules
|
|
9723
|
+
rules,
|
|
9724
|
+
recentCommits,
|
|
9725
|
+
generationHints
|
|
9190
9726
|
);
|
|
9191
9727
|
if (!url || url.includes("YOUR-WORKER")) {
|
|
9192
9728
|
throw new Error(
|
|
9193
9729
|
"Cloudflare provider requires api_url. Run: qc config set api_url https://your-worker.workers.dev"
|
|
9194
9730
|
);
|
|
9195
9731
|
}
|
|
9196
|
-
const
|
|
9197
|
-
|
|
9198
|
-
|
|
9199
|
-
|
|
9200
|
-
|
|
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;
|
|
9201
9746
|
if (!res.ok) {
|
|
9202
9747
|
const text = await res.text();
|
|
9203
9748
|
throw new Error(`Provider error (${res.status}): ${text}`);
|
|
@@ -9208,12 +9753,37 @@ async function runLocalCommit(messageOnly, push, modelFlag) {
|
|
|
9208
9753
|
if (!message) {
|
|
9209
9754
|
throw new Error("Failed to generate commit message.");
|
|
9210
9755
|
}
|
|
9211
|
-
|
|
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) {
|
|
9212
9770
|
console.log(message);
|
|
9213
9771
|
return;
|
|
9214
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
|
+
}
|
|
9215
9785
|
gitCommit(message);
|
|
9216
|
-
if (push) {
|
|
9786
|
+
if (args.push) {
|
|
9217
9787
|
gitPush();
|
|
9218
9788
|
}
|
|
9219
9789
|
}
|
|
@@ -9228,6 +9798,10 @@ var init_local = __esm({
|
|
|
9228
9798
|
init_dist();
|
|
9229
9799
|
init_git();
|
|
9230
9800
|
init_monorepo();
|
|
9801
|
+
init_commitlint();
|
|
9802
|
+
init_smart_diff();
|
|
9803
|
+
init_ui();
|
|
9804
|
+
init_commit_helpers();
|
|
9231
9805
|
CONFIG_PATH2 = (0, import_path10.join)((0, import_os4.homedir)(), CONFIG_DIR);
|
|
9232
9806
|
PROVIDER_URLS = {
|
|
9233
9807
|
ollama: "http://localhost:11434",
|
|
@@ -9246,166 +9820,55 @@ var init_local = __esm({
|
|
|
9246
9820
|
}
|
|
9247
9821
|
});
|
|
9248
9822
|
|
|
9249
|
-
// src/
|
|
9250
|
-
|
|
9251
|
-
|
|
9252
|
-
|
|
9253
|
-
|
|
9254
|
-
|
|
9255
|
-
|
|
9256
|
-
|
|
9257
|
-
|
|
9258
|
-
qc Generate commit message and commit (default)
|
|
9259
|
-
qc --message-only Generate message only, print to stdout
|
|
9260
|
-
qc --push Commit and push to origin
|
|
9261
|
-
qc pr Generate PR description from branch commits
|
|
9262
|
-
qc changelog Generate changelog from commits since last tag
|
|
9263
|
-
qc changeset Automate pnpm changeset with AI
|
|
9264
|
-
qc init Install prepare-commit-msg hook for auto-generation
|
|
9265
|
-
qc login Sign in via browser
|
|
9266
|
-
qc logout Clear local credentials
|
|
9267
|
-
qc status Show auth, plan, usage
|
|
9268
|
-
qc team Team management (info, rules, invite)
|
|
9269
|
-
|
|
9270
|
-
Options:
|
|
9271
|
-
-h, --help Show this help
|
|
9272
|
-
-a, --all Stage all tracked changes before generating
|
|
9273
|
-
-m, --message-only Generate message only
|
|
9274
|
-
-p, --push Commit and push after generating
|
|
9275
|
-
--api-key <key> Use this API key (overrides credentials file)
|
|
9276
|
-
--base <branch> Base branch for qc pr, qc changeset (default: main)
|
|
9277
|
-
--create Create PR with gh CLI after qc pr
|
|
9278
|
-
--from <ref> Start ref for qc changelog (default: latest tag)
|
|
9279
|
-
--to <ref> End ref for qc changelog (default: HEAD)
|
|
9280
|
-
--write Prepend changelog to CHANGELOG.md
|
|
9281
|
-
--version <ver> Version label for changelog header (default: derived from --to or "<from>-next")
|
|
9282
|
-
--uninstall Remove Quikcommit hook (qc init --uninstall)
|
|
9283
|
-
--model <id> Use specific model (e.g. qwen25-coder-32b, llama-3.3-70b)
|
|
9284
|
-
|
|
9285
|
-
Commands:
|
|
9286
|
-
qc config Show current config
|
|
9287
|
-
qc config set <k> <v> Set config (model, api_url)
|
|
9288
|
-
qc config reset Reset to defaults
|
|
9289
|
-
qc upgrade Open billing page in browser
|
|
9290
|
-
`;
|
|
9291
|
-
function parseArgs(args) {
|
|
9292
|
-
let command = "commit";
|
|
9293
|
-
let all = false;
|
|
9294
|
-
let messageOnly = false;
|
|
9295
|
-
let push = false;
|
|
9296
|
-
let apiKey;
|
|
9297
|
-
let model;
|
|
9298
|
-
let local = false;
|
|
9299
|
-
let base;
|
|
9300
|
-
let create = false;
|
|
9301
|
-
let from;
|
|
9302
|
-
let to;
|
|
9303
|
-
let write = false;
|
|
9304
|
-
let version;
|
|
9305
|
-
let uninstall = false;
|
|
9306
|
-
let hookMode = false;
|
|
9307
|
-
for (let i = 0; i < args.length; i++) {
|
|
9308
|
-
const arg = args[i];
|
|
9309
|
-
if (arg === "-h" || arg === "--help") {
|
|
9310
|
-
command = "help";
|
|
9311
|
-
} else if (arg === "-a" || arg === "--all") {
|
|
9312
|
-
all = true;
|
|
9313
|
-
} else if (arg === "-m" || arg === "--message-only") {
|
|
9314
|
-
messageOnly = true;
|
|
9315
|
-
} else if (arg === "-p" || arg === "--push") {
|
|
9316
|
-
push = true;
|
|
9317
|
-
} else if (arg === "--api-key" && i + 1 < args.length) {
|
|
9318
|
-
apiKey = args[++i];
|
|
9319
|
-
} else if (arg === "--base" && i + 1 < args.length) {
|
|
9320
|
-
base = args[++i];
|
|
9321
|
-
} else if (arg === "--create") {
|
|
9322
|
-
create = true;
|
|
9323
|
-
} else if (arg === "--from" && i + 1 < args.length) {
|
|
9324
|
-
from = args[++i];
|
|
9325
|
-
} else if (arg === "--to" && i + 1 < args.length) {
|
|
9326
|
-
to = args[++i];
|
|
9327
|
-
} else if (arg === "--write") {
|
|
9328
|
-
write = true;
|
|
9329
|
-
} else if (arg === "--version" && i + 1 < args.length) {
|
|
9330
|
-
version = args[++i];
|
|
9331
|
-
} else if (arg === "--uninstall") {
|
|
9332
|
-
uninstall = true;
|
|
9333
|
-
} else if (arg === "--hook-mode") {
|
|
9334
|
-
hookMode = true;
|
|
9335
|
-
} else if (arg === "login") {
|
|
9336
|
-
command = "login";
|
|
9337
|
-
} else if (arg === "logout") {
|
|
9338
|
-
command = "logout";
|
|
9339
|
-
} else if (arg === "status") {
|
|
9340
|
-
command = "status";
|
|
9341
|
-
} else if (arg === "pr") {
|
|
9342
|
-
command = "pr";
|
|
9343
|
-
} else if (arg === "changelog") {
|
|
9344
|
-
command = "changelog";
|
|
9345
|
-
} else if (arg === "init") {
|
|
9346
|
-
command = "init";
|
|
9347
|
-
} else if (arg === "team") {
|
|
9348
|
-
command = "team";
|
|
9349
|
-
} else if (arg === "config") {
|
|
9350
|
-
command = "config";
|
|
9351
|
-
} else if (arg === "upgrade") {
|
|
9352
|
-
command = "upgrade";
|
|
9353
|
-
} else if (arg === "changeset") {
|
|
9354
|
-
command = "changeset";
|
|
9355
|
-
} else if (arg === "--model" && i + 1 < args.length) {
|
|
9356
|
-
model = args[++i];
|
|
9357
|
-
} else if (arg === "--local" || arg === "--use-ollama" || arg === "--use-lmstudio" || arg === "--use-openrouter" || arg === "--use-cloudflare") {
|
|
9358
|
-
local = true;
|
|
9359
|
-
if (arg === "--use-ollama") {
|
|
9360
|
-
saveConfig({ ...getConfig(), provider: "ollama", apiUrl: "http://localhost:11434", model: "codellama" });
|
|
9361
|
-
} else if (arg === "--use-lmstudio") {
|
|
9362
|
-
saveConfig({ ...getConfig(), provider: "lmstudio", apiUrl: "http://localhost:1234/v1", model: "default" });
|
|
9363
|
-
} else if (arg === "--use-openrouter") {
|
|
9364
|
-
saveConfig({ ...getConfig(), provider: "openrouter", apiUrl: "https://openrouter.ai/api/v1", model: "google/gemini-flash-1.5-8b" });
|
|
9365
|
-
} else if (arg === "--use-cloudflare") {
|
|
9366
|
-
saveConfig({
|
|
9367
|
-
...getConfig(),
|
|
9368
|
-
provider: "cloudflare",
|
|
9369
|
-
apiUrl: "https://YOUR-WORKER.workers.dev",
|
|
9370
|
-
model: "@cf/qwen/qwen2.5-coder-32b-instruct"
|
|
9371
|
-
});
|
|
9372
|
-
console.error(
|
|
9373
|
-
"[qc] Cloudflare provider set. Run: qc config set api_url https://your-worker.workers.dev"
|
|
9374
|
-
);
|
|
9375
|
-
}
|
|
9376
|
-
}
|
|
9377
|
-
}
|
|
9378
|
-
return { command, all, messageOnly, push, apiKey, base, create, from, to, write, version, uninstall, hookMode, model, local };
|
|
9379
|
-
}
|
|
9380
|
-
async function runCommit(messageOnly, push, apiKeyFlag, hookMode = false, modelFlag, stageAll_) {
|
|
9381
|
-
const log = hookMode ? () => {
|
|
9382
|
-
} : (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;
|
|
9383
9832
|
if (!isGitRepo()) {
|
|
9384
|
-
log("
|
|
9833
|
+
log.error("Not a git repository.");
|
|
9385
9834
|
process.exit(1);
|
|
9386
9835
|
}
|
|
9387
9836
|
const config2 = getConfig();
|
|
9388
|
-
if (
|
|
9837
|
+
if (all || config2.autoStage) {
|
|
9389
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}`);
|
|
9390
9843
|
}
|
|
9391
9844
|
if (!hasStagedChanges()) {
|
|
9392
9845
|
const unstaged = getUnstagedFiles();
|
|
9393
9846
|
if (unstaged.length > 0) {
|
|
9394
|
-
log("
|
|
9847
|
+
log.error("No staged changes. Use `qc -a` to stage tracked files, or `git add` manually.");
|
|
9395
9848
|
} else {
|
|
9396
|
-
log("
|
|
9849
|
+
log.error("No changes to commit.");
|
|
9397
9850
|
}
|
|
9398
9851
|
process.exit(1);
|
|
9399
9852
|
}
|
|
9400
9853
|
const apiKey = apiKeyFlag ?? getApiKey();
|
|
9401
9854
|
if (!apiKey) {
|
|
9402
|
-
log("
|
|
9855
|
+
log.error("Not authenticated. Run `qc login` first.");
|
|
9403
9856
|
process.exit(1);
|
|
9404
9857
|
}
|
|
9405
9858
|
const model = modelFlag ?? config2.model;
|
|
9406
|
-
const excludes = config2.excludes ?? [];
|
|
9859
|
+
const excludes = [...config2.excludes ?? [], ...args.exclude];
|
|
9407
9860
|
const diff = getStagedDiff(excludes);
|
|
9408
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
|
+
}
|
|
9409
9872
|
const commitlintRules = await detectCommitlintRules();
|
|
9410
9873
|
let rules = { ...commitlintRules, ...config2.rules ?? {} };
|
|
9411
9874
|
const workspace = detectWorkspace();
|
|
@@ -9422,7 +9885,7 @@ async function runCommit(messageOnly, push, apiKeyFlag, hookMode = false, modelF
|
|
|
9422
9885
|
try {
|
|
9423
9886
|
const teamRules = await client.getTeamRules();
|
|
9424
9887
|
if (teamRules && Object.keys(teamRules).length > 0) {
|
|
9425
|
-
log("
|
|
9888
|
+
log.step("using team rules from org");
|
|
9426
9889
|
rules = { ...rules, ...teamRules };
|
|
9427
9890
|
if (monorepoScopes && teamRules.scopes && teamRules.scopes.length > 0) {
|
|
9428
9891
|
const allowed = new Set(teamRules.scopes);
|
|
@@ -9432,24 +9895,393 @@ async function runCommit(messageOnly, push, apiKeyFlag, hookMode = false, modelF
|
|
|
9432
9895
|
}
|
|
9433
9896
|
} catch {
|
|
9434
9897
|
}
|
|
9435
|
-
|
|
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
|
+
}
|
|
9436
9935
|
if (messageOnly) {
|
|
9437
9936
|
console.log(message);
|
|
9438
9937
|
return;
|
|
9439
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
|
+
}
|
|
9440
9949
|
gitCommit(message);
|
|
9950
|
+
const hash = getCommitHash();
|
|
9951
|
+
const branch = getCurrentBranch();
|
|
9952
|
+
log.step(`[${branch} ${hash}] committed`);
|
|
9441
9953
|
if (push) {
|
|
9954
|
+
log.step(`pushing to origin/${branch}...`);
|
|
9442
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)");
|
|
9443
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;
|
|
9444
10243
|
}
|
|
9445
10244
|
async function main() {
|
|
9446
10245
|
const argv = process.argv.slice(2);
|
|
9447
|
-
|
|
9448
|
-
|
|
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;
|
|
9449
10254
|
if (command === "help") {
|
|
9450
10255
|
console.log(HELP);
|
|
9451
10256
|
return;
|
|
9452
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
|
+
}
|
|
9453
10285
|
if (command === "login") {
|
|
9454
10286
|
const { runLogin: runLogin2 } = await Promise.resolve().then(() => (init_login(), login_exports));
|
|
9455
10287
|
await runLogin2();
|
|
@@ -9500,14 +10332,12 @@ async function main() {
|
|
|
9500
10332
|
}
|
|
9501
10333
|
if (command === "team") {
|
|
9502
10334
|
const { team: team2 } = await Promise.resolve().then(() => (init_team(), team_exports));
|
|
9503
|
-
|
|
9504
|
-
await team2(positionals[0], positionals.slice(1));
|
|
10335
|
+
await team2(values.positionals[0], values.positionals.slice(1));
|
|
9505
10336
|
return;
|
|
9506
10337
|
}
|
|
9507
10338
|
if (command === "config") {
|
|
9508
10339
|
const { config: config2 } = await Promise.resolve().then(() => (init_config2(), config_exports));
|
|
9509
|
-
|
|
9510
|
-
config2(positionals);
|
|
10340
|
+
config2(values.positionals);
|
|
9511
10341
|
return;
|
|
9512
10342
|
}
|
|
9513
10343
|
if (command === "upgrade") {
|
|
@@ -9517,7 +10347,7 @@ async function main() {
|
|
|
9517
10347
|
}
|
|
9518
10348
|
if (values.local) {
|
|
9519
10349
|
const { runLocalCommit: runLocalCommit2 } = await Promise.resolve().then(() => (init_local(), local_exports));
|
|
9520
|
-
await runLocalCommit2(
|
|
10350
|
+
await runLocalCommit2(values);
|
|
9521
10351
|
return;
|
|
9522
10352
|
}
|
|
9523
10353
|
const apiKeyToUse = apiKey ?? getApiKey();
|
|
@@ -9525,11 +10355,12 @@ async function main() {
|
|
|
9525
10355
|
const { getLocalProviderConfig: getLocalProviderConfig2 } = await Promise.resolve().then(() => (init_local(), local_exports));
|
|
9526
10356
|
if (getLocalProviderConfig2()) {
|
|
9527
10357
|
const { runLocalCommit: runLocalCommit2 } = await Promise.resolve().then(() => (init_local(), local_exports));
|
|
9528
|
-
await runLocalCommit2(
|
|
10358
|
+
await runLocalCommit2(values);
|
|
9529
10359
|
return;
|
|
9530
10360
|
}
|
|
9531
10361
|
}
|
|
9532
|
-
|
|
10362
|
+
const { runCommit: runCommit2 } = await Promise.resolve().then(() => (init_commit(), commit_exports));
|
|
10363
|
+
await runCommit2(values);
|
|
9533
10364
|
}
|
|
9534
10365
|
main().catch((err) => {
|
|
9535
10366
|
const args = process.argv.slice(2);
|
|
@@ -9539,3 +10370,7 @@ main().catch((err) => {
|
|
|
9539
10370
|
}
|
|
9540
10371
|
process.exit(1);
|
|
9541
10372
|
});
|
|
10373
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
10374
|
+
0 && (module.exports = {
|
|
10375
|
+
parseArgs
|
|
10376
|
+
});
|