contribute-now 0.8.0-dev.7eaa951 → 0.8.0-dev.a835394
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -0
- package/dist/cli.js +1417 -455
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -5644,6 +5644,242 @@ var init_dist = __esm(() => {
|
|
|
5644
5644
|
init_types2();
|
|
5645
5645
|
});
|
|
5646
5646
|
|
|
5647
|
+
// src/utils/gh.ts
|
|
5648
|
+
var exports_gh = {};
|
|
5649
|
+
__export(exports_gh, {
|
|
5650
|
+
isRepoFork: () => isRepoFork,
|
|
5651
|
+
getRepoLabels: () => getRepoLabels,
|
|
5652
|
+
getPRForBranch: () => getPRForBranch,
|
|
5653
|
+
getPRContent: () => getPRContent,
|
|
5654
|
+
getMergedPRForBranch: () => getMergedPRForBranch,
|
|
5655
|
+
getIssueContent: () => getIssueContent,
|
|
5656
|
+
getCurrentRepoInfo: () => getCurrentRepoInfo,
|
|
5657
|
+
createPRFill: () => createPRFill,
|
|
5658
|
+
createPR: () => createPR,
|
|
5659
|
+
checkRepoPermissions: () => checkRepoPermissions,
|
|
5660
|
+
checkGhInstalled: () => checkGhInstalled,
|
|
5661
|
+
checkGhAuth: () => checkGhAuth,
|
|
5662
|
+
addLabelsToPR: () => addLabelsToPR,
|
|
5663
|
+
addLabelsToIssue: () => addLabelsToIssue
|
|
5664
|
+
});
|
|
5665
|
+
import { execFile as execFileCb2 } from "child_process";
|
|
5666
|
+
function run2(args) {
|
|
5667
|
+
return new Promise((resolve5) => {
|
|
5668
|
+
execFileCb2("gh", args, (error2, stdout2, stderr) => {
|
|
5669
|
+
resolve5({
|
|
5670
|
+
exitCode: error2 ? error2.code === "ENOENT" ? 127 : error2.status ?? 1 : 0,
|
|
5671
|
+
stdout: stdout2 ?? "",
|
|
5672
|
+
stderr: stderr ?? ""
|
|
5673
|
+
});
|
|
5674
|
+
});
|
|
5675
|
+
});
|
|
5676
|
+
}
|
|
5677
|
+
async function checkGhInstalled() {
|
|
5678
|
+
try {
|
|
5679
|
+
const { exitCode } = await run2(["--version"]);
|
|
5680
|
+
return exitCode === 0;
|
|
5681
|
+
} catch {
|
|
5682
|
+
return false;
|
|
5683
|
+
}
|
|
5684
|
+
}
|
|
5685
|
+
async function checkGhAuth() {
|
|
5686
|
+
try {
|
|
5687
|
+
const { exitCode } = await run2(["auth", "status"]);
|
|
5688
|
+
return exitCode === 0;
|
|
5689
|
+
} catch {
|
|
5690
|
+
return false;
|
|
5691
|
+
}
|
|
5692
|
+
}
|
|
5693
|
+
async function checkRepoPermissions(owner, repo) {
|
|
5694
|
+
if (!SAFE_SLUG.test(owner) || !SAFE_SLUG.test(repo))
|
|
5695
|
+
return null;
|
|
5696
|
+
const { exitCode, stdout: stdout2 } = await run2(["api", `repos/${owner}/${repo}`, "--jq", ".permissions"]);
|
|
5697
|
+
if (exitCode !== 0)
|
|
5698
|
+
return null;
|
|
5699
|
+
try {
|
|
5700
|
+
return JSON.parse(stdout2.trim());
|
|
5701
|
+
} catch {
|
|
5702
|
+
return null;
|
|
5703
|
+
}
|
|
5704
|
+
}
|
|
5705
|
+
async function isRepoFork() {
|
|
5706
|
+
const { exitCode, stdout: stdout2 } = await run2(["repo", "view", "--json", "isFork", "-q", ".isFork"]);
|
|
5707
|
+
if (exitCode !== 0)
|
|
5708
|
+
return null;
|
|
5709
|
+
const val = stdout2.trim();
|
|
5710
|
+
if (val === "true")
|
|
5711
|
+
return true;
|
|
5712
|
+
if (val === "false")
|
|
5713
|
+
return false;
|
|
5714
|
+
return null;
|
|
5715
|
+
}
|
|
5716
|
+
async function getCurrentRepoInfo() {
|
|
5717
|
+
const { exitCode, stdout: stdout2 } = await run2([
|
|
5718
|
+
"repo",
|
|
5719
|
+
"view",
|
|
5720
|
+
"--json",
|
|
5721
|
+
"nameWithOwner",
|
|
5722
|
+
"-q",
|
|
5723
|
+
".nameWithOwner"
|
|
5724
|
+
]);
|
|
5725
|
+
if (exitCode !== 0)
|
|
5726
|
+
return null;
|
|
5727
|
+
const nameWithOwner = stdout2.trim();
|
|
5728
|
+
if (!nameWithOwner)
|
|
5729
|
+
return null;
|
|
5730
|
+
const [owner, repo] = nameWithOwner.split("/");
|
|
5731
|
+
if (!owner || !repo)
|
|
5732
|
+
return null;
|
|
5733
|
+
return { owner, repo };
|
|
5734
|
+
}
|
|
5735
|
+
async function createPR(options) {
|
|
5736
|
+
const args = [
|
|
5737
|
+
"pr",
|
|
5738
|
+
"create",
|
|
5739
|
+
"--base",
|
|
5740
|
+
options.base,
|
|
5741
|
+
"--title",
|
|
5742
|
+
options.title,
|
|
5743
|
+
"--body",
|
|
5744
|
+
options.body
|
|
5745
|
+
];
|
|
5746
|
+
if (options.draft)
|
|
5747
|
+
args.push("--draft");
|
|
5748
|
+
return run2(args);
|
|
5749
|
+
}
|
|
5750
|
+
async function createPRFill(base, draft) {
|
|
5751
|
+
const args = ["pr", "create", "--base", base, "--fill"];
|
|
5752
|
+
if (draft)
|
|
5753
|
+
args.push("--draft");
|
|
5754
|
+
return run2(args);
|
|
5755
|
+
}
|
|
5756
|
+
async function getPRForBranch(headBranch) {
|
|
5757
|
+
const { exitCode, stdout: stdout2 } = await run2([
|
|
5758
|
+
"pr",
|
|
5759
|
+
"list",
|
|
5760
|
+
"--head",
|
|
5761
|
+
headBranch,
|
|
5762
|
+
"--state",
|
|
5763
|
+
"open",
|
|
5764
|
+
"--json",
|
|
5765
|
+
"number,url,title,state",
|
|
5766
|
+
"--limit",
|
|
5767
|
+
"1"
|
|
5768
|
+
]);
|
|
5769
|
+
if (exitCode !== 0)
|
|
5770
|
+
return null;
|
|
5771
|
+
try {
|
|
5772
|
+
const prs = JSON.parse(stdout2.trim());
|
|
5773
|
+
return prs.length > 0 ? prs[0] : null;
|
|
5774
|
+
} catch {
|
|
5775
|
+
return null;
|
|
5776
|
+
}
|
|
5777
|
+
}
|
|
5778
|
+
async function getMergedPRForBranch(headBranch) {
|
|
5779
|
+
const { exitCode, stdout: stdout2 } = await run2([
|
|
5780
|
+
"pr",
|
|
5781
|
+
"list",
|
|
5782
|
+
"--head",
|
|
5783
|
+
headBranch,
|
|
5784
|
+
"--state",
|
|
5785
|
+
"merged",
|
|
5786
|
+
"--json",
|
|
5787
|
+
"number,url,title,state",
|
|
5788
|
+
"--limit",
|
|
5789
|
+
"1"
|
|
5790
|
+
]);
|
|
5791
|
+
if (exitCode !== 0)
|
|
5792
|
+
return null;
|
|
5793
|
+
try {
|
|
5794
|
+
const prs = JSON.parse(stdout2.trim());
|
|
5795
|
+
return prs.length > 0 ? prs[0] : null;
|
|
5796
|
+
} catch {
|
|
5797
|
+
return null;
|
|
5798
|
+
}
|
|
5799
|
+
}
|
|
5800
|
+
async function getRepoLabels() {
|
|
5801
|
+
const PAGE_SIZE = 100;
|
|
5802
|
+
const allLabels = [];
|
|
5803
|
+
let page = 1;
|
|
5804
|
+
while (true) {
|
|
5805
|
+
const { exitCode, stdout: stdout2 } = await run2([
|
|
5806
|
+
"label",
|
|
5807
|
+
"list",
|
|
5808
|
+
"--json",
|
|
5809
|
+
"name,description,color",
|
|
5810
|
+
"--limit",
|
|
5811
|
+
String(PAGE_SIZE),
|
|
5812
|
+
"--page",
|
|
5813
|
+
String(page)
|
|
5814
|
+
]);
|
|
5815
|
+
if (exitCode !== 0)
|
|
5816
|
+
break;
|
|
5817
|
+
let batch = [];
|
|
5818
|
+
try {
|
|
5819
|
+
batch = JSON.parse(stdout2.trim());
|
|
5820
|
+
} catch {
|
|
5821
|
+
break;
|
|
5822
|
+
}
|
|
5823
|
+
if (!Array.isArray(batch) || batch.length === 0)
|
|
5824
|
+
break;
|
|
5825
|
+
for (const item of batch) {
|
|
5826
|
+
if (typeof item.name === "string" && item.name.trim().length > 0) {
|
|
5827
|
+
allLabels.push({
|
|
5828
|
+
name: item.name.trim(),
|
|
5829
|
+
description: typeof item.description === "string" ? item.description.trim() : "",
|
|
5830
|
+
color: typeof item.color === "string" ? item.color.trim().replace(/^#/, "") : ""
|
|
5831
|
+
});
|
|
5832
|
+
}
|
|
5833
|
+
}
|
|
5834
|
+
if (batch.length < PAGE_SIZE)
|
|
5835
|
+
break;
|
|
5836
|
+
page++;
|
|
5837
|
+
}
|
|
5838
|
+
return allLabels;
|
|
5839
|
+
}
|
|
5840
|
+
async function getIssueContent(issueNumber) {
|
|
5841
|
+
const { exitCode, stdout: stdout2 } = await run2([
|
|
5842
|
+
"issue",
|
|
5843
|
+
"view",
|
|
5844
|
+
String(issueNumber),
|
|
5845
|
+
"--json",
|
|
5846
|
+
"title,body"
|
|
5847
|
+
]);
|
|
5848
|
+
if (exitCode !== 0)
|
|
5849
|
+
return null;
|
|
5850
|
+
try {
|
|
5851
|
+
const parsed = JSON.parse(stdout2.trim());
|
|
5852
|
+
const title = typeof parsed.title === "string" ? parsed.title : "";
|
|
5853
|
+
const body = typeof parsed.body === "string" ? parsed.body : "";
|
|
5854
|
+
return { title, body };
|
|
5855
|
+
} catch {
|
|
5856
|
+
return null;
|
|
5857
|
+
}
|
|
5858
|
+
}
|
|
5859
|
+
async function getPRContent(prNumber) {
|
|
5860
|
+
const { exitCode, stdout: stdout2 } = await run2(["pr", "view", String(prNumber), "--json", "title,body"]);
|
|
5861
|
+
if (exitCode !== 0)
|
|
5862
|
+
return null;
|
|
5863
|
+
try {
|
|
5864
|
+
const parsed = JSON.parse(stdout2.trim());
|
|
5865
|
+
const title = typeof parsed.title === "string" ? parsed.title : "";
|
|
5866
|
+
const body = typeof parsed.body === "string" ? parsed.body : "";
|
|
5867
|
+
return { title, body };
|
|
5868
|
+
} catch {
|
|
5869
|
+
return null;
|
|
5870
|
+
}
|
|
5871
|
+
}
|
|
5872
|
+
async function addLabelsToIssue(issueNumber, labels) {
|
|
5873
|
+
return run2(["issue", "edit", String(issueNumber), "--add-label", labels.join(",")]);
|
|
5874
|
+
}
|
|
5875
|
+
async function addLabelsToPR(prNumber, labels) {
|
|
5876
|
+
return run2(["pr", "edit", String(prNumber), "--add-label", labels.join(",")]);
|
|
5877
|
+
}
|
|
5878
|
+
var SAFE_SLUG;
|
|
5879
|
+
var init_gh = __esm(() => {
|
|
5880
|
+
SAFE_SLUG = /^[\w.-]+$/;
|
|
5881
|
+
});
|
|
5882
|
+
|
|
5647
5883
|
// node_modules/consola/dist/core.mjs
|
|
5648
5884
|
var LogLevels = {
|
|
5649
5885
|
silent: Number.NEGATIVE_INFINITY,
|
|
@@ -7055,10 +7291,13 @@ import {
|
|
|
7055
7291
|
statSync,
|
|
7056
7292
|
writeFileSync
|
|
7057
7293
|
} from "fs";
|
|
7294
|
+
import { homedir } from "os";
|
|
7058
7295
|
import { dirname, join, resolve } from "path";
|
|
7059
7296
|
var CONFIG_FILENAME = ".contributerc.json";
|
|
7060
7297
|
var LOCAL_CONFIG_DIRNAME = "contribute-now";
|
|
7061
7298
|
var LOCAL_CONFIG_FILENAME = "config.json";
|
|
7299
|
+
var GLOBAL_CONFIG_DIRNAME = ".contribute-now";
|
|
7300
|
+
var GLOBAL_CONFIG_FILENAME = "config.json";
|
|
7062
7301
|
function findRepoRoot(cwd = process.cwd()) {
|
|
7063
7302
|
let current = resolve(cwd);
|
|
7064
7303
|
while (true) {
|
|
@@ -7148,13 +7387,44 @@ function parseConfigFile(path) {
|
|
|
7148
7387
|
aiHost: _aiHost,
|
|
7149
7388
|
...config
|
|
7150
7389
|
} = parsed;
|
|
7151
|
-
|
|
7390
|
+
const normalizedConfig = {
|
|
7152
7391
|
...config,
|
|
7153
|
-
aiEnabled: parsed.aiEnabled !== false,
|
|
7154
7392
|
aiProvider: parsed.aiProvider,
|
|
7155
7393
|
aiModel: parsed.aiModel?.trim() || undefined,
|
|
7156
7394
|
showTips: parsed.showTips !== false
|
|
7157
7395
|
};
|
|
7396
|
+
if (typeof parsed.aiEnabled === "boolean") {
|
|
7397
|
+
normalizedConfig.aiEnabled = parsed.aiEnabled;
|
|
7398
|
+
}
|
|
7399
|
+
return normalizedConfig;
|
|
7400
|
+
} catch {
|
|
7401
|
+
return null;
|
|
7402
|
+
}
|
|
7403
|
+
}
|
|
7404
|
+
function parseGlobalConfigFile(path) {
|
|
7405
|
+
try {
|
|
7406
|
+
const raw = readFileSync(path, "utf-8");
|
|
7407
|
+
const parsed = JSON.parse(raw);
|
|
7408
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
7409
|
+
return null;
|
|
7410
|
+
}
|
|
7411
|
+
if (parsed.aiProvider !== undefined && (typeof parsed.aiProvider !== "string" || !VALID_AI_PROVIDERS.includes(parsed.aiProvider))) {
|
|
7412
|
+
console.error(`Invalid aiProvider "${String(parsed.aiProvider)}" in ${GLOBAL_CONFIG_FILENAME}. Valid: ${VALID_AI_PROVIDERS.join(", ")}`);
|
|
7413
|
+
return null;
|
|
7414
|
+
}
|
|
7415
|
+
if (parsed.aiModel !== undefined && (typeof parsed.aiModel !== "string" || !parsed.aiModel.trim())) {
|
|
7416
|
+
console.error(`Invalid ${GLOBAL_CONFIG_FILENAME}: aiModel must be a non-empty string.`);
|
|
7417
|
+
return null;
|
|
7418
|
+
}
|
|
7419
|
+
if (parsed.aiEnabled !== undefined && typeof parsed.aiEnabled !== "boolean") {
|
|
7420
|
+
console.error(`Invalid ${GLOBAL_CONFIG_FILENAME}: aiEnabled must be a boolean when set.`);
|
|
7421
|
+
return null;
|
|
7422
|
+
}
|
|
7423
|
+
return {
|
|
7424
|
+
aiEnabled: parsed.aiEnabled,
|
|
7425
|
+
aiProvider: parsed.aiProvider,
|
|
7426
|
+
aiModel: parsed.aiModel?.trim() || undefined
|
|
7427
|
+
};
|
|
7158
7428
|
} catch {
|
|
7159
7429
|
return null;
|
|
7160
7430
|
}
|
|
@@ -7169,6 +7439,9 @@ function getConfigPath(cwd = process.cwd()) {
|
|
|
7169
7439
|
function getLegacyConfigPath(cwd = process.cwd()) {
|
|
7170
7440
|
return join(findRepoRoot(cwd) ?? cwd, CONFIG_FILENAME);
|
|
7171
7441
|
}
|
|
7442
|
+
function getGlobalConfigPath(baseDir = homedir()) {
|
|
7443
|
+
return join(baseDir, GLOBAL_CONFIG_DIRNAME, GLOBAL_CONFIG_FILENAME);
|
|
7444
|
+
}
|
|
7172
7445
|
function getLocalConfigPath(cwd = process.cwd()) {
|
|
7173
7446
|
const gitDir = resolveGitDir(cwd);
|
|
7174
7447
|
if (!gitDir) {
|
|
@@ -7203,12 +7476,25 @@ function getConfigLocationLabel(cwd = process.cwd()) {
|
|
|
7203
7476
|
function configExists(cwd = process.cwd()) {
|
|
7204
7477
|
return getConfigSource(cwd) !== null;
|
|
7205
7478
|
}
|
|
7479
|
+
function globalConfigExists(baseDir = homedir()) {
|
|
7480
|
+
return existsSync(getGlobalConfigPath(baseDir));
|
|
7481
|
+
}
|
|
7206
7482
|
var VALID_WORKFLOWS = ["clean-flow", "github-flow", "git-flow"];
|
|
7207
7483
|
var VALID_ROLES = ["maintainer", "contributor"];
|
|
7208
7484
|
var VALID_CONVENTIONS = ["conventional", "clean-commit", "none"];
|
|
7209
7485
|
var VALID_AI_PROVIDERS = ["copilot", "ollama-cloud", "openrouter"];
|
|
7210
|
-
function isAIEnabled(config, cliNoAI = false) {
|
|
7211
|
-
|
|
7486
|
+
function isAIEnabled(config, cliNoAI = false, globalConfig) {
|
|
7487
|
+
if (cliNoAI) {
|
|
7488
|
+
return false;
|
|
7489
|
+
}
|
|
7490
|
+
if (typeof config.aiEnabled === "boolean") {
|
|
7491
|
+
return config.aiEnabled;
|
|
7492
|
+
}
|
|
7493
|
+
const resolvedGlobalConfig = globalConfig === undefined ? readGlobalConfig() : globalConfig;
|
|
7494
|
+
if (typeof resolvedGlobalConfig?.aiEnabled === "boolean") {
|
|
7495
|
+
return resolvedGlobalConfig.aiEnabled;
|
|
7496
|
+
}
|
|
7497
|
+
return true;
|
|
7212
7498
|
}
|
|
7213
7499
|
function shouldShowTips(config) {
|
|
7214
7500
|
return config?.showTips !== false;
|
|
@@ -7222,6 +7508,13 @@ function readConfig(cwd = process.cwd()) {
|
|
|
7222
7508
|
return null;
|
|
7223
7509
|
return parseConfigFile(path);
|
|
7224
7510
|
}
|
|
7511
|
+
function readGlobalConfig(baseDir = homedir()) {
|
|
7512
|
+
const path = getGlobalConfigPath(baseDir);
|
|
7513
|
+
if (!existsSync(path)) {
|
|
7514
|
+
return null;
|
|
7515
|
+
}
|
|
7516
|
+
return parseGlobalConfigFile(path);
|
|
7517
|
+
}
|
|
7225
7518
|
function writeConfig(config, cwd = process.cwd()) {
|
|
7226
7519
|
const path = getConfigPath(cwd);
|
|
7227
7520
|
const { aiHost: _aiHost, ...storedConfig } = config;
|
|
@@ -7229,6 +7522,15 @@ function writeConfig(config, cwd = process.cwd()) {
|
|
|
7229
7522
|
writeFileSync(path, `${JSON.stringify(storedConfig, null, 2)}
|
|
7230
7523
|
`, "utf-8");
|
|
7231
7524
|
}
|
|
7525
|
+
function writeGlobalConfig(config, baseDir = homedir()) {
|
|
7526
|
+
const path = getGlobalConfigPath(baseDir);
|
|
7527
|
+
mkdirSync(dirname(path), { recursive: true, mode: 448 });
|
|
7528
|
+
writeFileSync(path, `${JSON.stringify(config, null, 2)}
|
|
7529
|
+
`, {
|
|
7530
|
+
encoding: "utf-8",
|
|
7531
|
+
mode: 384
|
|
7532
|
+
});
|
|
7533
|
+
}
|
|
7232
7534
|
function isGitignored(cwd = process.cwd()) {
|
|
7233
7535
|
const gitignorePath = join(cwd, ".gitignore");
|
|
7234
7536
|
if (!existsSync(gitignorePath))
|
|
@@ -9115,6 +9417,24 @@ var COMMAND_GUIDES = {
|
|
|
9115
9417
|
description: "check config, remotes, and workflow resolution together"
|
|
9116
9418
|
}
|
|
9117
9419
|
]
|
|
9420
|
+
},
|
|
9421
|
+
label: {
|
|
9422
|
+
summary: "Apply existing labels or get ranked suggestions for issues and pull requests.",
|
|
9423
|
+
examples: [
|
|
9424
|
+
{ command: "cn label --help", description: "learn label add and suggest usage" },
|
|
9425
|
+
{
|
|
9426
|
+
command: "cn label add --issue 42 bug,enhancement",
|
|
9427
|
+
description: "apply labels to an issue"
|
|
9428
|
+
},
|
|
9429
|
+
{
|
|
9430
|
+
command: 'cn label add --pr 7 "good first issue"',
|
|
9431
|
+
description: "apply a label with spaces to a PR"
|
|
9432
|
+
},
|
|
9433
|
+
{
|
|
9434
|
+
command: "cn label suggest --issue 42",
|
|
9435
|
+
description: "get ranked label suggestions from content"
|
|
9436
|
+
}
|
|
9437
|
+
]
|
|
9118
9438
|
}
|
|
9119
9439
|
};
|
|
9120
9440
|
var LOADING_TIPS = [
|
|
@@ -10619,19 +10939,19 @@ init_dist();
|
|
|
10619
10939
|
|
|
10620
10940
|
// src/utils/secrets.ts
|
|
10621
10941
|
import { chmodSync, existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync4 } from "fs";
|
|
10622
|
-
import { homedir } from "os";
|
|
10942
|
+
import { homedir as homedir2 } from "os";
|
|
10623
10943
|
import { join as join5, resolve as resolve4 } from "path";
|
|
10624
10944
|
var CONTRIBUTE_NOW_SECRETS_DIRNAME = ".contribute-now";
|
|
10625
10945
|
var CONTRIBUTE_NOW_SECRETS_STORE_DIRNAME = "secrets";
|
|
10626
10946
|
var OLLAMA_CLOUD_API_KEY = "ollama.cloud.apiKey";
|
|
10627
10947
|
var OPENROUTER_API_KEY = "openrouter.apiKey";
|
|
10628
|
-
function getSecretsStorePath(baseDir =
|
|
10948
|
+
function getSecretsStorePath(baseDir = homedir2()) {
|
|
10629
10949
|
return resolve4(baseDir, CONTRIBUTE_NOW_SECRETS_DIRNAME, CONTRIBUTE_NOW_SECRETS_STORE_DIRNAME);
|
|
10630
10950
|
}
|
|
10631
|
-
function getSecretsFilePath(baseDir =
|
|
10951
|
+
function getSecretsFilePath(baseDir = homedir2()) {
|
|
10632
10952
|
return join5(getSecretsStorePath(baseDir), "store.json");
|
|
10633
10953
|
}
|
|
10634
|
-
function readSecretsStore(baseDir =
|
|
10954
|
+
function readSecretsStore(baseDir = homedir2()) {
|
|
10635
10955
|
const filePath = getSecretsFilePath(baseDir);
|
|
10636
10956
|
if (!existsSync5(filePath)) {
|
|
10637
10957
|
return null;
|
|
@@ -10643,7 +10963,7 @@ function readSecretsStore(baseDir = homedir()) {
|
|
|
10643
10963
|
return null;
|
|
10644
10964
|
}
|
|
10645
10965
|
}
|
|
10646
|
-
function writeSecretsStore(store, baseDir =
|
|
10966
|
+
function writeSecretsStore(store, baseDir = homedir2()) {
|
|
10647
10967
|
const storePath = getSecretsStorePath(baseDir);
|
|
10648
10968
|
const filePath = getSecretsFilePath(baseDir);
|
|
10649
10969
|
mkdirSync4(storePath, { recursive: true, mode: 448 });
|
|
@@ -10657,23 +10977,23 @@ function writeSecretsStore(store, baseDir = homedir()) {
|
|
|
10657
10977
|
chmodSync(filePath, 384);
|
|
10658
10978
|
} catch {}
|
|
10659
10979
|
}
|
|
10660
|
-
function hasSecretsStore(baseDir =
|
|
10980
|
+
function hasSecretsStore(baseDir = homedir2()) {
|
|
10661
10981
|
return existsSync5(getSecretsFilePath(baseDir));
|
|
10662
10982
|
}
|
|
10663
|
-
async function hasOllamaCloudApiKey(baseDir =
|
|
10983
|
+
async function hasOllamaCloudApiKey(baseDir = homedir2()) {
|
|
10664
10984
|
return typeof readSecretsStore(baseDir)?.[OLLAMA_CLOUD_API_KEY] === "string";
|
|
10665
10985
|
}
|
|
10666
|
-
async function getOllamaCloudApiKey(baseDir =
|
|
10986
|
+
async function getOllamaCloudApiKey(baseDir = homedir2()) {
|
|
10667
10987
|
return readSecretsStore(baseDir)?.[OLLAMA_CLOUD_API_KEY] ?? null;
|
|
10668
10988
|
}
|
|
10669
|
-
async function setOllamaCloudApiKey(value, baseDir =
|
|
10989
|
+
async function setOllamaCloudApiKey(value, baseDir = homedir2()) {
|
|
10670
10990
|
const existingStore = readSecretsStore(baseDir) ?? {};
|
|
10671
10991
|
writeSecretsStore({
|
|
10672
10992
|
...existingStore,
|
|
10673
10993
|
[OLLAMA_CLOUD_API_KEY]: value
|
|
10674
10994
|
}, baseDir);
|
|
10675
10995
|
}
|
|
10676
|
-
async function deleteOllamaCloudApiKey(baseDir =
|
|
10996
|
+
async function deleteOllamaCloudApiKey(baseDir = homedir2()) {
|
|
10677
10997
|
const existingStore = readSecretsStore(baseDir);
|
|
10678
10998
|
if (!existingStore || !(OLLAMA_CLOUD_API_KEY in existingStore)) {
|
|
10679
10999
|
return false;
|
|
@@ -10690,20 +11010,20 @@ async function deleteOllamaCloudApiKey(baseDir = homedir()) {
|
|
|
10690
11010
|
writeSecretsStore(nextStore, baseDir);
|
|
10691
11011
|
return true;
|
|
10692
11012
|
}
|
|
10693
|
-
async function hasOpenRouterApiKey(baseDir =
|
|
11013
|
+
async function hasOpenRouterApiKey(baseDir = homedir2()) {
|
|
10694
11014
|
return typeof readSecretsStore(baseDir)?.[OPENROUTER_API_KEY] === "string";
|
|
10695
11015
|
}
|
|
10696
|
-
async function getOpenRouterApiKey(baseDir =
|
|
11016
|
+
async function getOpenRouterApiKey(baseDir = homedir2()) {
|
|
10697
11017
|
return readSecretsStore(baseDir)?.[OPENROUTER_API_KEY] ?? null;
|
|
10698
11018
|
}
|
|
10699
|
-
async function setOpenRouterApiKey(value, baseDir =
|
|
11019
|
+
async function setOpenRouterApiKey(value, baseDir = homedir2()) {
|
|
10700
11020
|
const existingStore = readSecretsStore(baseDir) ?? {};
|
|
10701
11021
|
writeSecretsStore({
|
|
10702
11022
|
...existingStore,
|
|
10703
11023
|
[OPENROUTER_API_KEY]: value
|
|
10704
11024
|
}, baseDir);
|
|
10705
11025
|
}
|
|
10706
|
-
async function deleteOpenRouterApiKey(baseDir =
|
|
11026
|
+
async function deleteOpenRouterApiKey(baseDir = homedir2()) {
|
|
10707
11027
|
const existingStore = readSecretsStore(baseDir);
|
|
10708
11028
|
if (!existingStore || !(OPENROUTER_API_KEY in existingStore)) {
|
|
10709
11029
|
return false;
|
|
@@ -10789,6 +11109,31 @@ var DEFAULT_OLLAMA_CLOUD_MODEL = "gpt-oss:120b";
|
|
|
10789
11109
|
var DEFAULT_OLLAMA_CLOUD_HOST = "https://ollama.com/v1";
|
|
10790
11110
|
var DEFAULT_OPENROUTER_MODEL = "openai/gpt-4o-mini";
|
|
10791
11111
|
var DEFAULT_OPENROUTER_HOST = "https://openrouter.ai/api/v1";
|
|
11112
|
+
function resolveAIConfigFromSources(repoConfig, globalConfig) {
|
|
11113
|
+
const provider = repoConfig?.aiProvider ?? globalConfig?.aiProvider ?? "copilot";
|
|
11114
|
+
const useGlobalModel = !repoConfig?.aiModel && globalConfig?.aiProvider === provider;
|
|
11115
|
+
const globalModel = useGlobalModel ? globalConfig?.aiModel?.trim() : undefined;
|
|
11116
|
+
if (provider === "ollama-cloud") {
|
|
11117
|
+
return {
|
|
11118
|
+
provider,
|
|
11119
|
+
providerLabel: "Ollama Cloud",
|
|
11120
|
+
model: repoConfig?.aiModel?.trim() || globalModel || DEFAULT_OLLAMA_CLOUD_MODEL,
|
|
11121
|
+
host: DEFAULT_OLLAMA_CLOUD_HOST
|
|
11122
|
+
};
|
|
11123
|
+
}
|
|
11124
|
+
if (provider === "openrouter") {
|
|
11125
|
+
return {
|
|
11126
|
+
provider,
|
|
11127
|
+
providerLabel: "OpenRouter",
|
|
11128
|
+
model: repoConfig?.aiModel?.trim() || globalModel || DEFAULT_OPENROUTER_MODEL,
|
|
11129
|
+
host: DEFAULT_OPENROUTER_HOST
|
|
11130
|
+
};
|
|
11131
|
+
}
|
|
11132
|
+
return {
|
|
11133
|
+
provider: "copilot",
|
|
11134
|
+
providerLabel: "GitHub Copilot"
|
|
11135
|
+
};
|
|
11136
|
+
}
|
|
10792
11137
|
function prioritizeOllamaCloudModels(models, preferredModel = DEFAULT_OLLAMA_CLOUD_MODEL) {
|
|
10793
11138
|
const uniqueModels = [...new Set(models.map((model) => model.trim()).filter(Boolean))];
|
|
10794
11139
|
const sortedModels = [...uniqueModels].sort((left, right) => left.localeCompare(right));
|
|
@@ -10862,28 +11207,9 @@ function prioritizeOpenRouterModels(models, preferredModel = DEFAULT_OPENROUTER_
|
|
|
10862
11207
|
return sortedModels.includes(preferredModel) ? [preferredModel, ...sortedModels.filter((model) => model !== preferredModel)] : sortedModels;
|
|
10863
11208
|
}
|
|
10864
11209
|
function resolveAIConfig(config) {
|
|
10865
|
-
const
|
|
10866
|
-
const
|
|
10867
|
-
|
|
10868
|
-
return {
|
|
10869
|
-
provider,
|
|
10870
|
-
providerLabel: "Ollama Cloud",
|
|
10871
|
-
model: resolvedConfig?.aiModel?.trim() || DEFAULT_OLLAMA_CLOUD_MODEL,
|
|
10872
|
-
host: DEFAULT_OLLAMA_CLOUD_HOST
|
|
10873
|
-
};
|
|
10874
|
-
}
|
|
10875
|
-
if (provider === "openrouter") {
|
|
10876
|
-
return {
|
|
10877
|
-
provider,
|
|
10878
|
-
providerLabel: "OpenRouter",
|
|
10879
|
-
model: resolvedConfig?.aiModel?.trim() || DEFAULT_OPENROUTER_MODEL,
|
|
10880
|
-
host: DEFAULT_OPENROUTER_HOST
|
|
10881
|
-
};
|
|
10882
|
-
}
|
|
10883
|
-
return {
|
|
10884
|
-
provider: "copilot",
|
|
10885
|
-
providerLabel: "GitHub Copilot"
|
|
10886
|
-
};
|
|
11210
|
+
const repoConfig = config ?? readConfig();
|
|
11211
|
+
const globalConfig = readGlobalConfig();
|
|
11212
|
+
return resolveAIConfigFromSources(repoConfig, globalConfig);
|
|
10887
11213
|
}
|
|
10888
11214
|
function suppressSubprocessWarnings() {
|
|
10889
11215
|
process.env.NODE_NO_WARNINGS = "1";
|
|
@@ -11793,145 +12119,8 @@ async function promptForBranchName(options) {
|
|
|
11793
12119
|
}
|
|
11794
12120
|
}
|
|
11795
12121
|
|
|
11796
|
-
// src/utils/gh.ts
|
|
11797
|
-
import { execFile as execFileCb2 } from "child_process";
|
|
11798
|
-
function run2(args) {
|
|
11799
|
-
return new Promise((resolve5) => {
|
|
11800
|
-
execFileCb2("gh", args, (error2, stdout2, stderr) => {
|
|
11801
|
-
resolve5({
|
|
11802
|
-
exitCode: error2 ? error2.code === "ENOENT" ? 127 : error2.status ?? 1 : 0,
|
|
11803
|
-
stdout: stdout2 ?? "",
|
|
11804
|
-
stderr: stderr ?? ""
|
|
11805
|
-
});
|
|
11806
|
-
});
|
|
11807
|
-
});
|
|
11808
|
-
}
|
|
11809
|
-
async function checkGhInstalled() {
|
|
11810
|
-
try {
|
|
11811
|
-
const { exitCode } = await run2(["--version"]);
|
|
11812
|
-
return exitCode === 0;
|
|
11813
|
-
} catch {
|
|
11814
|
-
return false;
|
|
11815
|
-
}
|
|
11816
|
-
}
|
|
11817
|
-
async function checkGhAuth() {
|
|
11818
|
-
try {
|
|
11819
|
-
const { exitCode } = await run2(["auth", "status"]);
|
|
11820
|
-
return exitCode === 0;
|
|
11821
|
-
} catch {
|
|
11822
|
-
return false;
|
|
11823
|
-
}
|
|
11824
|
-
}
|
|
11825
|
-
var SAFE_SLUG = /^[\w.-]+$/;
|
|
11826
|
-
async function checkRepoPermissions(owner, repo) {
|
|
11827
|
-
if (!SAFE_SLUG.test(owner) || !SAFE_SLUG.test(repo))
|
|
11828
|
-
return null;
|
|
11829
|
-
const { exitCode, stdout: stdout2 } = await run2(["api", `repos/${owner}/${repo}`, "--jq", ".permissions"]);
|
|
11830
|
-
if (exitCode !== 0)
|
|
11831
|
-
return null;
|
|
11832
|
-
try {
|
|
11833
|
-
return JSON.parse(stdout2.trim());
|
|
11834
|
-
} catch {
|
|
11835
|
-
return null;
|
|
11836
|
-
}
|
|
11837
|
-
}
|
|
11838
|
-
async function isRepoFork() {
|
|
11839
|
-
const { exitCode, stdout: stdout2 } = await run2(["repo", "view", "--json", "isFork", "-q", ".isFork"]);
|
|
11840
|
-
if (exitCode !== 0)
|
|
11841
|
-
return null;
|
|
11842
|
-
const val = stdout2.trim();
|
|
11843
|
-
if (val === "true")
|
|
11844
|
-
return true;
|
|
11845
|
-
if (val === "false")
|
|
11846
|
-
return false;
|
|
11847
|
-
return null;
|
|
11848
|
-
}
|
|
11849
|
-
async function getCurrentRepoInfo() {
|
|
11850
|
-
const { exitCode, stdout: stdout2 } = await run2([
|
|
11851
|
-
"repo",
|
|
11852
|
-
"view",
|
|
11853
|
-
"--json",
|
|
11854
|
-
"nameWithOwner",
|
|
11855
|
-
"-q",
|
|
11856
|
-
".nameWithOwner"
|
|
11857
|
-
]);
|
|
11858
|
-
if (exitCode !== 0)
|
|
11859
|
-
return null;
|
|
11860
|
-
const nameWithOwner = stdout2.trim();
|
|
11861
|
-
if (!nameWithOwner)
|
|
11862
|
-
return null;
|
|
11863
|
-
const [owner, repo] = nameWithOwner.split("/");
|
|
11864
|
-
if (!owner || !repo)
|
|
11865
|
-
return null;
|
|
11866
|
-
return { owner, repo };
|
|
11867
|
-
}
|
|
11868
|
-
async function createPR(options) {
|
|
11869
|
-
const args = [
|
|
11870
|
-
"pr",
|
|
11871
|
-
"create",
|
|
11872
|
-
"--base",
|
|
11873
|
-
options.base,
|
|
11874
|
-
"--title",
|
|
11875
|
-
options.title,
|
|
11876
|
-
"--body",
|
|
11877
|
-
options.body
|
|
11878
|
-
];
|
|
11879
|
-
if (options.draft)
|
|
11880
|
-
args.push("--draft");
|
|
11881
|
-
return run2(args);
|
|
11882
|
-
}
|
|
11883
|
-
async function createPRFill(base, draft) {
|
|
11884
|
-
const args = ["pr", "create", "--base", base, "--fill"];
|
|
11885
|
-
if (draft)
|
|
11886
|
-
args.push("--draft");
|
|
11887
|
-
return run2(args);
|
|
11888
|
-
}
|
|
11889
|
-
async function getPRForBranch(headBranch) {
|
|
11890
|
-
const { exitCode, stdout: stdout2 } = await run2([
|
|
11891
|
-
"pr",
|
|
11892
|
-
"list",
|
|
11893
|
-
"--head",
|
|
11894
|
-
headBranch,
|
|
11895
|
-
"--state",
|
|
11896
|
-
"open",
|
|
11897
|
-
"--json",
|
|
11898
|
-
"number,url,title,state",
|
|
11899
|
-
"--limit",
|
|
11900
|
-
"1"
|
|
11901
|
-
]);
|
|
11902
|
-
if (exitCode !== 0)
|
|
11903
|
-
return null;
|
|
11904
|
-
try {
|
|
11905
|
-
const prs = JSON.parse(stdout2.trim());
|
|
11906
|
-
return prs.length > 0 ? prs[0] : null;
|
|
11907
|
-
} catch {
|
|
11908
|
-
return null;
|
|
11909
|
-
}
|
|
11910
|
-
}
|
|
11911
|
-
async function getMergedPRForBranch(headBranch) {
|
|
11912
|
-
const { exitCode, stdout: stdout2 } = await run2([
|
|
11913
|
-
"pr",
|
|
11914
|
-
"list",
|
|
11915
|
-
"--head",
|
|
11916
|
-
headBranch,
|
|
11917
|
-
"--state",
|
|
11918
|
-
"merged",
|
|
11919
|
-
"--json",
|
|
11920
|
-
"number,url,title,state",
|
|
11921
|
-
"--limit",
|
|
11922
|
-
"1"
|
|
11923
|
-
]);
|
|
11924
|
-
if (exitCode !== 0)
|
|
11925
|
-
return null;
|
|
11926
|
-
try {
|
|
11927
|
-
const prs = JSON.parse(stdout2.trim());
|
|
11928
|
-
return prs.length > 0 ? prs[0] : null;
|
|
11929
|
-
} catch {
|
|
11930
|
-
return null;
|
|
11931
|
-
}
|
|
11932
|
-
}
|
|
11933
|
-
|
|
11934
12122
|
// src/commands/clean.ts
|
|
12123
|
+
init_gh();
|
|
11935
12124
|
async function handleCurrentBranchDeletion(currentBranch, baseBranch, config) {
|
|
11936
12125
|
if (!config)
|
|
11937
12126
|
return "skipped";
|
|
@@ -12723,6 +12912,62 @@ function buildConfigSnapshot(config, meta) {
|
|
|
12723
12912
|
}
|
|
12724
12913
|
};
|
|
12725
12914
|
}
|
|
12915
|
+
function normalizeGlobalConfig(config) {
|
|
12916
|
+
const normalized = {
|
|
12917
|
+
aiEnabled: config.aiEnabled !== false
|
|
12918
|
+
};
|
|
12919
|
+
if (normalized.aiEnabled) {
|
|
12920
|
+
normalized.aiProvider = config.aiProvider ?? "copilot";
|
|
12921
|
+
if (normalized.aiProvider === "ollama-cloud") {
|
|
12922
|
+
normalized.aiModel = (config.aiModel?.trim() || DEFAULT_OLLAMA_CLOUD_MODEL).trim();
|
|
12923
|
+
} else if (normalized.aiProvider === "openrouter") {
|
|
12924
|
+
normalized.aiModel = (config.aiModel?.trim() || DEFAULT_OPENROUTER_MODEL).trim();
|
|
12925
|
+
}
|
|
12926
|
+
}
|
|
12927
|
+
return normalized;
|
|
12928
|
+
}
|
|
12929
|
+
function buildGlobalConfigSnapshot(config) {
|
|
12930
|
+
const normalized = normalizeGlobalConfig(config);
|
|
12931
|
+
const aiProvider = normalized.aiProvider ?? "copilot";
|
|
12932
|
+
const aiEnabled = normalized.aiEnabled !== false;
|
|
12933
|
+
const providerLabel = aiProvider === "ollama-cloud" ? "Ollama Cloud" : aiProvider === "openrouter" ? "OpenRouter" : "GitHub Copilot";
|
|
12934
|
+
return {
|
|
12935
|
+
location: getGlobalConfigPath(),
|
|
12936
|
+
exists: globalConfigExists(),
|
|
12937
|
+
ai: {
|
|
12938
|
+
enabled: aiEnabled,
|
|
12939
|
+
provider: aiProvider,
|
|
12940
|
+
providerLabel,
|
|
12941
|
+
model: aiEnabled ? normalized.aiModel ?? null : null
|
|
12942
|
+
}
|
|
12943
|
+
};
|
|
12944
|
+
}
|
|
12945
|
+
async function promptForGlobalConfigEdits(current) {
|
|
12946
|
+
const normalized = normalizeGlobalConfig(current);
|
|
12947
|
+
const aiEnabled = await selectBooleanValue("Global AI default", normalized.aiEnabled !== false, "Enabled", "Disabled");
|
|
12948
|
+
if (!aiEnabled) {
|
|
12949
|
+
return {
|
|
12950
|
+
config: {
|
|
12951
|
+
aiEnabled: false
|
|
12952
|
+
}
|
|
12953
|
+
};
|
|
12954
|
+
}
|
|
12955
|
+
const currentProvider = normalized.aiProvider ?? "copilot";
|
|
12956
|
+
const aiProvider = await selectCurrentValue("Global AI provider", AI_PROVIDER_OPTIONS, currentProvider);
|
|
12957
|
+
let aiModel;
|
|
12958
|
+
if (aiProvider === "ollama-cloud") {
|
|
12959
|
+
aiModel = await inputPrompt("Global Ollama Cloud model", normalized.aiModel ?? DEFAULT_OLLAMA_CLOUD_MODEL);
|
|
12960
|
+
} else if (aiProvider === "openrouter") {
|
|
12961
|
+
aiModel = await inputPrompt("Global OpenRouter model", normalized.aiModel ?? DEFAULT_OPENROUTER_MODEL);
|
|
12962
|
+
}
|
|
12963
|
+
return {
|
|
12964
|
+
config: {
|
|
12965
|
+
aiEnabled: true,
|
|
12966
|
+
aiProvider,
|
|
12967
|
+
aiModel: aiModel?.trim() || undefined
|
|
12968
|
+
}
|
|
12969
|
+
};
|
|
12970
|
+
}
|
|
12726
12971
|
function defaultDevBranchForWorkflow(workflow) {
|
|
12727
12972
|
return workflow === "git-flow" ? "develop" : "dev";
|
|
12728
12973
|
}
|
|
@@ -13003,12 +13248,26 @@ function printConfigSummary(snapshot) {
|
|
|
13003
13248
|
}
|
|
13004
13249
|
}
|
|
13005
13250
|
}
|
|
13251
|
+
function printGlobalConfigSummary(snapshot) {
|
|
13252
|
+
info(`Global config path: ${import_picocolors10.default.bold(snapshot.location)}`);
|
|
13253
|
+
info(`Global defaults file: ${import_picocolors10.default.bold(snapshot.exists ? "present" : "missing (using built-ins)")}`);
|
|
13254
|
+
info(`AI default: ${import_picocolors10.default.bold(snapshot.ai.enabled ? "enabled" : "disabled")}`);
|
|
13255
|
+
info(`AI provider: ${import_picocolors10.default.bold(snapshot.ai.providerLabel)}`);
|
|
13256
|
+
if (snapshot.ai.model) {
|
|
13257
|
+
info(`AI model: ${import_picocolors10.default.bold(snapshot.ai.model)}`);
|
|
13258
|
+
}
|
|
13259
|
+
}
|
|
13006
13260
|
var config_default = defineCommand({
|
|
13007
13261
|
meta: {
|
|
13008
13262
|
name: "config",
|
|
13009
13263
|
description: "Inspect or edit the repo config without rerunning setup"
|
|
13010
13264
|
},
|
|
13011
13265
|
args: {
|
|
13266
|
+
global: {
|
|
13267
|
+
type: "boolean",
|
|
13268
|
+
description: "Read or edit global defaults instead of repo config",
|
|
13269
|
+
default: false
|
|
13270
|
+
},
|
|
13012
13271
|
json: {
|
|
13013
13272
|
type: "boolean",
|
|
13014
13273
|
description: "Print the active repo config as JSON with metadata",
|
|
@@ -13021,15 +13280,44 @@ var config_default = defineCommand({
|
|
|
13021
13280
|
}
|
|
13022
13281
|
},
|
|
13023
13282
|
async run({ args }) {
|
|
13024
|
-
if (!await isGitRepo()) {
|
|
13025
|
-
error("Not inside a git repository.");
|
|
13026
|
-
process.exit(1);
|
|
13027
|
-
}
|
|
13028
13283
|
if (args.json && args.edit) {
|
|
13029
13284
|
error("Use either --json or --edit, not both at the same time.");
|
|
13030
13285
|
process.exit(1);
|
|
13031
13286
|
}
|
|
13032
13287
|
await projectHeading("config", "\u2699\uFE0F");
|
|
13288
|
+
if (args.global) {
|
|
13289
|
+
const currentGlobal = readGlobalConfig() ?? {};
|
|
13290
|
+
if (args.edit) {
|
|
13291
|
+
try {
|
|
13292
|
+
const editResult = await promptForGlobalConfigEdits(currentGlobal);
|
|
13293
|
+
writeGlobalConfig(normalizeGlobalConfig(editResult.config));
|
|
13294
|
+
success("Updated global defaults.");
|
|
13295
|
+
const snapshot3 = buildGlobalConfigSnapshot(readGlobalConfig() ?? {});
|
|
13296
|
+
printGlobalConfigSummary(snapshot3);
|
|
13297
|
+
if (args.json) {
|
|
13298
|
+
console.log(JSON.stringify(snapshot3, null, 2));
|
|
13299
|
+
}
|
|
13300
|
+
return;
|
|
13301
|
+
} catch (err) {
|
|
13302
|
+
error(err instanceof Error ? err.message : String(err));
|
|
13303
|
+
process.exit(1);
|
|
13304
|
+
}
|
|
13305
|
+
}
|
|
13306
|
+
const snapshot2 = buildGlobalConfigSnapshot(currentGlobal);
|
|
13307
|
+
if (args.json) {
|
|
13308
|
+
console.log(JSON.stringify(snapshot2, null, 2));
|
|
13309
|
+
return;
|
|
13310
|
+
}
|
|
13311
|
+
printGlobalConfigSummary(snapshot2);
|
|
13312
|
+
console.log();
|
|
13313
|
+
console.log(` ${import_picocolors10.default.dim("Run `cn config --global --edit` to update global defaults.")}`);
|
|
13314
|
+
console.log();
|
|
13315
|
+
return;
|
|
13316
|
+
}
|
|
13317
|
+
if (!await isGitRepo()) {
|
|
13318
|
+
error("Not inside a git repository.");
|
|
13319
|
+
process.exit(1);
|
|
13320
|
+
}
|
|
13033
13321
|
if (!configExists()) {
|
|
13034
13322
|
error("No repo config found. Run `cn setup` first.");
|
|
13035
13323
|
process.exit(1);
|
|
@@ -13195,7 +13483,7 @@ var import_picocolors12 = __toESM(require_picocolors(), 1);
|
|
|
13195
13483
|
// package.json
|
|
13196
13484
|
var package_default = {
|
|
13197
13485
|
name: "contribute-now",
|
|
13198
|
-
version: "0.8.0-dev.
|
|
13486
|
+
version: "0.8.0-dev.a835394",
|
|
13199
13487
|
description: "Developer CLI that automates git workflows \u2014 branching, syncing, committing, and PRs \u2014 with multi-workflow and commit convention support.",
|
|
13200
13488
|
type: "module",
|
|
13201
13489
|
bin: {
|
|
@@ -13253,6 +13541,9 @@ var package_default = {
|
|
|
13253
13541
|
}
|
|
13254
13542
|
};
|
|
13255
13543
|
|
|
13544
|
+
// src/commands/doctor.ts
|
|
13545
|
+
init_gh();
|
|
13546
|
+
|
|
13256
13547
|
// src/utils/remote.ts
|
|
13257
13548
|
function parseRepoFromUrl(url) {
|
|
13258
13549
|
const httpsMatch = url.match(/https?:\/\/github\.com\/([^/]+)\/([^/.]+?)(?:\.git)?$/);
|
|
@@ -13762,8 +14053,610 @@ async function uninstallHook() {
|
|
|
13762
14053
|
success("commit-msg hook removed.");
|
|
13763
14054
|
}
|
|
13764
14055
|
|
|
13765
|
-
// src/commands/
|
|
14056
|
+
// src/commands/label.ts
|
|
14057
|
+
init_gh();
|
|
13766
14058
|
var import_picocolors14 = __toESM(require_picocolors(), 1);
|
|
14059
|
+
|
|
14060
|
+
// src/utils/label.ts
|
|
14061
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync6, readFileSync as readFileSync6, statSync as statSync4, writeFileSync as writeFileSync6 } from "fs";
|
|
14062
|
+
import { dirname as dirname5, join as join7, resolve as resolve5 } from "path";
|
|
14063
|
+
|
|
14064
|
+
// src/data/clean-labels.ts
|
|
14065
|
+
var CLEAN_LABELS = [
|
|
14066
|
+
{
|
|
14067
|
+
name: "bug",
|
|
14068
|
+
description: "[Type] Something isn't working [issues, PRs]",
|
|
14069
|
+
color: "d73a4a"
|
|
14070
|
+
},
|
|
14071
|
+
{
|
|
14072
|
+
name: "enhancement",
|
|
14073
|
+
description: "[Type] New feature or improvement to existing functionality [issues, PRs]",
|
|
14074
|
+
color: "1a7f37"
|
|
14075
|
+
},
|
|
14076
|
+
{
|
|
14077
|
+
name: "documentation",
|
|
14078
|
+
description: "[Type] Improvements or additions to docs, README, or guides [issues, PRs]",
|
|
14079
|
+
color: "0075ca"
|
|
14080
|
+
},
|
|
14081
|
+
{
|
|
14082
|
+
name: "refactor",
|
|
14083
|
+
description: "[Type] Code improvement without changing functionality [PRs]",
|
|
14084
|
+
color: "8957e5"
|
|
14085
|
+
},
|
|
14086
|
+
{
|
|
14087
|
+
name: "performance",
|
|
14088
|
+
description: "[Type] Optimization, speed, or resource usage improvements [issues, PRs]",
|
|
14089
|
+
color: "e3795c"
|
|
14090
|
+
},
|
|
14091
|
+
{
|
|
14092
|
+
name: "security",
|
|
14093
|
+
description: "[Type] Security vulnerability or hardening [issues, PRs]",
|
|
14094
|
+
color: "d4a72c"
|
|
14095
|
+
},
|
|
14096
|
+
{
|
|
14097
|
+
name: "blocked",
|
|
14098
|
+
description: "[Status] Waiting on another issue, decision, or external factor [issues]",
|
|
14099
|
+
color: "cf222e"
|
|
14100
|
+
},
|
|
14101
|
+
{
|
|
14102
|
+
name: "needs triage",
|
|
14103
|
+
description: "[Status] New issue \u2014 needs review and categorization [issues]",
|
|
14104
|
+
color: "e16f24"
|
|
14105
|
+
},
|
|
14106
|
+
{
|
|
14107
|
+
name: "awaiting response",
|
|
14108
|
+
description: "[Status] Waiting for more information from the reporter [issues]",
|
|
14109
|
+
color: "1a7ec7"
|
|
14110
|
+
},
|
|
14111
|
+
{
|
|
14112
|
+
name: "ready",
|
|
14113
|
+
description: "[Status] Triaged and ready to be picked up [issues]",
|
|
14114
|
+
color: "2da44e"
|
|
14115
|
+
},
|
|
14116
|
+
{
|
|
14117
|
+
name: "good first issue",
|
|
14118
|
+
description: "[Community] Good for newcomers \u2014 well-scoped and documented [issues]",
|
|
14119
|
+
color: "7057ff"
|
|
14120
|
+
},
|
|
14121
|
+
{
|
|
14122
|
+
name: "help wanted",
|
|
14123
|
+
description: "[Community] Open for community contribution [issues]",
|
|
14124
|
+
color: "0e8a16"
|
|
14125
|
+
},
|
|
14126
|
+
{
|
|
14127
|
+
name: "maintainer only",
|
|
14128
|
+
description: "[Community] Reserved for maintainers \u2014 not open for external contribution [issues, PRs]",
|
|
14129
|
+
color: "b60205"
|
|
14130
|
+
},
|
|
14131
|
+
{
|
|
14132
|
+
name: "duplicate",
|
|
14133
|
+
description: "[Resolution] This issue or pull request already exists [issues, PRs]",
|
|
14134
|
+
color: "cfd3d7"
|
|
14135
|
+
},
|
|
14136
|
+
{
|
|
14137
|
+
name: "invalid",
|
|
14138
|
+
description: "[Resolution] This doesn't seem right [issues, PRs]",
|
|
14139
|
+
color: "cfd3d7"
|
|
14140
|
+
},
|
|
14141
|
+
{
|
|
14142
|
+
name: "wontfix",
|
|
14143
|
+
description: "[Resolution] This will not be worked on [issues]",
|
|
14144
|
+
color: "cfd3d7"
|
|
14145
|
+
},
|
|
14146
|
+
{
|
|
14147
|
+
name: "core",
|
|
14148
|
+
description: "[Area] Core logic, business rules, and primary functionality [issues, PRs]",
|
|
14149
|
+
color: "0052cc"
|
|
14150
|
+
},
|
|
14151
|
+
{
|
|
14152
|
+
name: "interface",
|
|
14153
|
+
description: "[Area] User-facing layer \u2014 UI, CLI, API endpoints, or SDK surface [issues, PRs]",
|
|
14154
|
+
color: "5319e7"
|
|
14155
|
+
},
|
|
14156
|
+
{
|
|
14157
|
+
name: "data",
|
|
14158
|
+
description: "[Area] Database, storage, caching, or data models [issues, PRs]",
|
|
14159
|
+
color: "006b75"
|
|
14160
|
+
},
|
|
14161
|
+
{
|
|
14162
|
+
name: "infra",
|
|
14163
|
+
description: "[Area] Build system, CI/CD, deployment, config, and DevOps [issues, PRs]",
|
|
14164
|
+
color: "e16f24"
|
|
14165
|
+
},
|
|
14166
|
+
{
|
|
14167
|
+
name: "testing",
|
|
14168
|
+
description: "[Area] Unit tests, integration tests, E2E, and test tooling [issues, PRs]",
|
|
14169
|
+
color: "1a7f37"
|
|
14170
|
+
}
|
|
14171
|
+
];
|
|
14172
|
+
|
|
14173
|
+
// src/utils/label.ts
|
|
14174
|
+
var LABEL_CACHE_DIRNAME = "contribute-now";
|
|
14175
|
+
var LABEL_CACHE_FILENAME = "labels.json";
|
|
14176
|
+
function findRepoRoot3(cwd = process.cwd()) {
|
|
14177
|
+
let current = resolve5(cwd);
|
|
14178
|
+
while (true) {
|
|
14179
|
+
if (existsSync7(join7(current, ".git"))) {
|
|
14180
|
+
return current;
|
|
14181
|
+
}
|
|
14182
|
+
const parent = dirname5(current);
|
|
14183
|
+
if (parent === current) {
|
|
14184
|
+
return null;
|
|
14185
|
+
}
|
|
14186
|
+
current = parent;
|
|
14187
|
+
}
|
|
14188
|
+
}
|
|
14189
|
+
function resolveGitDir3(cwd = process.cwd()) {
|
|
14190
|
+
const repoRoot = findRepoRoot3(cwd);
|
|
14191
|
+
if (!repoRoot) {
|
|
14192
|
+
return null;
|
|
14193
|
+
}
|
|
14194
|
+
const dotGitPath = join7(repoRoot, ".git");
|
|
14195
|
+
try {
|
|
14196
|
+
const stat = statSync4(dotGitPath);
|
|
14197
|
+
if (stat.isDirectory()) {
|
|
14198
|
+
return dotGitPath;
|
|
14199
|
+
}
|
|
14200
|
+
if (!stat.isFile()) {
|
|
14201
|
+
return null;
|
|
14202
|
+
}
|
|
14203
|
+
const content = readFileSync6(dotGitPath, "utf-8").trim();
|
|
14204
|
+
const match = /^gitdir:\s*(.+)$/i.exec(content);
|
|
14205
|
+
if (!match) {
|
|
14206
|
+
return null;
|
|
14207
|
+
}
|
|
14208
|
+
return resolve5(repoRoot, match[1].trim());
|
|
14209
|
+
} catch {
|
|
14210
|
+
return null;
|
|
14211
|
+
}
|
|
14212
|
+
}
|
|
14213
|
+
function getLabelCachePath(cwd = process.cwd()) {
|
|
14214
|
+
const gitDir = resolveGitDir3(cwd);
|
|
14215
|
+
if (!gitDir) {
|
|
14216
|
+
return null;
|
|
14217
|
+
}
|
|
14218
|
+
return join7(gitDir, LABEL_CACHE_DIRNAME, LABEL_CACHE_FILENAME);
|
|
14219
|
+
}
|
|
14220
|
+
function readLabelCache(cwd = process.cwd()) {
|
|
14221
|
+
const cachePath = getLabelCachePath(cwd);
|
|
14222
|
+
if (!cachePath || !existsSync7(cachePath)) {
|
|
14223
|
+
return null;
|
|
14224
|
+
}
|
|
14225
|
+
try {
|
|
14226
|
+
const raw = JSON.parse(readFileSync6(cachePath, "utf-8"));
|
|
14227
|
+
if (!Array.isArray(raw.labels) || typeof raw.fetchedAt !== "string" || raw.source !== "clean-labels" && raw.source !== "repo") {
|
|
14228
|
+
return null;
|
|
14229
|
+
}
|
|
14230
|
+
const validatedLabels = [];
|
|
14231
|
+
for (const entry of raw.labels) {
|
|
14232
|
+
if (typeof entry !== "object" || entry === null || typeof entry.name !== "string" || !entry.name) {
|
|
14233
|
+
continue;
|
|
14234
|
+
}
|
|
14235
|
+
const e3 = entry;
|
|
14236
|
+
validatedLabels.push({
|
|
14237
|
+
name: e3.name.trim(),
|
|
14238
|
+
description: typeof e3.description === "string" ? e3.description.trim() : "",
|
|
14239
|
+
color: typeof e3.color === "string" ? e3.color.trim() : ""
|
|
14240
|
+
});
|
|
14241
|
+
}
|
|
14242
|
+
if (validatedLabels.length === 0) {
|
|
14243
|
+
return null;
|
|
14244
|
+
}
|
|
14245
|
+
return {
|
|
14246
|
+
labels: validatedLabels,
|
|
14247
|
+
source: raw.source,
|
|
14248
|
+
fetchedAt: raw.fetchedAt
|
|
14249
|
+
};
|
|
14250
|
+
} catch {
|
|
14251
|
+
return null;
|
|
14252
|
+
}
|
|
14253
|
+
}
|
|
14254
|
+
function writeLabelCache(cache, cwd = process.cwd()) {
|
|
14255
|
+
const cachePath = getLabelCachePath(cwd);
|
|
14256
|
+
if (!cachePath) {
|
|
14257
|
+
return;
|
|
14258
|
+
}
|
|
14259
|
+
mkdirSync6(dirname5(cachePath), { recursive: true });
|
|
14260
|
+
writeFileSync6(cachePath, `${JSON.stringify(cache, null, 2)}
|
|
14261
|
+
`, "utf-8");
|
|
14262
|
+
}
|
|
14263
|
+
function normalizeLabelName(name) {
|
|
14264
|
+
return name.toLowerCase().trim();
|
|
14265
|
+
}
|
|
14266
|
+
function isCleanLabelsMatch(repoLabels) {
|
|
14267
|
+
const cleanNames = new Set(CLEAN_LABELS.map((l2) => normalizeLabelName(l2.name)));
|
|
14268
|
+
const repoNames = new Set(repoLabels.map((l2) => normalizeLabelName(l2.name)));
|
|
14269
|
+
if (cleanNames.size !== repoNames.size) {
|
|
14270
|
+
return false;
|
|
14271
|
+
}
|
|
14272
|
+
for (const name of cleanNames) {
|
|
14273
|
+
if (!repoNames.has(name)) {
|
|
14274
|
+
return false;
|
|
14275
|
+
}
|
|
14276
|
+
}
|
|
14277
|
+
return true;
|
|
14278
|
+
}
|
|
14279
|
+
function buildEffectiveLabelSource(repoLabels) {
|
|
14280
|
+
if (isCleanLabelsMatch(repoLabels)) {
|
|
14281
|
+
return {
|
|
14282
|
+
labels: CLEAN_LABELS.map((cl) => ({
|
|
14283
|
+
name: cl.name,
|
|
14284
|
+
description: cl.description,
|
|
14285
|
+
color: cl.color
|
|
14286
|
+
})),
|
|
14287
|
+
source: "clean-labels"
|
|
14288
|
+
};
|
|
14289
|
+
}
|
|
14290
|
+
return { labels: repoLabels, source: "repo" };
|
|
14291
|
+
}
|
|
14292
|
+
async function syncLabelCache(cwd = process.cwd()) {
|
|
14293
|
+
const { getRepoLabels: getRepoLabels2 } = await Promise.resolve().then(() => (init_gh(), exports_gh));
|
|
14294
|
+
const repoLabels = await getRepoLabels2();
|
|
14295
|
+
if (repoLabels.length === 0) {
|
|
14296
|
+
return null;
|
|
14297
|
+
}
|
|
14298
|
+
const { labels, source } = buildEffectiveLabelSource(repoLabels);
|
|
14299
|
+
const cache = {
|
|
14300
|
+
labels,
|
|
14301
|
+
source,
|
|
14302
|
+
fetchedAt: new Date().toISOString()
|
|
14303
|
+
};
|
|
14304
|
+
writeLabelCache(cache, cwd);
|
|
14305
|
+
return cache;
|
|
14306
|
+
}
|
|
14307
|
+
async function getActiveLabels(cwd = process.cwd(), force = false) {
|
|
14308
|
+
if (!force) {
|
|
14309
|
+
const cached = readLabelCache(cwd);
|
|
14310
|
+
if (cached) {
|
|
14311
|
+
return cached;
|
|
14312
|
+
}
|
|
14313
|
+
}
|
|
14314
|
+
return syncLabelCache(cwd);
|
|
14315
|
+
}
|
|
14316
|
+
function parseLabelsCsv(csv) {
|
|
14317
|
+
return csv.split(",").map((part) => part.trim()).filter((part) => part.length > 0);
|
|
14318
|
+
}
|
|
14319
|
+
function validateLabels(requested, available) {
|
|
14320
|
+
const availableNormalized = new Map;
|
|
14321
|
+
for (const label of available) {
|
|
14322
|
+
availableNormalized.set(normalizeLabelName(label.name), label.name);
|
|
14323
|
+
}
|
|
14324
|
+
const valid = [];
|
|
14325
|
+
const invalid = [];
|
|
14326
|
+
for (const req of requested) {
|
|
14327
|
+
const normalized = normalizeLabelName(req);
|
|
14328
|
+
const canonical = availableNormalized.get(normalized);
|
|
14329
|
+
if (canonical !== undefined) {
|
|
14330
|
+
valid.push(canonical);
|
|
14331
|
+
} else {
|
|
14332
|
+
invalid.push(req);
|
|
14333
|
+
}
|
|
14334
|
+
}
|
|
14335
|
+
return { valid, invalid };
|
|
14336
|
+
}
|
|
14337
|
+
function findCloseMatches(input, available, maxResults = 3) {
|
|
14338
|
+
const needle = normalizeLabelName(input);
|
|
14339
|
+
const scored = available.map((label) => {
|
|
14340
|
+
const haystack = normalizeLabelName(label.name);
|
|
14341
|
+
let score = 0;
|
|
14342
|
+
if (haystack === needle) {
|
|
14343
|
+
score = 100;
|
|
14344
|
+
} else if (haystack.startsWith(needle) || needle.startsWith(haystack)) {
|
|
14345
|
+
score = 60;
|
|
14346
|
+
} else if (haystack.includes(needle) || needle.includes(haystack)) {
|
|
14347
|
+
score = 40;
|
|
14348
|
+
} else {
|
|
14349
|
+
const needleBigrams = toBigrams(needle);
|
|
14350
|
+
const haystackBigrams = toBigrams(haystack);
|
|
14351
|
+
const overlap = [...needleBigrams].filter((b2) => haystackBigrams.has(b2)).length;
|
|
14352
|
+
const union = new Set([...needleBigrams, ...haystackBigrams]).size;
|
|
14353
|
+
score = union > 0 ? Math.round(overlap / union * 30) : 0;
|
|
14354
|
+
}
|
|
14355
|
+
return { name: label.name, score };
|
|
14356
|
+
});
|
|
14357
|
+
return scored.filter((item) => item.score > 0).sort((a2, b2) => b2.score - a2.score).slice(0, maxResults).map((item) => item.name);
|
|
14358
|
+
}
|
|
14359
|
+
function scoreLabelsForContent(content, labels) {
|
|
14360
|
+
const contentTokens = tokenize(content.toLowerCase());
|
|
14361
|
+
const scored = labels.map((label) => {
|
|
14362
|
+
const nameTokens = tokenize(label.name.toLowerCase());
|
|
14363
|
+
const descTokens = [...tokenize(stripDescriptionMeta(label.description).toLowerCase())].filter((t2) => t2.length > 3 && !STOP_WORDS.has(t2));
|
|
14364
|
+
let score = 0;
|
|
14365
|
+
for (const token of nameTokens) {
|
|
14366
|
+
if (contentTokens.has(token))
|
|
14367
|
+
score += 3;
|
|
14368
|
+
}
|
|
14369
|
+
const normalizedName = normalizeLabelName(label.name);
|
|
14370
|
+
if (content.toLowerCase().includes(normalizedName)) {
|
|
14371
|
+
score += 5;
|
|
14372
|
+
}
|
|
14373
|
+
for (const token of descTokens) {
|
|
14374
|
+
if (contentTokens.has(token))
|
|
14375
|
+
score += 1;
|
|
14376
|
+
}
|
|
14377
|
+
return { label, score };
|
|
14378
|
+
});
|
|
14379
|
+
return scored.filter((item) => item.score > 0).sort((a2, b2) => b2.score - a2.score);
|
|
14380
|
+
}
|
|
14381
|
+
var STOP_WORDS = new Set([
|
|
14382
|
+
"the",
|
|
14383
|
+
"and",
|
|
14384
|
+
"for",
|
|
14385
|
+
"with",
|
|
14386
|
+
"this",
|
|
14387
|
+
"that",
|
|
14388
|
+
"from",
|
|
14389
|
+
"into",
|
|
14390
|
+
"over",
|
|
14391
|
+
"under",
|
|
14392
|
+
"have",
|
|
14393
|
+
"will",
|
|
14394
|
+
"been",
|
|
14395
|
+
"more",
|
|
14396
|
+
"than",
|
|
14397
|
+
"also",
|
|
14398
|
+
"when",
|
|
14399
|
+
"what",
|
|
14400
|
+
"which",
|
|
14401
|
+
"some",
|
|
14402
|
+
"such",
|
|
14403
|
+
"its",
|
|
14404
|
+
"not",
|
|
14405
|
+
"only",
|
|
14406
|
+
"any",
|
|
14407
|
+
"each",
|
|
14408
|
+
"both"
|
|
14409
|
+
]);
|
|
14410
|
+
function stripDescriptionMeta(description) {
|
|
14411
|
+
return description.replace(/^\[[\w\s]+\]\s*/u, "").replace(/\s*\[[\w,\s]+\]$/u, "").trim();
|
|
14412
|
+
}
|
|
14413
|
+
function tokenize(text) {
|
|
14414
|
+
return new Set(text.split(/[\s\-_/,.:;!?()[\]{}"']+/).filter((t2) => t2.length > 0));
|
|
14415
|
+
}
|
|
14416
|
+
function toBigrams(text) {
|
|
14417
|
+
const bigrams = new Set;
|
|
14418
|
+
for (let i2 = 0;i2 < text.length - 1; i2++) {
|
|
14419
|
+
bigrams.add(text.slice(i2, i2 + 2));
|
|
14420
|
+
}
|
|
14421
|
+
return bigrams;
|
|
14422
|
+
}
|
|
14423
|
+
|
|
14424
|
+
// src/commands/label.ts
|
|
14425
|
+
async function requireGitRepository() {
|
|
14426
|
+
if (!await isGitRepo()) {
|
|
14427
|
+
error("Not inside a git repository.");
|
|
14428
|
+
process.exit(1);
|
|
14429
|
+
}
|
|
14430
|
+
}
|
|
14431
|
+
async function requireGhCli() {
|
|
14432
|
+
if (!await checkGhInstalled()) {
|
|
14433
|
+
error("GitHub CLI (gh) is required. Install it at https://cli.github.com");
|
|
14434
|
+
process.exit(1);
|
|
14435
|
+
}
|
|
14436
|
+
if (!await checkGhAuth()) {
|
|
14437
|
+
error("Not authenticated with GitHub CLI. Run `gh auth login` first.");
|
|
14438
|
+
process.exit(1);
|
|
14439
|
+
}
|
|
14440
|
+
}
|
|
14441
|
+
function extractLabelsCsv(rawArgs) {
|
|
14442
|
+
const knownFlagsWithValues = new Set(["--issue", "--pr", "-i", "-p"]);
|
|
14443
|
+
const parts = [];
|
|
14444
|
+
let skipNext = false;
|
|
14445
|
+
for (const arg of rawArgs) {
|
|
14446
|
+
if (skipNext) {
|
|
14447
|
+
skipNext = false;
|
|
14448
|
+
continue;
|
|
14449
|
+
}
|
|
14450
|
+
if (knownFlagsWithValues.has(arg)) {
|
|
14451
|
+
skipNext = true;
|
|
14452
|
+
continue;
|
|
14453
|
+
}
|
|
14454
|
+
if (arg.startsWith("-")) {
|
|
14455
|
+
continue;
|
|
14456
|
+
}
|
|
14457
|
+
parts.push(arg);
|
|
14458
|
+
}
|
|
14459
|
+
return parts.join(" ");
|
|
14460
|
+
}
|
|
14461
|
+
function formatSourceNote(source) {
|
|
14462
|
+
return source === "clean-labels" ? "(source: Clean Labels dataset)" : "(source: repo labels)";
|
|
14463
|
+
}
|
|
14464
|
+
var addCommand = defineCommand({
|
|
14465
|
+
meta: {
|
|
14466
|
+
name: "add",
|
|
14467
|
+
description: "Apply existing labels to an issue or pull request"
|
|
14468
|
+
},
|
|
14469
|
+
args: {
|
|
14470
|
+
issue: {
|
|
14471
|
+
type: "string",
|
|
14472
|
+
alias: "i",
|
|
14473
|
+
description: "Issue number to label"
|
|
14474
|
+
},
|
|
14475
|
+
pr: {
|
|
14476
|
+
type: "string",
|
|
14477
|
+
alias: "p",
|
|
14478
|
+
description: "Pull request number to label"
|
|
14479
|
+
},
|
|
14480
|
+
labels: {
|
|
14481
|
+
type: "positional",
|
|
14482
|
+
description: "Comma-separated label names (spaces are part of label names)",
|
|
14483
|
+
required: false
|
|
14484
|
+
}
|
|
14485
|
+
},
|
|
14486
|
+
async run({ args, rawArgs }) {
|
|
14487
|
+
await requireGitRepository();
|
|
14488
|
+
await requireGhCli();
|
|
14489
|
+
await projectHeading("label add", "\uD83C\uDFF7\uFE0F");
|
|
14490
|
+
const hasIssue = Boolean(args.issue);
|
|
14491
|
+
const hasPr = Boolean(args.pr);
|
|
14492
|
+
if (!hasIssue && !hasPr) {
|
|
14493
|
+
error("Provide a target: --issue <number> or --pr <number>");
|
|
14494
|
+
process.exit(1);
|
|
14495
|
+
}
|
|
14496
|
+
if (hasIssue && hasPr) {
|
|
14497
|
+
error("Use either --issue or --pr, not both.");
|
|
14498
|
+
process.exit(1);
|
|
14499
|
+
}
|
|
14500
|
+
const targetNumber = Number(hasIssue ? args.issue : args.pr);
|
|
14501
|
+
if (!Number.isInteger(targetNumber) || targetNumber <= 0) {
|
|
14502
|
+
error(`Invalid ${hasIssue ? "issue" : "PR"} number: ${String(hasIssue ? args.issue : args.pr)}`);
|
|
14503
|
+
process.exit(1);
|
|
14504
|
+
}
|
|
14505
|
+
const labelsCsv = extractLabelsCsv(rawArgs);
|
|
14506
|
+
if (!labelsCsv) {
|
|
14507
|
+
error("No labels provided. Pass a comma-separated list after the target flag.");
|
|
14508
|
+
info("Example: cn label add --issue 42 bug,enhancement", "");
|
|
14509
|
+
process.exit(1);
|
|
14510
|
+
}
|
|
14511
|
+
const requested = parseLabelsCsv(labelsCsv);
|
|
14512
|
+
if (requested.length === 0) {
|
|
14513
|
+
error("No valid label names found in input.");
|
|
14514
|
+
process.exit(1);
|
|
14515
|
+
}
|
|
14516
|
+
let cache = await getActiveLabels();
|
|
14517
|
+
if (!cache) {
|
|
14518
|
+
error("Could not load repository labels. Make sure you are authenticated with `gh auth login`.");
|
|
14519
|
+
process.exit(1);
|
|
14520
|
+
}
|
|
14521
|
+
let { valid, invalid } = validateLabels(requested, cache.labels);
|
|
14522
|
+
if (invalid.length > 0) {
|
|
14523
|
+
warn(`Unknown label(s) detected \u2014 resyncing label cache\u2026`);
|
|
14524
|
+
const freshCache = await syncLabelCache();
|
|
14525
|
+
if (freshCache) {
|
|
14526
|
+
cache = freshCache;
|
|
14527
|
+
const revalidated = validateLabels(requested, cache.labels);
|
|
14528
|
+
valid = revalidated.valid;
|
|
14529
|
+
invalid = revalidated.invalid;
|
|
14530
|
+
}
|
|
14531
|
+
}
|
|
14532
|
+
if (invalid.length > 0) {
|
|
14533
|
+
error(`Unknown label(s): ${invalid.map((l2) => import_picocolors14.default.bold(l2)).join(", ")}`);
|
|
14534
|
+
console.log();
|
|
14535
|
+
for (const label of invalid) {
|
|
14536
|
+
const suggestions = findCloseMatches(label, cache.labels);
|
|
14537
|
+
if (suggestions.length > 0) {
|
|
14538
|
+
info(` Did you mean for "${label}": ${suggestions.map((s2) => import_picocolors14.default.cyan(s2)).join(", ")}`, "");
|
|
14539
|
+
}
|
|
14540
|
+
}
|
|
14541
|
+
console.log();
|
|
14542
|
+
info(`Available labels: ${cache.labels.map((l2) => import_picocolors14.default.dim(l2.name)).join(", ")}`, "");
|
|
14543
|
+
process.exit(1);
|
|
14544
|
+
}
|
|
14545
|
+
const targetLabel = hasIssue ? `issue #${targetNumber}` : `PR #${targetNumber}`;
|
|
14546
|
+
info(`Applying ${valid.length} label(s) to ${import_picocolors14.default.bold(targetLabel)}\u2026`, "\uD83C\uDFF7\uFE0F");
|
|
14547
|
+
const result = hasIssue ? await addLabelsToIssue(targetNumber, valid) : await addLabelsToPR(targetNumber, valid);
|
|
14548
|
+
if (result.exitCode !== 0) {
|
|
14549
|
+
const stderr = result.stderr.trim();
|
|
14550
|
+
const isDrift = /not found|does not exist/i.test(stderr) || /not found|does not exist/i.test(result.stdout);
|
|
14551
|
+
if (isDrift) {
|
|
14552
|
+
warn("Label not found on remote \u2014 resyncing and retrying\u2026");
|
|
14553
|
+
const freshCache = await syncLabelCache();
|
|
14554
|
+
if (freshCache) {
|
|
14555
|
+
const revalidated = validateLabels(requested, freshCache.labels);
|
|
14556
|
+
if (revalidated.invalid.length === 0) {
|
|
14557
|
+
const retry = hasIssue ? await addLabelsToIssue(targetNumber, revalidated.valid) : await addLabelsToPR(targetNumber, revalidated.valid);
|
|
14558
|
+
if (retry.exitCode === 0) {
|
|
14559
|
+
success(`Applied to ${import_picocolors14.default.bold(targetLabel)}: ${revalidated.valid.map((l2) => import_picocolors14.default.cyan(l2)).join(", ")}`);
|
|
14560
|
+
return;
|
|
14561
|
+
}
|
|
14562
|
+
error(`Retry failed: ${retry.stderr.trim() || retry.stdout.trim()}`);
|
|
14563
|
+
process.exit(1);
|
|
14564
|
+
}
|
|
14565
|
+
}
|
|
14566
|
+
}
|
|
14567
|
+
error(`Failed to apply labels: ${stderr || result.stdout.trim()}`);
|
|
14568
|
+
info("Run `cn label add --help` for usage guidance.", "");
|
|
14569
|
+
process.exit(1);
|
|
14570
|
+
}
|
|
14571
|
+
success(`Applied to ${import_picocolors14.default.bold(targetLabel)}: ${valid.map((l2) => import_picocolors14.default.cyan(l2)).join(", ")}`);
|
|
14572
|
+
const sourceNote = formatSourceNote(cache.source);
|
|
14573
|
+
info(sourceNote, "");
|
|
14574
|
+
}
|
|
14575
|
+
});
|
|
14576
|
+
var suggestCommand = defineCommand({
|
|
14577
|
+
meta: {
|
|
14578
|
+
name: "suggest",
|
|
14579
|
+
description: "Suggest labels for an issue or pull request based on its content"
|
|
14580
|
+
},
|
|
14581
|
+
args: {
|
|
14582
|
+
issue: {
|
|
14583
|
+
type: "string",
|
|
14584
|
+
alias: "i",
|
|
14585
|
+
description: "Issue number to suggest labels for"
|
|
14586
|
+
},
|
|
14587
|
+
pr: {
|
|
14588
|
+
type: "string",
|
|
14589
|
+
alias: "p",
|
|
14590
|
+
description: "Pull request number to suggest labels for"
|
|
14591
|
+
}
|
|
14592
|
+
},
|
|
14593
|
+
async run({ args }) {
|
|
14594
|
+
await requireGitRepository();
|
|
14595
|
+
await requireGhCli();
|
|
14596
|
+
await projectHeading("label suggest", "\uD83C\uDFF7\uFE0F");
|
|
14597
|
+
const hasIssue = Boolean(args.issue);
|
|
14598
|
+
const hasPr = Boolean(args.pr);
|
|
14599
|
+
if (!hasIssue && !hasPr) {
|
|
14600
|
+
error("Provide a target: --issue <number> or --pr <number>");
|
|
14601
|
+
process.exit(1);
|
|
14602
|
+
}
|
|
14603
|
+
if (hasIssue && hasPr) {
|
|
14604
|
+
error("Use either --issue or --pr, not both.");
|
|
14605
|
+
process.exit(1);
|
|
14606
|
+
}
|
|
14607
|
+
const targetNumber = Number(hasIssue ? args.issue : args.pr);
|
|
14608
|
+
if (!Number.isInteger(targetNumber) || targetNumber <= 0) {
|
|
14609
|
+
error(`Invalid ${hasIssue ? "issue" : "PR"} number: ${String(hasIssue ? args.issue : args.pr)}`);
|
|
14610
|
+
process.exit(1);
|
|
14611
|
+
}
|
|
14612
|
+
const targetLabel = hasIssue ? `issue #${targetNumber}` : `PR #${targetNumber}`;
|
|
14613
|
+
info(`Fetching ${import_picocolors14.default.bold(targetLabel)} content\u2026`, "");
|
|
14614
|
+
const content = hasIssue ? await getIssueContent(targetNumber) : await getPRContent(targetNumber);
|
|
14615
|
+
if (!content) {
|
|
14616
|
+
error(`Could not fetch content for ${targetLabel}. Verify the number and your gh auth.`);
|
|
14617
|
+
process.exit(1);
|
|
14618
|
+
}
|
|
14619
|
+
const fullText = `${content.title}
|
|
14620
|
+
|
|
14621
|
+
${content.body}`;
|
|
14622
|
+
const cache = await getActiveLabels();
|
|
14623
|
+
if (!cache) {
|
|
14624
|
+
error("Could not load repository labels. Run `cn label add --help` for setup guidance.");
|
|
14625
|
+
process.exit(1);
|
|
14626
|
+
}
|
|
14627
|
+
const ranked = scoreLabelsForContent(fullText, cache.labels);
|
|
14628
|
+
if (ranked.length === 0) {
|
|
14629
|
+
info(`No label suggestions found for ${import_picocolors14.default.bold(targetLabel)}.`);
|
|
14630
|
+
info(`Total labels available: ${cache.labels.length}`, "");
|
|
14631
|
+
return;
|
|
14632
|
+
}
|
|
14633
|
+
const sourceNote = formatSourceNote(cache.source);
|
|
14634
|
+
console.log();
|
|
14635
|
+
console.log(` ${import_picocolors14.default.bold(`Suggested labels for ${import_picocolors14.default.cyan(targetLabel)}:`)} ${import_picocolors14.default.dim(sourceNote)}`);
|
|
14636
|
+
console.log();
|
|
14637
|
+
const topN = ranked.slice(0, 5);
|
|
14638
|
+
for (const { label, score } of topN) {
|
|
14639
|
+
const descPart = label.description ? import_picocolors14.default.dim(` \u2014 ${label.description}`) : "";
|
|
14640
|
+
const scorePart = import_picocolors14.default.dim(` [score: ${score}]`);
|
|
14641
|
+
console.log(` ${import_picocolors14.default.cyan("\u2022")} ${import_picocolors14.default.bold(label.name)}${descPart}${scorePart}`);
|
|
14642
|
+
}
|
|
14643
|
+
console.log();
|
|
14644
|
+
info(`Apply a label: cn label add --${hasIssue ? "issue" : "pr"} ${targetNumber} <label>`, "");
|
|
14645
|
+
}
|
|
14646
|
+
});
|
|
14647
|
+
var label_default = defineCommand({
|
|
14648
|
+
meta: {
|
|
14649
|
+
name: "label",
|
|
14650
|
+
description: "Manage labels on issues and pull requests"
|
|
14651
|
+
},
|
|
14652
|
+
subCommands: {
|
|
14653
|
+
add: addCommand,
|
|
14654
|
+
suggest: suggestCommand
|
|
14655
|
+
}
|
|
14656
|
+
});
|
|
14657
|
+
|
|
14658
|
+
// src/commands/log.ts
|
|
14659
|
+
var import_picocolors15 = __toESM(require_picocolors(), 1);
|
|
13767
14660
|
function getDefaultOverviewRemoteCommitCount(hasLocalUnpushedCommits) {
|
|
13768
14661
|
return hasLocalUnpushedCommits ? 10 : 20;
|
|
13769
14662
|
}
|
|
@@ -13861,9 +14754,9 @@ var log_default = defineCommand({
|
|
|
13861
14754
|
} else if (mode === "local") {
|
|
13862
14755
|
if (!compareRef) {
|
|
13863
14756
|
console.log();
|
|
13864
|
-
console.log(
|
|
13865
|
-
console.log(
|
|
13866
|
-
console.log(
|
|
14757
|
+
console.log(import_picocolors15.default.yellow(" \u26A0 Could not determine a comparison branch."));
|
|
14758
|
+
console.log(import_picocolors15.default.dim(" No upstream tracking set and no remote base branch found."));
|
|
14759
|
+
console.log(import_picocolors15.default.dim(` Use ${import_picocolors15.default.bold("cn log --full")} to see the full commit history instead.`));
|
|
13867
14760
|
console.log();
|
|
13868
14761
|
return;
|
|
13869
14762
|
}
|
|
@@ -13882,8 +14775,8 @@ var log_default = defineCommand({
|
|
|
13882
14775
|
const remoteBranch = compareRef ?? targetBranch;
|
|
13883
14776
|
if (!remoteBranch) {
|
|
13884
14777
|
console.log();
|
|
13885
|
-
console.log(
|
|
13886
|
-
console.log(
|
|
14778
|
+
console.log(import_picocolors15.default.yellow(" \u26A0 Could not determine a remote branch to display."));
|
|
14779
|
+
console.log(import_picocolors15.default.dim(" Set an upstream tracking branch or configure your base branch first."));
|
|
13887
14780
|
console.log();
|
|
13888
14781
|
return;
|
|
13889
14782
|
}
|
|
@@ -13942,31 +14835,31 @@ async function getOverviewRemoteCommitCount(currentBranch, compareRef) {
|
|
|
13942
14835
|
}
|
|
13943
14836
|
function printModeHeader(mode, currentBranch, compareRef, usingFallback = false) {
|
|
13944
14837
|
const branch = currentBranch ?? "HEAD";
|
|
13945
|
-
const fallbackNote = usingFallback ?
|
|
14838
|
+
const fallbackNote = usingFallback ? import_picocolors15.default.yellow(" (no upstream \u2014 comparing against base branch)") : "";
|
|
13946
14839
|
switch (mode) {
|
|
13947
14840
|
case "overview":
|
|
13948
|
-
console.log(
|
|
14841
|
+
console.log(import_picocolors15.default.dim(` mode: ${import_picocolors15.default.bold("overview")} \u2014 local unpushed commits and remote branch history for ${import_picocolors15.default.bold(branch)}`) + fallbackNote);
|
|
13949
14842
|
if (compareRef) {
|
|
13950
|
-
console.log(
|
|
14843
|
+
console.log(import_picocolors15.default.dim(` remote source: ${import_picocolors15.default.bold(compareRef)}`));
|
|
13951
14844
|
}
|
|
13952
14845
|
break;
|
|
13953
14846
|
case "local":
|
|
13954
|
-
console.log(
|
|
14847
|
+
console.log(import_picocolors15.default.dim(` mode: ${import_picocolors15.default.bold("local")} \u2014 unpushed commits on ${import_picocolors15.default.bold(branch)}`) + fallbackNote);
|
|
13955
14848
|
if (compareRef) {
|
|
13956
|
-
console.log(
|
|
14849
|
+
console.log(import_picocolors15.default.dim(` comparing: ${import_picocolors15.default.bold(compareRef)} \u279C ${import_picocolors15.default.bold("HEAD")}`));
|
|
13957
14850
|
}
|
|
13958
14851
|
break;
|
|
13959
14852
|
case "remote":
|
|
13960
|
-
console.log(
|
|
14853
|
+
console.log(import_picocolors15.default.dim(` mode: ${import_picocolors15.default.bold("remote")} \u2014 remote branch history relevant to ${import_picocolors15.default.bold(branch)}`) + fallbackNote);
|
|
13961
14854
|
if (compareRef) {
|
|
13962
|
-
console.log(
|
|
14855
|
+
console.log(import_picocolors15.default.dim(` branch: ${import_picocolors15.default.bold(compareRef)}`));
|
|
13963
14856
|
}
|
|
13964
14857
|
break;
|
|
13965
14858
|
case "full":
|
|
13966
|
-
console.log(
|
|
14859
|
+
console.log(import_picocolors15.default.dim(` mode: ${import_picocolors15.default.bold("full")} \u2014 complete commit history for ${import_picocolors15.default.bold(branch)}`));
|
|
13967
14860
|
break;
|
|
13968
14861
|
case "all":
|
|
13969
|
-
console.log(
|
|
14862
|
+
console.log(import_picocolors15.default.dim(` mode: ${import_picocolors15.default.bold("all")} \u2014 commits across all branches`));
|
|
13970
14863
|
break;
|
|
13971
14864
|
}
|
|
13972
14865
|
}
|
|
@@ -13990,7 +14883,7 @@ async function renderScopedLog(options) {
|
|
|
13990
14883
|
}
|
|
13991
14884
|
console.log();
|
|
13992
14885
|
for (const entry of entries) {
|
|
13993
|
-
const hashStr =
|
|
14886
|
+
const hashStr = import_picocolors15.default.yellow(entry.hash);
|
|
13994
14887
|
const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
|
|
13995
14888
|
const subjectStr = colorizeSubject(entry.subject);
|
|
13996
14889
|
console.log(` ${hashStr}${refsStr} ${subjectStr}`);
|
|
@@ -14009,10 +14902,10 @@ async function renderOverviewLog(options) {
|
|
|
14009
14902
|
usingFallback
|
|
14010
14903
|
} = options;
|
|
14011
14904
|
console.log();
|
|
14012
|
-
console.log(
|
|
14905
|
+
console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan(" Local Unpushed Commits")));
|
|
14013
14906
|
if (!compareRef) {
|
|
14014
|
-
console.log(
|
|
14015
|
-
console.log(
|
|
14907
|
+
console.log(import_picocolors15.default.dim(" No comparison branch detected for local commit status."));
|
|
14908
|
+
console.log(import_picocolors15.default.dim(" Set an upstream tracking branch or run cn log --full to inspect the current branch history."));
|
|
14016
14909
|
} else {
|
|
14017
14910
|
await renderScopedLog({
|
|
14018
14911
|
mode: "local",
|
|
@@ -14024,11 +14917,11 @@ async function renderOverviewLog(options) {
|
|
|
14024
14917
|
});
|
|
14025
14918
|
}
|
|
14026
14919
|
console.log();
|
|
14027
|
-
console.log(
|
|
14920
|
+
console.log(import_picocolors15.default.bold(import_picocolors15.default.cyan(" Remote Branch History")));
|
|
14028
14921
|
if (!compareRef) {
|
|
14029
|
-
console.log(
|
|
14922
|
+
console.log(import_picocolors15.default.dim(" No remote branch detected."));
|
|
14030
14923
|
if (usingFallback) {
|
|
14031
|
-
console.log(
|
|
14924
|
+
console.log(import_picocolors15.default.dim(" Configure your base branch or upstream tracking to enable the split view."));
|
|
14032
14925
|
}
|
|
14033
14926
|
return;
|
|
14034
14927
|
}
|
|
@@ -14041,15 +14934,15 @@ async function renderOverviewLog(options) {
|
|
|
14041
14934
|
currentBranch
|
|
14042
14935
|
});
|
|
14043
14936
|
if (!hasRemoteHistory) {
|
|
14044
|
-
console.log(
|
|
14937
|
+
console.log(import_picocolors15.default.dim(" No remote history found for the selected branch."));
|
|
14045
14938
|
}
|
|
14046
14939
|
}
|
|
14047
14940
|
function printEmptyState(mode) {
|
|
14048
14941
|
console.log();
|
|
14049
14942
|
if (mode === "local") {
|
|
14050
|
-
console.log(
|
|
14943
|
+
console.log(import_picocolors15.default.dim(" No local unpushed commits \u2014 you're up to date with remote!"));
|
|
14051
14944
|
} else {
|
|
14052
|
-
console.log(
|
|
14945
|
+
console.log(import_picocolors15.default.dim(" No remote-only commits \u2014 your local branch is up to date!"));
|
|
14053
14946
|
}
|
|
14054
14947
|
console.log();
|
|
14055
14948
|
}
|
|
@@ -14058,7 +14951,7 @@ async function renderFullLog(options) {
|
|
|
14058
14951
|
if (showGraph) {
|
|
14059
14952
|
const lines = await getLogGraph({ count, all, branch: targetBranch });
|
|
14060
14953
|
if (lines.length === 0) {
|
|
14061
|
-
console.log(
|
|
14954
|
+
console.log(import_picocolors15.default.dim(" No commits found."));
|
|
14062
14955
|
console.log();
|
|
14063
14956
|
return false;
|
|
14064
14957
|
}
|
|
@@ -14069,13 +14962,13 @@ async function renderFullLog(options) {
|
|
|
14069
14962
|
} else {
|
|
14070
14963
|
const entries = await getLogEntries({ count, all, branch: targetBranch });
|
|
14071
14964
|
if (entries.length === 0) {
|
|
14072
|
-
console.log(
|
|
14965
|
+
console.log(import_picocolors15.default.dim(" No commits found."));
|
|
14073
14966
|
console.log();
|
|
14074
14967
|
return false;
|
|
14075
14968
|
}
|
|
14076
14969
|
console.log();
|
|
14077
14970
|
for (const entry of entries) {
|
|
14078
|
-
const hashStr =
|
|
14971
|
+
const hashStr = import_picocolors15.default.yellow(entry.hash);
|
|
14079
14972
|
const refsStr = entry.refs ? ` ${colorizeRefs(entry.refs, protectedBranches, currentBranch)}` : "";
|
|
14080
14973
|
const subjectStr = colorizeSubject(entry.subject);
|
|
14081
14974
|
console.log(` ${hashStr}${refsStr} ${subjectStr}`);
|
|
@@ -14087,33 +14980,33 @@ function printFooter(mode, count, overviewRemoteCount, targetBranch) {
|
|
|
14087
14980
|
console.log();
|
|
14088
14981
|
switch (mode) {
|
|
14089
14982
|
case "overview":
|
|
14090
|
-
console.log(
|
|
14983
|
+
console.log(import_picocolors15.default.dim(` Showing up to ${count} local commits and ${overviewRemoteCount} remote commits`));
|
|
14091
14984
|
break;
|
|
14092
14985
|
case "local":
|
|
14093
|
-
console.log(
|
|
14986
|
+
console.log(import_picocolors15.default.dim(` Showing up to ${count} unpushed commits`));
|
|
14094
14987
|
break;
|
|
14095
14988
|
case "remote":
|
|
14096
|
-
console.log(
|
|
14989
|
+
console.log(import_picocolors15.default.dim(` Showing ${count} most recent commits from the remote branch`));
|
|
14097
14990
|
break;
|
|
14098
14991
|
case "full":
|
|
14099
|
-
console.log(
|
|
14992
|
+
console.log(import_picocolors15.default.dim(` Showing ${count} most recent commits${targetBranch ? ` (${targetBranch})` : ""}`));
|
|
14100
14993
|
break;
|
|
14101
14994
|
case "all":
|
|
14102
|
-
console.log(
|
|
14995
|
+
console.log(import_picocolors15.default.dim(` Showing ${count} most recent commits (all branches)`));
|
|
14103
14996
|
break;
|
|
14104
14997
|
}
|
|
14105
14998
|
}
|
|
14106
14999
|
function colorizeGraphLine(line, protectedBranches, currentBranch) {
|
|
14107
15000
|
const match = line.match(/^([|/\\*\s_.-]*)([a-f0-9]{7,12})(\s+\(([^)]+)\))?\s*(.*)/);
|
|
14108
15001
|
if (!match) {
|
|
14109
|
-
return
|
|
15002
|
+
return import_picocolors15.default.cyan(line);
|
|
14110
15003
|
}
|
|
14111
15004
|
const [, graphPart = "", hash, , refs, subject = ""] = match;
|
|
14112
15005
|
const parts = [];
|
|
14113
15006
|
if (graphPart) {
|
|
14114
15007
|
parts.push(colorizeGraphChars(graphPart));
|
|
14115
15008
|
}
|
|
14116
|
-
parts.push(
|
|
15009
|
+
parts.push(import_picocolors15.default.yellow(hash));
|
|
14117
15010
|
if (refs) {
|
|
14118
15011
|
parts.push(` (${colorizeRefs(refs, protectedBranches, currentBranch)})`);
|
|
14119
15012
|
}
|
|
@@ -14124,15 +15017,15 @@ function colorizeGraphChars(graphPart) {
|
|
|
14124
15017
|
return graphPart.split("").map((ch) => {
|
|
14125
15018
|
switch (ch) {
|
|
14126
15019
|
case "*":
|
|
14127
|
-
return
|
|
15020
|
+
return import_picocolors15.default.green(ch);
|
|
14128
15021
|
case "|":
|
|
14129
|
-
return
|
|
15022
|
+
return import_picocolors15.default.cyan(ch);
|
|
14130
15023
|
case "/":
|
|
14131
15024
|
case "\\":
|
|
14132
|
-
return
|
|
15025
|
+
return import_picocolors15.default.cyan(ch);
|
|
14133
15026
|
case "-":
|
|
14134
15027
|
case "_":
|
|
14135
|
-
return
|
|
15028
|
+
return import_picocolors15.default.cyan(ch);
|
|
14136
15029
|
default:
|
|
14137
15030
|
return ch;
|
|
14138
15031
|
}
|
|
@@ -14144,50 +15037,50 @@ function colorizeRefs(refs, protectedBranches, currentBranch) {
|
|
|
14144
15037
|
if (trimmed.startsWith("HEAD ->") || trimmed === "HEAD") {
|
|
14145
15038
|
const branchName = trimmed.replace("HEAD -> ", "");
|
|
14146
15039
|
if (trimmed === "HEAD") {
|
|
14147
|
-
return
|
|
15040
|
+
return import_picocolors15.default.bold(import_picocolors15.default.cyan("HEAD"));
|
|
14148
15041
|
}
|
|
14149
|
-
return `${
|
|
15042
|
+
return `${import_picocolors15.default.bold(import_picocolors15.default.cyan("HEAD"))} ${import_picocolors15.default.dim("->")} ${colorizeRefName(branchName, protectedBranches, currentBranch)}`;
|
|
14150
15043
|
}
|
|
14151
15044
|
if (trimmed.startsWith("tag:")) {
|
|
14152
|
-
return
|
|
15045
|
+
return import_picocolors15.default.bold(import_picocolors15.default.magenta(trimmed));
|
|
14153
15046
|
}
|
|
14154
15047
|
return colorizeRefName(trimmed, protectedBranches, currentBranch);
|
|
14155
|
-
}).join(
|
|
15048
|
+
}).join(import_picocolors15.default.dim(", "));
|
|
14156
15049
|
}
|
|
14157
15050
|
function colorizeRefName(name, protectedBranches, currentBranch) {
|
|
14158
15051
|
const isRemote = name.includes("/");
|
|
14159
15052
|
const localName = isRemote ? name.split("/").slice(1).join("/") : name;
|
|
14160
15053
|
if (protectedBranches.includes(localName)) {
|
|
14161
|
-
return isRemote ?
|
|
15054
|
+
return isRemote ? import_picocolors15.default.bold(import_picocolors15.default.red(name)) : import_picocolors15.default.bold(import_picocolors15.default.red(name));
|
|
14162
15055
|
}
|
|
14163
15056
|
if (localName === currentBranch) {
|
|
14164
|
-
return
|
|
15057
|
+
return import_picocolors15.default.bold(import_picocolors15.default.green(name));
|
|
14165
15058
|
}
|
|
14166
15059
|
if (isRemote) {
|
|
14167
|
-
return
|
|
15060
|
+
return import_picocolors15.default.blue(name);
|
|
14168
15061
|
}
|
|
14169
|
-
return
|
|
15062
|
+
return import_picocolors15.default.green(name);
|
|
14170
15063
|
}
|
|
14171
15064
|
function colorizeSubject(subject) {
|
|
14172
15065
|
const emojiMatch = subject.match(/^((?:\p{Emoji_Presentation}|\p{Emoji}\uFE0F)+\s*)/u);
|
|
14173
15066
|
if (emojiMatch) {
|
|
14174
15067
|
const emoji = emojiMatch[1];
|
|
14175
15068
|
const rest = subject.slice(emoji.length);
|
|
14176
|
-
return `${emoji}${
|
|
15069
|
+
return `${emoji}${import_picocolors15.default.white(rest)}`;
|
|
14177
15070
|
}
|
|
14178
15071
|
if (subject.startsWith("Merge ")) {
|
|
14179
|
-
return
|
|
15072
|
+
return import_picocolors15.default.dim(subject);
|
|
14180
15073
|
}
|
|
14181
|
-
return
|
|
15074
|
+
return import_picocolors15.default.white(subject);
|
|
14182
15075
|
}
|
|
14183
15076
|
|
|
14184
15077
|
// src/commands/save.ts
|
|
14185
15078
|
import { execFile as execFileCb4 } from "child_process";
|
|
14186
|
-
var
|
|
15079
|
+
var import_picocolors16 = __toESM(require_picocolors(), 1);
|
|
14187
15080
|
function gitRun(args) {
|
|
14188
|
-
return new Promise((
|
|
15081
|
+
return new Promise((resolve6) => {
|
|
14189
15082
|
execFileCb4("git", args, (err, stdout2, stderr) => {
|
|
14190
|
-
|
|
15083
|
+
resolve6({
|
|
14191
15084
|
exitCode: err ? err.code === "ENOENT" ? 127 : err.status ?? 1 : 0,
|
|
14192
15085
|
stdout: stdout2 ?? "",
|
|
14193
15086
|
stderr: stderr ?? ""
|
|
@@ -14257,8 +15150,8 @@ async function handleSave(message) {
|
|
|
14257
15150
|
info("No uncommitted changes to save.");
|
|
14258
15151
|
return;
|
|
14259
15152
|
}
|
|
14260
|
-
success(`Saved: ${
|
|
14261
|
-
info(`Use ${
|
|
15153
|
+
success(`Saved: ${import_picocolors16.default.dim(label)}`);
|
|
15154
|
+
info(`Use ${import_picocolors16.default.bold("cn save --restore")} to bring them back.`, "");
|
|
14262
15155
|
}
|
|
14263
15156
|
async function handleRestore() {
|
|
14264
15157
|
await projectHeading("save --restore", "\uD83D\uDCBE");
|
|
@@ -14274,7 +15167,7 @@ async function handleRestore() {
|
|
|
14274
15167
|
warn("You may have conflicts. Resolve them and run `git stash drop` when done.");
|
|
14275
15168
|
process.exit(1);
|
|
14276
15169
|
}
|
|
14277
|
-
success(`Restored: ${
|
|
15170
|
+
success(`Restored: ${import_picocolors16.default.dim(stashes[0].message)}`);
|
|
14278
15171
|
return;
|
|
14279
15172
|
}
|
|
14280
15173
|
const choices = stashes.map((s2) => `${s2.index} ${s2.message}`);
|
|
@@ -14287,7 +15180,7 @@ async function handleRestore() {
|
|
|
14287
15180
|
process.exit(1);
|
|
14288
15181
|
}
|
|
14289
15182
|
const match = stashes.find((s2) => String(s2.index) === idx);
|
|
14290
|
-
success(`Restored: ${
|
|
15183
|
+
success(`Restored: ${import_picocolors16.default.dim(match?.message ?? "saved changes")}`);
|
|
14291
15184
|
}
|
|
14292
15185
|
async function handleList() {
|
|
14293
15186
|
await projectHeading("save --list", "\uD83D\uDCBE");
|
|
@@ -14298,13 +15191,13 @@ async function handleList() {
|
|
|
14298
15191
|
}
|
|
14299
15192
|
console.log();
|
|
14300
15193
|
for (const s2 of stashes) {
|
|
14301
|
-
const idx =
|
|
15194
|
+
const idx = import_picocolors16.default.dim(`[${s2.index}]`);
|
|
14302
15195
|
const msg = s2.message;
|
|
14303
15196
|
console.log(` ${idx} ${msg}`);
|
|
14304
15197
|
}
|
|
14305
15198
|
console.log();
|
|
14306
|
-
info(`Use ${
|
|
14307
|
-
info(`Use ${
|
|
15199
|
+
info(`Use ${import_picocolors16.default.bold("cn save --restore")} to bring changes back.`, "");
|
|
15200
|
+
info(`Use ${import_picocolors16.default.bold("cn save --drop")} to discard saved changes.`, "");
|
|
14308
15201
|
}
|
|
14309
15202
|
async function handleDrop() {
|
|
14310
15203
|
await projectHeading("save --drop", "\uD83D\uDCBE");
|
|
@@ -14322,7 +15215,7 @@ async function handleDrop() {
|
|
|
14322
15215
|
process.exit(1);
|
|
14323
15216
|
}
|
|
14324
15217
|
const match = stashes.find((s2) => String(s2.index) === idx);
|
|
14325
|
-
success(`Dropped: ${
|
|
15218
|
+
success(`Dropped: ${import_picocolors16.default.dim(match?.message ?? "saved changes")}`);
|
|
14326
15219
|
}
|
|
14327
15220
|
async function getStashList() {
|
|
14328
15221
|
const result = await gitRun(["stash", "list"]);
|
|
@@ -14339,7 +15232,8 @@ async function getStashList() {
|
|
|
14339
15232
|
}
|
|
14340
15233
|
|
|
14341
15234
|
// src/commands/setup.ts
|
|
14342
|
-
var
|
|
15235
|
+
var import_picocolors17 = __toESM(require_picocolors(), 1);
|
|
15236
|
+
init_gh();
|
|
14343
15237
|
async function shouldContinueSetupWithExistingConfig(options) {
|
|
14344
15238
|
const { existingConfig, hasConfigFile, confirm, onInfo, onWarn, onSuccess, summary } = options;
|
|
14345
15239
|
if (existingConfig) {
|
|
@@ -14362,6 +15256,43 @@ async function shouldContinueSetupWithExistingConfig(options) {
|
|
|
14362
15256
|
}
|
|
14363
15257
|
return true;
|
|
14364
15258
|
}
|
|
15259
|
+
async function resolveApiKeyForSetup(options) {
|
|
15260
|
+
const { providerLabel, hasStoredKey, getStoredKey, select, promptSecret } = options;
|
|
15261
|
+
if (hasStoredKey) {
|
|
15262
|
+
const choice = await select(`${providerLabel} API key`, [
|
|
15263
|
+
"Keep existing stored key",
|
|
15264
|
+
"Replace stored key"
|
|
15265
|
+
]);
|
|
15266
|
+
if (choice === "Keep existing stored key") {
|
|
15267
|
+
const existing = (await getStoredKey())?.trim() || "";
|
|
15268
|
+
if (existing) {
|
|
15269
|
+
return {
|
|
15270
|
+
apiKey: existing,
|
|
15271
|
+
shouldStore: false,
|
|
15272
|
+
reusedStoredKey: true
|
|
15273
|
+
};
|
|
15274
|
+
}
|
|
15275
|
+
}
|
|
15276
|
+
const replacement = (await promptSecret(`Enter your ${providerLabel} API key`)).trim();
|
|
15277
|
+
if (!replacement) {
|
|
15278
|
+
throw new Error(`${providerLabel} API key is required when ${providerLabel} is selected.`);
|
|
15279
|
+
}
|
|
15280
|
+
return {
|
|
15281
|
+
apiKey: replacement,
|
|
15282
|
+
shouldStore: true,
|
|
15283
|
+
reusedStoredKey: false
|
|
15284
|
+
};
|
|
15285
|
+
}
|
|
15286
|
+
const initial = (await promptSecret(`Enter your ${providerLabel} API key`)).trim();
|
|
15287
|
+
if (!initial) {
|
|
15288
|
+
throw new Error(`${providerLabel} API key is required when ${providerLabel} is selected.`);
|
|
15289
|
+
}
|
|
15290
|
+
return {
|
|
15291
|
+
apiKey: initial,
|
|
15292
|
+
shouldStore: true,
|
|
15293
|
+
reusedStoredKey: false
|
|
15294
|
+
};
|
|
15295
|
+
}
|
|
14365
15296
|
async function promptForOllamaCloudModel(apiKey, host = DEFAULT_OLLAMA_CLOUD_HOST) {
|
|
14366
15297
|
try {
|
|
14367
15298
|
info("Fetching available Ollama Cloud models...");
|
|
@@ -14448,7 +15379,7 @@ var setup_default = defineCommand({
|
|
|
14448
15379
|
workflow = "github-flow";
|
|
14449
15380
|
else if (workflowChoice.startsWith("Git Flow"))
|
|
14450
15381
|
workflow = "git-flow";
|
|
14451
|
-
info(`Workflow: ${
|
|
15382
|
+
info(`Workflow: ${import_picocolors17.default.bold(WORKFLOW_DESCRIPTIONS[workflow])}`);
|
|
14452
15383
|
const conventionChoice = await selectPrompt("Which commit convention should this project use?", [
|
|
14453
15384
|
`${CONVENTION_DESCRIPTIONS["clean-commit"]} (recommended)`,
|
|
14454
15385
|
CONVENTION_DESCRIPTIONS.conventional,
|
|
@@ -14476,32 +15407,58 @@ var setup_default = defineCommand({
|
|
|
14476
15407
|
aiProvider = "copilot";
|
|
14477
15408
|
}
|
|
14478
15409
|
if (aiProvider === "ollama-cloud") {
|
|
14479
|
-
|
|
14480
|
-
|
|
14481
|
-
|
|
15410
|
+
let resolvedKey;
|
|
15411
|
+
try {
|
|
15412
|
+
resolvedKey = await resolveApiKeyForSetup({
|
|
15413
|
+
providerLabel: "Ollama Cloud",
|
|
15414
|
+
hasStoredKey: await hasOllamaCloudApiKey(),
|
|
15415
|
+
getStoredKey: getOllamaCloudApiKey,
|
|
15416
|
+
select: selectPrompt,
|
|
15417
|
+
promptSecret: passwordPrompt
|
|
15418
|
+
});
|
|
15419
|
+
} catch (err) {
|
|
15420
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15421
|
+
error(message);
|
|
14482
15422
|
process.exit(1);
|
|
14483
15423
|
}
|
|
14484
|
-
aiModel = await promptForOllamaCloudModel(apiKey);
|
|
15424
|
+
aiModel = await promptForOllamaCloudModel(resolvedKey.apiKey);
|
|
14485
15425
|
try {
|
|
14486
|
-
|
|
14487
|
-
|
|
14488
|
-
|
|
15426
|
+
if (resolvedKey.shouldStore) {
|
|
15427
|
+
await setOllamaCloudApiKey(resolvedKey.apiKey);
|
|
15428
|
+
success("Stored Ollama Cloud API key in the local secrets store.");
|
|
15429
|
+
info(`Secrets path: ${import_picocolors17.default.bold(getSecretsStorePath())}`);
|
|
15430
|
+
} else {
|
|
15431
|
+
info("Using existing Ollama Cloud API key from the local secrets store.");
|
|
15432
|
+
}
|
|
14489
15433
|
} catch (err) {
|
|
14490
15434
|
const message = err instanceof Error ? err.message : String(err);
|
|
14491
15435
|
error(`Failed to store Ollama Cloud API key: ${message}`);
|
|
14492
15436
|
process.exit(1);
|
|
14493
15437
|
}
|
|
14494
15438
|
} else if (aiProvider === "openrouter") {
|
|
14495
|
-
|
|
14496
|
-
|
|
14497
|
-
|
|
15439
|
+
let resolvedKey;
|
|
15440
|
+
try {
|
|
15441
|
+
resolvedKey = await resolveApiKeyForSetup({
|
|
15442
|
+
providerLabel: "OpenRouter",
|
|
15443
|
+
hasStoredKey: await hasOpenRouterApiKey(),
|
|
15444
|
+
getStoredKey: getOpenRouterApiKey,
|
|
15445
|
+
select: selectPrompt,
|
|
15446
|
+
promptSecret: passwordPrompt
|
|
15447
|
+
});
|
|
15448
|
+
} catch (err) {
|
|
15449
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
15450
|
+
error(message);
|
|
14498
15451
|
process.exit(1);
|
|
14499
15452
|
}
|
|
14500
|
-
aiModel = await promptForOpenRouterModel(apiKey);
|
|
15453
|
+
aiModel = await promptForOpenRouterModel(resolvedKey.apiKey);
|
|
14501
15454
|
try {
|
|
14502
|
-
|
|
14503
|
-
|
|
14504
|
-
|
|
15455
|
+
if (resolvedKey.shouldStore) {
|
|
15456
|
+
await setOpenRouterApiKey(resolvedKey.apiKey);
|
|
15457
|
+
success("Stored OpenRouter API key in the local secrets store.");
|
|
15458
|
+
info(`Secrets path: ${import_picocolors17.default.bold(getSecretsStorePath())}`);
|
|
15459
|
+
} else {
|
|
15460
|
+
info("Using existing OpenRouter API key from the local secrets store.");
|
|
15461
|
+
}
|
|
14505
15462
|
} catch (err) {
|
|
14506
15463
|
const message = err instanceof Error ? err.message : String(err);
|
|
14507
15464
|
error(`Failed to store OpenRouter API key: ${message}`);
|
|
@@ -14563,15 +15520,15 @@ var setup_default = defineCommand({
|
|
|
14563
15520
|
detectedRole = roleChoice;
|
|
14564
15521
|
detectionSource = "user selection";
|
|
14565
15522
|
} else {
|
|
14566
|
-
info(`Detected role: ${
|
|
14567
|
-
const confirmed = await confirmPrompt(`Role detected as ${
|
|
15523
|
+
info(`Detected role: ${import_picocolors17.default.bold(detectedRole)} (via ${detectionSource})`);
|
|
15524
|
+
const confirmed = await confirmPrompt(`Role detected as ${import_picocolors17.default.bold(detectedRole)}. Is this correct?`);
|
|
14568
15525
|
if (!confirmed) {
|
|
14569
15526
|
const roleChoice = await selectPrompt("Select your role:", ["maintainer", "contributor"]);
|
|
14570
15527
|
detectedRole = roleChoice;
|
|
14571
15528
|
}
|
|
14572
15529
|
}
|
|
14573
15530
|
const defaultConfig = getDefaultConfig();
|
|
14574
|
-
info(
|
|
15531
|
+
info(import_picocolors17.default.dim("Tip: press Enter to keep the default branch name shown in each prompt."));
|
|
14575
15532
|
const mainBranchDefault = defaultConfig.mainBranch;
|
|
14576
15533
|
const mainBranch = await inputPrompt(`Main branch name (default: ${mainBranchDefault} \u2014 press Enter to keep)`, mainBranchDefault);
|
|
14577
15534
|
let devBranch;
|
|
@@ -14597,7 +15554,7 @@ var setup_default = defineCommand({
|
|
|
14597
15554
|
error("Setup cannot continue without the upstream remote for contributors.");
|
|
14598
15555
|
process.exit(1);
|
|
14599
15556
|
}
|
|
14600
|
-
success(`Added remote ${
|
|
15557
|
+
success(`Added remote ${import_picocolors17.default.bold(upstreamRemote)} \u2192 ${upstreamUrl}`);
|
|
14601
15558
|
} else {
|
|
14602
15559
|
error("An upstream remote URL is required for contributors.");
|
|
14603
15560
|
info("Add it manually: git remote add upstream <url>", "");
|
|
@@ -14620,67 +15577,67 @@ var setup_default = defineCommand({
|
|
|
14620
15577
|
showTips
|
|
14621
15578
|
};
|
|
14622
15579
|
writeConfig(config);
|
|
14623
|
-
success(`Config written to ${
|
|
15580
|
+
success(`Config written to ${import_picocolors17.default.bold(getConfigLocationLabel())}`);
|
|
14624
15581
|
info("This setup is stored locally for this clone and does not modify tracked files.", "");
|
|
14625
15582
|
const syncRemote = config.role === "contributor" ? config.upstream : config.origin;
|
|
14626
|
-
info(`Fetching ${
|
|
15583
|
+
info(`Fetching ${import_picocolors17.default.bold(syncRemote)} to verify branch configuration...`, "");
|
|
14627
15584
|
await fetchRemote(syncRemote);
|
|
14628
15585
|
const mainRef = `${syncRemote}/${config.mainBranch}`;
|
|
14629
15586
|
if (!await refExists(mainRef)) {
|
|
14630
|
-
warn(`Main branch ref ${
|
|
15587
|
+
warn(`Main branch ref ${import_picocolors17.default.bold(mainRef)} not found on remote.`);
|
|
14631
15588
|
warn("Config was saved \u2014 verify the branch name and re-run setup if needed.");
|
|
14632
15589
|
}
|
|
14633
15590
|
if (config.devBranch) {
|
|
14634
15591
|
const devRef = `${syncRemote}/${config.devBranch}`;
|
|
14635
15592
|
if (!await refExists(devRef)) {
|
|
14636
|
-
warn(`Dev branch ref ${
|
|
15593
|
+
warn(`Dev branch ref ${import_picocolors17.default.bold(devRef)} not found on remote.`);
|
|
14637
15594
|
warn("Config was saved \u2014 verify the branch name and re-run setup if needed.");
|
|
14638
15595
|
}
|
|
14639
15596
|
}
|
|
14640
15597
|
console.log();
|
|
14641
15598
|
const resolvedAIConfig = resolveAIConfig(config);
|
|
14642
|
-
info(`Workflow: ${
|
|
14643
|
-
info(`Convention: ${
|
|
14644
|
-
info(`AI: ${
|
|
15599
|
+
info(`Workflow: ${import_picocolors17.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
15600
|
+
info(`Convention: ${import_picocolors17.default.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
|
|
15601
|
+
info(`AI: ${import_picocolors17.default.bold(isAIEnabled(config) ? "enabled" : "disabled")}`);
|
|
14645
15602
|
if (isAIEnabled(config)) {
|
|
14646
|
-
info(`AI provider: ${
|
|
15603
|
+
info(`AI provider: ${import_picocolors17.default.bold(resolvedAIConfig.providerLabel)}`);
|
|
14647
15604
|
if (resolvedAIConfig.model) {
|
|
14648
|
-
info(`AI model: ${
|
|
15605
|
+
info(`AI model: ${import_picocolors17.default.bold(resolvedAIConfig.model)}`);
|
|
14649
15606
|
}
|
|
14650
15607
|
}
|
|
14651
|
-
info(`Guides: ${
|
|
14652
|
-
info(`Role: ${
|
|
15608
|
+
info(`Guides: ${import_picocolors17.default.bold(shouldShowTips(config) ? "shown" : "hidden")}`);
|
|
15609
|
+
info(`Role: ${import_picocolors17.default.bold(config.role)}`);
|
|
14653
15610
|
if (config.devBranch) {
|
|
14654
|
-
info(`Main: ${
|
|
15611
|
+
info(`Main: ${import_picocolors17.default.bold(config.mainBranch)} | Dev: ${import_picocolors17.default.bold(config.devBranch)}`);
|
|
14655
15612
|
} else {
|
|
14656
|
-
info(`Main: ${
|
|
15613
|
+
info(`Main: ${import_picocolors17.default.bold(config.mainBranch)}`);
|
|
14657
15614
|
}
|
|
14658
|
-
info(`Origin: ${
|
|
15615
|
+
info(`Origin: ${import_picocolors17.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors17.default.bold(config.upstream)}` : ""}`);
|
|
14659
15616
|
}
|
|
14660
15617
|
});
|
|
14661
15618
|
function logConfigSummary(config) {
|
|
14662
15619
|
const aiConfig = resolveAIConfig(config);
|
|
14663
|
-
info(`Workflow: ${
|
|
14664
|
-
info(`Convention: ${
|
|
14665
|
-
info(`AI: ${
|
|
15620
|
+
info(`Workflow: ${import_picocolors17.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
15621
|
+
info(`Convention: ${import_picocolors17.default.bold(CONVENTION_DESCRIPTIONS[config.commitConvention])}`);
|
|
15622
|
+
info(`AI: ${import_picocolors17.default.bold(isAIEnabled(config) ? "enabled" : "disabled")}`);
|
|
14666
15623
|
if (isAIEnabled(config)) {
|
|
14667
|
-
info(`AI provider: ${
|
|
15624
|
+
info(`AI provider: ${import_picocolors17.default.bold(aiConfig.providerLabel)}`);
|
|
14668
15625
|
if (aiConfig.model) {
|
|
14669
|
-
info(`AI model: ${
|
|
15626
|
+
info(`AI model: ${import_picocolors17.default.bold(aiConfig.model)}`);
|
|
14670
15627
|
}
|
|
14671
15628
|
}
|
|
14672
|
-
info(`Guides: ${
|
|
14673
|
-
info(`Role: ${
|
|
15629
|
+
info(`Guides: ${import_picocolors17.default.bold(shouldShowTips(config) ? "shown" : "hidden")}`);
|
|
15630
|
+
info(`Role: ${import_picocolors17.default.bold(config.role)}`);
|
|
14674
15631
|
if (config.devBranch) {
|
|
14675
|
-
info(`Main: ${
|
|
15632
|
+
info(`Main: ${import_picocolors17.default.bold(config.mainBranch)} | Dev: ${import_picocolors17.default.bold(config.devBranch)}`);
|
|
14676
15633
|
} else {
|
|
14677
|
-
info(`Main: ${
|
|
15634
|
+
info(`Main: ${import_picocolors17.default.bold(config.mainBranch)}`);
|
|
14678
15635
|
}
|
|
14679
|
-
info(`Origin: ${
|
|
15636
|
+
info(`Origin: ${import_picocolors17.default.bold(config.origin)}${config.role === "contributor" ? ` | Upstream: ${import_picocolors17.default.bold(config.upstream)}` : ""}`);
|
|
14680
15637
|
}
|
|
14681
15638
|
|
|
14682
15639
|
// src/commands/start.ts
|
|
14683
|
-
var
|
|
15640
|
+
var import_picocolors18 = __toESM(require_picocolors(), 1);
|
|
14684
15641
|
var start_default = defineCommand({
|
|
14685
15642
|
meta: {
|
|
14686
15643
|
name: "start",
|
|
@@ -14732,16 +15689,16 @@ var start_default = defineCommand({
|
|
|
14732
15689
|
warn("Start cancelled.");
|
|
14733
15690
|
process.exit(0);
|
|
14734
15691
|
}
|
|
14735
|
-
info(`Creating branch: ${
|
|
15692
|
+
info(`Creating branch: ${import_picocolors18.default.bold(branchName)}`);
|
|
14736
15693
|
await fetchRemote(syncSource.remote);
|
|
14737
15694
|
if (!await refExists(syncSource.ref)) {
|
|
14738
|
-
warn(`Remote ref ${
|
|
15695
|
+
warn(`Remote ref ${import_picocolors18.default.bold(syncSource.ref)} not found. Creating branch from local ${import_picocolors18.default.bold(baseBranch)}.`);
|
|
14739
15696
|
}
|
|
14740
15697
|
const currentBranch = await getCurrentBranch();
|
|
14741
15698
|
if (currentBranch === baseBranch && await refExists(syncSource.ref)) {
|
|
14742
15699
|
const ahead = await countCommitsAhead(baseBranch, syncSource.ref);
|
|
14743
15700
|
if (ahead > 0) {
|
|
14744
|
-
warn(`You are on ${
|
|
15701
|
+
warn(`You are on ${import_picocolors18.default.bold(baseBranch)} with ${import_picocolors18.default.bold(String(ahead))} local commit${ahead > 1 ? "s" : ""} not in ${import_picocolors18.default.bold(syncSource.ref)}.`);
|
|
14745
15702
|
info(" Syncing will discard those commits. Consider backing them up first (e.g. create a branch).");
|
|
14746
15703
|
const proceed = await confirmPrompt("Discard local commits and sync to remote?");
|
|
14747
15704
|
if (!proceed) {
|
|
@@ -14758,10 +15715,10 @@ var start_default = defineCommand({
|
|
|
14758
15715
|
error(`Failed to create branch: ${result2.stderr}`);
|
|
14759
15716
|
process.exit(1);
|
|
14760
15717
|
}
|
|
14761
|
-
success(`Created ${
|
|
15718
|
+
success(`Created ${import_picocolors18.default.bold(branchName)} from ${import_picocolors18.default.bold(syncSource.ref)}`);
|
|
14762
15719
|
return;
|
|
14763
15720
|
}
|
|
14764
|
-
error(`Failed to update ${
|
|
15721
|
+
error(`Failed to update ${import_picocolors18.default.bold(baseBranch)}: ${updateResult.stderr}`);
|
|
14765
15722
|
info("Make sure your base branch exists locally or the remote ref is available.", "");
|
|
14766
15723
|
process.exit(1);
|
|
14767
15724
|
}
|
|
@@ -14770,12 +15727,13 @@ var start_default = defineCommand({
|
|
|
14770
15727
|
error(`Failed to create branch: ${result.stderr}`);
|
|
14771
15728
|
process.exit(1);
|
|
14772
15729
|
}
|
|
14773
|
-
success(`Created ${
|
|
15730
|
+
success(`Created ${import_picocolors18.default.bold(branchName)} from latest ${import_picocolors18.default.bold(baseBranch)}`);
|
|
14774
15731
|
}
|
|
14775
15732
|
});
|
|
14776
15733
|
|
|
14777
15734
|
// src/commands/status.ts
|
|
14778
|
-
var
|
|
15735
|
+
var import_picocolors19 = __toESM(require_picocolors(), 1);
|
|
15736
|
+
init_gh();
|
|
14779
15737
|
var status_default = defineCommand({
|
|
14780
15738
|
meta: {
|
|
14781
15739
|
name: "status",
|
|
@@ -14792,8 +15750,8 @@ var status_default = defineCommand({
|
|
|
14792
15750
|
process.exit(1);
|
|
14793
15751
|
}
|
|
14794
15752
|
await projectHeading("status", "\uD83D\uDCCA");
|
|
14795
|
-
console.log(` ${
|
|
14796
|
-
console.log(` ${
|
|
15753
|
+
console.log(` ${import_picocolors19.default.dim("Workflow:")} ${import_picocolors19.default.bold(WORKFLOW_DESCRIPTIONS[config.workflow])}`);
|
|
15754
|
+
console.log(` ${import_picocolors19.default.dim("Role:")} ${import_picocolors19.default.bold(config.role)}`);
|
|
14797
15755
|
console.log();
|
|
14798
15756
|
await fetchAll();
|
|
14799
15757
|
const currentBranch = await getCurrentBranch();
|
|
@@ -14802,7 +15760,7 @@ var status_default = defineCommand({
|
|
|
14802
15760
|
const isContributor = config.role === "contributor";
|
|
14803
15761
|
const [dirty, fileStatus] = await Promise.all([hasUncommittedChanges(), getFileStatus()]);
|
|
14804
15762
|
if (dirty) {
|
|
14805
|
-
console.log(` ${
|
|
15763
|
+
console.log(` ${import_picocolors19.default.yellow("\u26A0")} ${import_picocolors19.default.yellow("Uncommitted changes in working tree")}`);
|
|
14806
15764
|
console.log();
|
|
14807
15765
|
}
|
|
14808
15766
|
const mainRemote = `${origin}/${mainBranch}`;
|
|
@@ -14821,16 +15779,16 @@ var status_default = defineCommand({
|
|
|
14821
15779
|
if (isFeatureBranch) {
|
|
14822
15780
|
const branchDiv = await getDivergence(currentBranch, baseBranch);
|
|
14823
15781
|
const branchLine = formatStatus(currentBranch, baseBranch, branchDiv.ahead, branchDiv.behind);
|
|
14824
|
-
console.log(branchLine +
|
|
15782
|
+
console.log(branchLine + import_picocolors19.default.dim(` (current ${import_picocolors19.default.green("*")})`));
|
|
14825
15783
|
branchStatus = await detectBranchStatus(currentBranch, baseBranch);
|
|
14826
15784
|
if (branchStatus.merged) {
|
|
14827
|
-
console.log(` ${
|
|
15785
|
+
console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.green("Branch merged")} \u2014 ${import_picocolors19.default.dim(branchStatus.mergedReason ?? "all commits reachable from base")}`);
|
|
14828
15786
|
}
|
|
14829
15787
|
if (branchStatus.stale) {
|
|
14830
|
-
console.log(` ${
|
|
15788
|
+
console.log(` ${import_picocolors19.default.yellow("\u23F3")} ${import_picocolors19.default.yellow("Branch is stale")} \u2014 ${import_picocolors19.default.dim(`last commit ${branchStatus.staleDaysAgo} days ago`)}`);
|
|
14831
15789
|
}
|
|
14832
15790
|
} else if (currentBranch) {
|
|
14833
|
-
console.log(
|
|
15791
|
+
console.log(import_picocolors19.default.dim(` (on ${import_picocolors19.default.bold(currentBranch)} branch)`));
|
|
14834
15792
|
}
|
|
14835
15793
|
let branchesAligned = true;
|
|
14836
15794
|
{
|
|
@@ -14867,20 +15825,20 @@ var status_default = defineCommand({
|
|
|
14867
15825
|
}
|
|
14868
15826
|
branchesAligned = groups.size === 1;
|
|
14869
15827
|
console.log();
|
|
14870
|
-
console.log(` ${
|
|
15828
|
+
console.log(` ${import_picocolors19.default.bold("\uD83D\uDD17 Branch Alignment")}`);
|
|
14871
15829
|
for (const [hash, names] of groups) {
|
|
14872
15830
|
const short = hash.slice(0, 7);
|
|
14873
|
-
const nameStr = names.map((n2) =>
|
|
14874
|
-
console.log(` ${
|
|
15831
|
+
const nameStr = names.map((n2) => import_picocolors19.default.bold(n2)).join(import_picocolors19.default.dim(" \xB7 "));
|
|
15832
|
+
console.log(` ${import_picocolors19.default.yellow(short)} ${import_picocolors19.default.dim("\u2500\u2500")} ${nameStr}`);
|
|
14875
15833
|
const subject = await getCommitSubject(hash);
|
|
14876
15834
|
if (subject) {
|
|
14877
|
-
console.log(` ${
|
|
15835
|
+
console.log(` ${import_picocolors19.default.dim(subject)}`);
|
|
14878
15836
|
}
|
|
14879
15837
|
}
|
|
14880
15838
|
if (branchesAligned) {
|
|
14881
|
-
console.log(` ${
|
|
15839
|
+
console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.green("All branches aligned")} ${import_picocolors19.default.dim("\u2014 ready to start")}`);
|
|
14882
15840
|
} else {
|
|
14883
|
-
console.log(` ${
|
|
15841
|
+
console.log(` ${import_picocolors19.default.yellow("\u26A0")} ${import_picocolors19.default.yellow("Branches are not fully aligned")}`);
|
|
14884
15842
|
}
|
|
14885
15843
|
}
|
|
14886
15844
|
}
|
|
@@ -14888,41 +15846,41 @@ var status_default = defineCommand({
|
|
|
14888
15846
|
if (hasFiles) {
|
|
14889
15847
|
console.log();
|
|
14890
15848
|
if (fileStatus.staged.length > 0) {
|
|
14891
|
-
console.log(` ${
|
|
15849
|
+
console.log(` ${import_picocolors19.default.green("Staged for commit:")}`);
|
|
14892
15850
|
for (const { file, status } of fileStatus.staged) {
|
|
14893
|
-
console.log(` ${
|
|
15851
|
+
console.log(` ${import_picocolors19.default.green("+")} ${import_picocolors19.default.dim(`${status}:`)} ${file}`);
|
|
14894
15852
|
}
|
|
14895
15853
|
}
|
|
14896
15854
|
if (fileStatus.modified.length > 0) {
|
|
14897
|
-
console.log(` ${
|
|
15855
|
+
console.log(` ${import_picocolors19.default.yellow("Unstaged changes:")}`);
|
|
14898
15856
|
for (const { file, status } of fileStatus.modified) {
|
|
14899
|
-
console.log(` ${
|
|
15857
|
+
console.log(` ${import_picocolors19.default.yellow("~")} ${import_picocolors19.default.dim(`${status}:`)} ${file}`);
|
|
14900
15858
|
}
|
|
14901
15859
|
}
|
|
14902
15860
|
if (fileStatus.untracked.length > 0) {
|
|
14903
|
-
console.log(` ${
|
|
15861
|
+
console.log(` ${import_picocolors19.default.red("Untracked files:")}`);
|
|
14904
15862
|
for (const file of fileStatus.untracked) {
|
|
14905
|
-
console.log(` ${
|
|
15863
|
+
console.log(` ${import_picocolors19.default.red("?")} ${file}`);
|
|
14906
15864
|
}
|
|
14907
15865
|
}
|
|
14908
15866
|
} else if (!dirty) {
|
|
14909
|
-
console.log(` ${
|
|
15867
|
+
console.log(` ${import_picocolors19.default.green("\u2713")} ${import_picocolors19.default.dim("Working tree clean")}`);
|
|
14910
15868
|
}
|
|
14911
15869
|
console.log();
|
|
14912
15870
|
}
|
|
14913
15871
|
});
|
|
14914
15872
|
function formatStatus(branch, base, ahead, behind) {
|
|
14915
|
-
const label =
|
|
15873
|
+
const label = import_picocolors19.default.bold(branch.padEnd(20));
|
|
14916
15874
|
if (ahead === 0 && behind === 0) {
|
|
14917
|
-
return ` ${
|
|
15875
|
+
return ` ${import_picocolors19.default.green("\u2713")} ${label} ${import_picocolors19.default.dim(`in sync with ${base}`)}`;
|
|
14918
15876
|
}
|
|
14919
15877
|
if (ahead > 0 && behind === 0) {
|
|
14920
|
-
return ` ${
|
|
15878
|
+
return ` ${import_picocolors19.default.yellow("\u2191")} ${label} ${import_picocolors19.default.yellow(`${ahead} commit${ahead !== 1 ? "s" : ""} ahead of ${base}`)}`;
|
|
14921
15879
|
}
|
|
14922
15880
|
if (behind > 0 && ahead === 0) {
|
|
14923
|
-
return ` ${
|
|
15881
|
+
return ` ${import_picocolors19.default.red("\u2193")} ${label} ${import_picocolors19.default.red(`${behind} commit${behind !== 1 ? "s" : ""} behind ${base}`)}`;
|
|
14924
15882
|
}
|
|
14925
|
-
return ` ${
|
|
15883
|
+
return ` ${import_picocolors19.default.red("\u26A1")} ${label} ${import_picocolors19.default.yellow(`${ahead} ahead`)}${import_picocolors19.default.dim(", ")}${import_picocolors19.default.red(`${behind} behind`)} ${import_picocolors19.default.dim(base)}`;
|
|
14926
15884
|
}
|
|
14927
15885
|
var STALE_THRESHOLD_DAYS = 14;
|
|
14928
15886
|
async function detectBranchStatus(branch, baseBranch) {
|
|
@@ -14973,15 +15931,16 @@ async function detectBranchStatus(branch, baseBranch) {
|
|
|
14973
15931
|
}
|
|
14974
15932
|
|
|
14975
15933
|
// src/commands/submit.ts
|
|
14976
|
-
var
|
|
15934
|
+
var import_picocolors20 = __toESM(require_picocolors(), 1);
|
|
15935
|
+
init_gh();
|
|
14977
15936
|
async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
14978
|
-
info(`Checking out ${
|
|
15937
|
+
info(`Checking out ${import_picocolors20.default.bold(baseBranch)}...`);
|
|
14979
15938
|
const coResult = await checkoutBranch(baseBranch);
|
|
14980
15939
|
if (coResult.exitCode !== 0) {
|
|
14981
15940
|
error(`Failed to checkout ${baseBranch}: ${coResult.stderr}`);
|
|
14982
15941
|
process.exit(1);
|
|
14983
15942
|
}
|
|
14984
|
-
info(`Squash merging ${
|
|
15943
|
+
info(`Squash merging ${import_picocolors20.default.bold(featureBranch)} into ${import_picocolors20.default.bold(baseBranch)}...`);
|
|
14985
15944
|
const mergeResult = await mergeSquash(featureBranch);
|
|
14986
15945
|
if (mergeResult.exitCode !== 0) {
|
|
14987
15946
|
error(`Squash merge failed: ${mergeResult.stderr}`);
|
|
@@ -15001,7 +15960,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
15001
15960
|
message = aiMsg;
|
|
15002
15961
|
spinner.success("AI commit message generated.");
|
|
15003
15962
|
console.log(`
|
|
15004
|
-
${
|
|
15963
|
+
${import_picocolors20.default.dim("AI suggestion:")} ${import_picocolors20.default.bold(import_picocolors20.default.cyan(message))}`);
|
|
15005
15964
|
break;
|
|
15006
15965
|
}
|
|
15007
15966
|
spinner.fail("AI did not return a commit message.");
|
|
@@ -15041,7 +16000,7 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
15041
16000
|
message = regen;
|
|
15042
16001
|
spinner.success("Commit message regenerated.");
|
|
15043
16002
|
console.log(`
|
|
15044
|
-
${
|
|
16003
|
+
${import_picocolors20.default.dim("AI suggestion:")} ${import_picocolors20.default.bold(import_picocolors20.default.cyan(regen))}`);
|
|
15045
16004
|
} else {
|
|
15046
16005
|
spinner.fail("Regeneration failed.");
|
|
15047
16006
|
}
|
|
@@ -15057,13 +16016,13 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
15057
16016
|
error(`Commit failed: ${commitResult.stderr}`);
|
|
15058
16017
|
process.exit(1);
|
|
15059
16018
|
}
|
|
15060
|
-
info(`Pushing ${
|
|
16019
|
+
info(`Pushing ${import_picocolors20.default.bold(baseBranch)} to ${origin}...`);
|
|
15061
16020
|
const pushResult = await pushBranch(origin, baseBranch);
|
|
15062
16021
|
if (pushResult.exitCode !== 0) {
|
|
15063
16022
|
error(`Failed to push ${baseBranch}: ${pushResult.stderr}`);
|
|
15064
16023
|
process.exit(1);
|
|
15065
16024
|
}
|
|
15066
|
-
info(`Deleting local branch ${
|
|
16025
|
+
info(`Deleting local branch ${import_picocolors20.default.bold(featureBranch)}...`);
|
|
15067
16026
|
const delLocal = await forceDeleteBranch(featureBranch);
|
|
15068
16027
|
if (delLocal.exitCode !== 0) {
|
|
15069
16028
|
warn(`Could not delete local branch: ${delLocal.stderr.trim()}`);
|
|
@@ -15071,14 +16030,14 @@ async function performSquashMerge(origin, baseBranch, featureBranch, options) {
|
|
|
15071
16030
|
const remoteBranchRef = `${origin}/${featureBranch}`;
|
|
15072
16031
|
const remoteExists = await branchExists(remoteBranchRef);
|
|
15073
16032
|
if (remoteExists) {
|
|
15074
|
-
info(`Deleting remote branch ${
|
|
16033
|
+
info(`Deleting remote branch ${import_picocolors20.default.bold(featureBranch)}...`);
|
|
15075
16034
|
const delRemote = await deleteRemoteBranch(origin, featureBranch);
|
|
15076
16035
|
if (delRemote.exitCode !== 0) {
|
|
15077
16036
|
warn(`Could not delete remote branch: ${delRemote.stderr.trim()}`);
|
|
15078
16037
|
}
|
|
15079
16038
|
}
|
|
15080
|
-
success(`Squash merged ${
|
|
15081
|
-
info(`Run ${
|
|
16039
|
+
success(`Squash merged ${import_picocolors20.default.bold(featureBranch)} into ${import_picocolors20.default.bold(baseBranch)} and pushed.`);
|
|
16040
|
+
info(`Run ${import_picocolors20.default.bold("cn start")} to begin a new feature.`, "");
|
|
15082
16041
|
}
|
|
15083
16042
|
var submit_default = defineCommand({
|
|
15084
16043
|
meta: {
|
|
@@ -15135,7 +16094,7 @@ var submit_default = defineCommand({
|
|
|
15135
16094
|
}
|
|
15136
16095
|
if (protectedBranches.includes(currentBranch)) {
|
|
15137
16096
|
await projectHeading("submit", "\uD83D\uDE80");
|
|
15138
|
-
warn(`You're on ${
|
|
16097
|
+
warn(`You're on ${import_picocolors20.default.bold(currentBranch)}, which is a protected branch. PRs should come from feature branches.`);
|
|
15139
16098
|
await fetchAll();
|
|
15140
16099
|
const remoteRef = `${origin}/${currentBranch}`;
|
|
15141
16100
|
const localWork = await hasLocalWork(origin, currentBranch);
|
|
@@ -15144,11 +16103,11 @@ var submit_default = defineCommand({
|
|
|
15144
16103
|
const hasAnything = hasCommits || dirty;
|
|
15145
16104
|
if (!hasAnything) {
|
|
15146
16105
|
error("No local changes or commits to move. Switch to a feature branch first.");
|
|
15147
|
-
info(` Run ${
|
|
16106
|
+
info(` Run ${import_picocolors20.default.bold("cn start")} to create a new feature branch.`, "");
|
|
15148
16107
|
process.exit(1);
|
|
15149
16108
|
}
|
|
15150
16109
|
if (hasCommits) {
|
|
15151
|
-
info(`Found ${
|
|
16110
|
+
info(`Found ${import_picocolors20.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors20.default.bold(currentBranch)}.`);
|
|
15152
16111
|
}
|
|
15153
16112
|
if (dirty) {
|
|
15154
16113
|
info("You also have uncommitted changes in the working tree.");
|
|
@@ -15178,12 +16137,12 @@ var submit_default = defineCommand({
|
|
|
15178
16137
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
15179
16138
|
process.exit(1);
|
|
15180
16139
|
}
|
|
15181
|
-
success(`Created ${
|
|
16140
|
+
success(`Created ${import_picocolors20.default.bold(newBranchName)} with your changes.`);
|
|
15182
16141
|
await updateLocalBranch(currentBranch, remoteRef);
|
|
15183
|
-
info(`Reset ${
|
|
16142
|
+
info(`Reset ${import_picocolors20.default.bold(currentBranch)} back to ${import_picocolors20.default.bold(remoteRef)} \u2014 no damage done.`, "");
|
|
15184
16143
|
console.log();
|
|
15185
|
-
success(`You're now on ${
|
|
15186
|
-
info(`Run ${
|
|
16144
|
+
success(`You're now on ${import_picocolors20.default.bold(newBranchName)} with all your work intact.`);
|
|
16145
|
+
info(`Run ${import_picocolors20.default.bold("cn submit")} again to push and create your PR.`, "");
|
|
15187
16146
|
return;
|
|
15188
16147
|
}
|
|
15189
16148
|
await projectHeading("submit", "\uD83D\uDE80");
|
|
@@ -15192,7 +16151,7 @@ var submit_default = defineCommand({
|
|
|
15192
16151
|
if (ghInstalled && ghAuthed) {
|
|
15193
16152
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
15194
16153
|
if (mergedPR) {
|
|
15195
|
-
warn(`PR #${mergedPR.number} (${
|
|
16154
|
+
warn(`PR #${mergedPR.number} (${import_picocolors20.default.bold(mergedPR.title)}) was already merged.`);
|
|
15196
16155
|
const localWork = await hasLocalWork(origin, currentBranch);
|
|
15197
16156
|
const hasWork = localWork.uncommitted || localWork.unpushedCommits > 0;
|
|
15198
16157
|
if (hasWork) {
|
|
@@ -15200,7 +16159,7 @@ var submit_default = defineCommand({
|
|
|
15200
16159
|
warn("You have uncommitted changes in your working tree.");
|
|
15201
16160
|
}
|
|
15202
16161
|
if (localWork.unpushedCommits > 0) {
|
|
15203
|
-
warn(`You have ${
|
|
16162
|
+
warn(`You have ${import_picocolors20.default.bold(String(localWork.unpushedCommits))} local commit${localWork.unpushedCommits !== 1 ? "s" : ""} not in the merged PR.`);
|
|
15204
16163
|
}
|
|
15205
16164
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
15206
16165
|
const DISCARD = "Discard all changes and clean up";
|
|
@@ -15227,10 +16186,10 @@ var submit_default = defineCommand({
|
|
|
15227
16186
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
15228
16187
|
process.exit(1);
|
|
15229
16188
|
}
|
|
15230
|
-
success(`Renamed ${
|
|
16189
|
+
success(`Renamed ${import_picocolors20.default.bold(currentBranch)} \u2192 ${import_picocolors20.default.bold(newBranchName)}`);
|
|
15231
16190
|
await unsetUpstream();
|
|
15232
16191
|
const syncSource2 = getSyncSource(config);
|
|
15233
|
-
info(`Syncing ${
|
|
16192
|
+
info(`Syncing ${import_picocolors20.default.bold(newBranchName)} with latest ${import_picocolors20.default.bold(baseBranch)}...`);
|
|
15234
16193
|
await fetchRemote(syncSource2.remote);
|
|
15235
16194
|
let rebaseResult;
|
|
15236
16195
|
if (staleUpstreamHash) {
|
|
@@ -15241,17 +16200,17 @@ var submit_default = defineCommand({
|
|
|
15241
16200
|
}
|
|
15242
16201
|
if (rebaseResult.exitCode !== 0) {
|
|
15243
16202
|
warn("Rebase encountered conflicts. Resolve them manually, then run:");
|
|
15244
|
-
info(` ${
|
|
16203
|
+
info(` ${import_picocolors20.default.bold("git rebase --continue")}`, "");
|
|
15245
16204
|
} else {
|
|
15246
|
-
success(`Rebased ${
|
|
16205
|
+
success(`Rebased ${import_picocolors20.default.bold(newBranchName)} onto ${import_picocolors20.default.bold(syncSource2.ref)}.`);
|
|
15247
16206
|
}
|
|
15248
|
-
info(`All your changes are preserved. Run ${
|
|
16207
|
+
info(`All your changes are preserved. Run ${import_picocolors20.default.bold("cn submit")} when ready to create a new PR.`, "");
|
|
15249
16208
|
return;
|
|
15250
16209
|
}
|
|
15251
16210
|
warn("Discarding local changes...");
|
|
15252
16211
|
}
|
|
15253
16212
|
const syncSource = getSyncSource(config);
|
|
15254
|
-
info(`Switching to ${
|
|
16213
|
+
info(`Switching to ${import_picocolors20.default.bold(baseBranch)} and syncing...`);
|
|
15255
16214
|
await fetchRemote(syncSource.remote);
|
|
15256
16215
|
await resetHard("HEAD");
|
|
15257
16216
|
const coResult = await checkoutBranch(baseBranch);
|
|
@@ -15260,23 +16219,23 @@ var submit_default = defineCommand({
|
|
|
15260
16219
|
process.exit(1);
|
|
15261
16220
|
}
|
|
15262
16221
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
15263
|
-
success(`Synced ${
|
|
15264
|
-
info(`Deleting stale branch ${
|
|
16222
|
+
success(`Synced ${import_picocolors20.default.bold(baseBranch)} with ${import_picocolors20.default.bold(syncSource.ref)}.`);
|
|
16223
|
+
info(`Deleting stale branch ${import_picocolors20.default.bold(currentBranch)}...`);
|
|
15265
16224
|
const delResult = await forceDeleteBranch(currentBranch);
|
|
15266
16225
|
if (delResult.exitCode === 0) {
|
|
15267
|
-
success(`Deleted ${
|
|
16226
|
+
success(`Deleted ${import_picocolors20.default.bold(currentBranch)}.`);
|
|
15268
16227
|
} else {
|
|
15269
16228
|
warn(`Could not delete branch: ${delResult.stderr.trim()}`);
|
|
15270
16229
|
}
|
|
15271
16230
|
console.log();
|
|
15272
|
-
info(`You're now on ${
|
|
16231
|
+
info(`You're now on ${import_picocolors20.default.bold(baseBranch)}. Run ${import_picocolors20.default.bold("cn start")} to begin a new feature.`);
|
|
15273
16232
|
return;
|
|
15274
16233
|
}
|
|
15275
16234
|
}
|
|
15276
16235
|
if (ghInstalled && ghAuthed) {
|
|
15277
16236
|
const existingPR = await getPRForBranch(currentBranch);
|
|
15278
16237
|
if (existingPR) {
|
|
15279
|
-
info(`Pushing ${
|
|
16238
|
+
info(`Pushing ${import_picocolors20.default.bold(currentBranch)} to ${origin}...`);
|
|
15280
16239
|
const pushResult2 = await pushSetUpstream(origin, currentBranch);
|
|
15281
16240
|
if (pushResult2.exitCode !== 0) {
|
|
15282
16241
|
error(`Failed to push: ${pushResult2.stderr}`);
|
|
@@ -15287,8 +16246,8 @@ var submit_default = defineCommand({
|
|
|
15287
16246
|
}
|
|
15288
16247
|
process.exit(1);
|
|
15289
16248
|
}
|
|
15290
|
-
success(`Pushed changes to existing PR #${existingPR.number}: ${
|
|
15291
|
-
console.log(` ${
|
|
16249
|
+
success(`Pushed changes to existing PR #${existingPR.number}: ${import_picocolors20.default.bold(existingPR.title)}`);
|
|
16250
|
+
console.log(` ${import_picocolors20.default.cyan(existingPR.url)}`);
|
|
15292
16251
|
return;
|
|
15293
16252
|
}
|
|
15294
16253
|
}
|
|
@@ -15310,10 +16269,10 @@ var submit_default = defineCommand({
|
|
|
15310
16269
|
prBody = result.body;
|
|
15311
16270
|
spinner.success("PR description generated.");
|
|
15312
16271
|
console.log(`
|
|
15313
|
-
${
|
|
16272
|
+
${import_picocolors20.default.dim("AI title:")} ${import_picocolors20.default.bold(import_picocolors20.default.cyan(prTitle))}`);
|
|
15314
16273
|
console.log(`
|
|
15315
|
-
${
|
|
15316
|
-
console.log(
|
|
16274
|
+
${import_picocolors20.default.dim("AI body preview:")}`);
|
|
16275
|
+
console.log(import_picocolors20.default.dim(prBody.slice(0, 300) + (prBody.length > 300 ? "..." : "")));
|
|
15317
16276
|
} else {
|
|
15318
16277
|
spinner.fail("AI did not return a PR description.");
|
|
15319
16278
|
}
|
|
@@ -15424,7 +16383,7 @@ ${import_picocolors19.default.dim("AI body preview:")}`);
|
|
|
15424
16383
|
warn("Submit cancelled.");
|
|
15425
16384
|
return;
|
|
15426
16385
|
}
|
|
15427
|
-
info(`Pushing ${
|
|
16386
|
+
info(`Pushing ${import_picocolors20.default.bold(currentBranch)} to ${origin}...`);
|
|
15428
16387
|
const pushResult = await pushSetUpstream(origin, currentBranch);
|
|
15429
16388
|
if (pushResult.exitCode !== 0) {
|
|
15430
16389
|
error(`Failed to push: ${pushResult.stderr}`);
|
|
@@ -15443,7 +16402,7 @@ ${import_picocolors19.default.dim("AI body preview:")}`);
|
|
|
15443
16402
|
const prUrl = `https://github.com/${repoInfo.owner}/${repoInfo.repo}/compare/${baseBranch}...${currentBranch}?expand=1`;
|
|
15444
16403
|
console.log();
|
|
15445
16404
|
info("Create your PR manually:", "");
|
|
15446
|
-
console.log(` ${
|
|
16405
|
+
console.log(` ${import_picocolors20.default.cyan(prUrl)}`);
|
|
15447
16406
|
} else {
|
|
15448
16407
|
info("gh CLI not available. Create your PR manually on GitHub.", "");
|
|
15449
16408
|
}
|
|
@@ -15477,7 +16436,7 @@ ${import_picocolors19.default.dim("AI body preview:")}`);
|
|
|
15477
16436
|
});
|
|
15478
16437
|
|
|
15479
16438
|
// src/commands/switch.ts
|
|
15480
|
-
var
|
|
16439
|
+
var import_picocolors21 = __toESM(require_picocolors(), 1);
|
|
15481
16440
|
var switch_default = defineCommand({
|
|
15482
16441
|
meta: {
|
|
15483
16442
|
name: "switch",
|
|
@@ -15509,11 +16468,11 @@ var switch_default = defineCommand({
|
|
|
15509
16468
|
const choices = localBranches.filter((b2) => b2.name !== currentBranch).map((b2) => {
|
|
15510
16469
|
const labels = [];
|
|
15511
16470
|
if (protectedBranches.includes(b2.name))
|
|
15512
|
-
labels.push(
|
|
16471
|
+
labels.push(import_picocolors21.default.red("protected"));
|
|
15513
16472
|
if (b2.upstream)
|
|
15514
|
-
labels.push(
|
|
16473
|
+
labels.push(import_picocolors21.default.dim(`\u2192 ${b2.upstream}`));
|
|
15515
16474
|
if (b2.gone)
|
|
15516
|
-
labels.push(
|
|
16475
|
+
labels.push(import_picocolors21.default.red("remote gone"));
|
|
15517
16476
|
const suffix = labels.length > 0 ? ` ${labels.join(" \xB7 ")}` : "";
|
|
15518
16477
|
return `${b2.name}${suffix}`;
|
|
15519
16478
|
});
|
|
@@ -15525,7 +16484,7 @@ var switch_default = defineCommand({
|
|
|
15525
16484
|
targetBranch = selected.split(/\s{2,}/)[0].trim();
|
|
15526
16485
|
}
|
|
15527
16486
|
if (targetBranch === currentBranch) {
|
|
15528
|
-
info(`Already on ${
|
|
16487
|
+
info(`Already on ${import_picocolors21.default.bold(targetBranch)}.`);
|
|
15529
16488
|
return;
|
|
15530
16489
|
}
|
|
15531
16490
|
if (await hasUncommittedChanges()) {
|
|
@@ -15544,7 +16503,7 @@ var switch_default = defineCommand({
|
|
|
15544
16503
|
const stashMsg = `contrib-save: auto-save from ${currentBranch}`;
|
|
15545
16504
|
try {
|
|
15546
16505
|
await exec("git", ["stash", "push", "-m", stashMsg]);
|
|
15547
|
-
info(`Saved changes: ${
|
|
16506
|
+
info(`Saved changes: ${import_picocolors21.default.dim(stashMsg)}`);
|
|
15548
16507
|
} catch {
|
|
15549
16508
|
error("Failed to save changes. Please commit or save manually.");
|
|
15550
16509
|
process.exit(1);
|
|
@@ -15560,9 +16519,9 @@ var switch_default = defineCommand({
|
|
|
15560
16519
|
}
|
|
15561
16520
|
process.exit(1);
|
|
15562
16521
|
}
|
|
15563
|
-
success(`Switched to ${
|
|
15564
|
-
info(`Your changes from ${
|
|
15565
|
-
info(`Use ${
|
|
16522
|
+
success(`Switched to ${import_picocolors21.default.bold(targetBranch)}`);
|
|
16523
|
+
info(`Your changes from ${import_picocolors21.default.bold(currentBranch ?? "previous branch")} are saved.`, "");
|
|
16524
|
+
info(`Use ${import_picocolors21.default.bold("cn save --restore")} to bring them back.`, "");
|
|
15566
16525
|
return;
|
|
15567
16526
|
}
|
|
15568
16527
|
const result = await checkoutBranch(targetBranch);
|
|
@@ -15570,12 +16529,12 @@ var switch_default = defineCommand({
|
|
|
15570
16529
|
error(`Failed to switch to ${targetBranch}: ${result.stderr}`);
|
|
15571
16530
|
process.exit(1);
|
|
15572
16531
|
}
|
|
15573
|
-
success(`Switched to ${
|
|
16532
|
+
success(`Switched to ${import_picocolors21.default.bold(targetBranch)}`);
|
|
15574
16533
|
}
|
|
15575
16534
|
});
|
|
15576
16535
|
|
|
15577
16536
|
// src/commands/sync.ts
|
|
15578
|
-
var
|
|
16537
|
+
var import_picocolors22 = __toESM(require_picocolors(), 1);
|
|
15579
16538
|
var sync_default = defineCommand({
|
|
15580
16539
|
meta: {
|
|
15581
16540
|
name: "sync",
|
|
@@ -15627,24 +16586,24 @@ var sync_default = defineCommand({
|
|
|
15627
16586
|
await fetchRemote(origin);
|
|
15628
16587
|
}
|
|
15629
16588
|
if (!await refExists(syncSource.ref)) {
|
|
15630
|
-
error(`Remote ref ${
|
|
16589
|
+
error(`Remote ref ${import_picocolors22.default.bold(syncSource.ref)} does not exist.`);
|
|
15631
16590
|
info("This can happen if the branch was renamed or deleted on the remote.", "");
|
|
15632
|
-
info(`Check your config: the base branch may need updating via ${
|
|
16591
|
+
info(`Check your config: the base branch may need updating via ${import_picocolors22.default.bold("cn setup")}.`, "");
|
|
15633
16592
|
process.exit(1);
|
|
15634
16593
|
}
|
|
15635
16594
|
let allowMergeCommit = false;
|
|
15636
16595
|
const div = await getDivergence(baseBranch, syncSource.ref);
|
|
15637
16596
|
if (div.ahead > 0 || div.behind > 0) {
|
|
15638
|
-
info(`${
|
|
16597
|
+
info(`${import_picocolors22.default.bold(baseBranch)} is ${import_picocolors22.default.yellow(`${div.ahead} ahead`)} and ${import_picocolors22.default.red(`${div.behind} behind`)} ${syncSource.ref}`);
|
|
15639
16598
|
} else {
|
|
15640
|
-
info(`${
|
|
16599
|
+
info(`${import_picocolors22.default.bold(baseBranch)} is already in sync with ${syncSource.ref}`);
|
|
15641
16600
|
}
|
|
15642
16601
|
if (div.ahead > 0) {
|
|
15643
16602
|
const currentBranch = await getCurrentBranch();
|
|
15644
16603
|
const protectedBranches = getProtectedBranches(config);
|
|
15645
16604
|
const isOnProtected = currentBranch && protectedBranches.includes(currentBranch);
|
|
15646
16605
|
if (isOnProtected) {
|
|
15647
|
-
warn(`You have ${
|
|
16606
|
+
warn(`You have ${import_picocolors22.default.bold(String(div.ahead))} local commit${div.ahead !== 1 ? "s" : ""} on ${import_picocolors22.default.bold(baseBranch)} that aren't on the remote.`);
|
|
15648
16607
|
info("Pulling now could create a merge commit, which breaks clean history.");
|
|
15649
16608
|
console.log();
|
|
15650
16609
|
const MOVE_BRANCH = "Move my commits to a new feature branch, then sync";
|
|
@@ -15674,7 +16633,7 @@ var sync_default = defineCommand({
|
|
|
15674
16633
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
15675
16634
|
process.exit(1);
|
|
15676
16635
|
}
|
|
15677
|
-
success(`Created ${
|
|
16636
|
+
success(`Created ${import_picocolors22.default.bold(newBranchName)} with your commits.`);
|
|
15678
16637
|
const coResult2 = await checkoutBranch(baseBranch);
|
|
15679
16638
|
if (coResult2.exitCode !== 0) {
|
|
15680
16639
|
error(`Failed to checkout ${baseBranch}: ${coResult2.stderr}`);
|
|
@@ -15682,11 +16641,11 @@ var sync_default = defineCommand({
|
|
|
15682
16641
|
}
|
|
15683
16642
|
const remoteRef = syncSource.ref;
|
|
15684
16643
|
await updateLocalBranch(baseBranch, remoteRef);
|
|
15685
|
-
success(`Reset ${
|
|
15686
|
-
success(`${
|
|
16644
|
+
success(`Reset ${import_picocolors22.default.bold(baseBranch)} to ${import_picocolors22.default.bold(remoteRef)}.`);
|
|
16645
|
+
success(`${import_picocolors22.default.bold(baseBranch)} is now in sync with ${syncSource.ref}`);
|
|
15687
16646
|
console.log();
|
|
15688
|
-
info(`Your commits are safe on ${
|
|
15689
|
-
info(`Run ${
|
|
16647
|
+
info(`Your commits are safe on ${import_picocolors22.default.bold(newBranchName)}.`, "");
|
|
16648
|
+
info(`Run ${import_picocolors22.default.bold(`git checkout ${newBranchName}`)} then ${import_picocolors22.default.bold("cn update")} to rebase onto the synced ${import_picocolors22.default.bold(baseBranch)}.`, "");
|
|
15690
16649
|
return;
|
|
15691
16650
|
}
|
|
15692
16651
|
allowMergeCommit = true;
|
|
@@ -15694,7 +16653,7 @@ var sync_default = defineCommand({
|
|
|
15694
16653
|
}
|
|
15695
16654
|
}
|
|
15696
16655
|
if (!args.yes) {
|
|
15697
|
-
const ok = await confirmPrompt(`This will pull ${
|
|
16656
|
+
const ok = await confirmPrompt(`This will pull ${import_picocolors22.default.bold(syncSource.ref)} into local ${import_picocolors22.default.bold(baseBranch)}.`);
|
|
15698
16657
|
if (!ok)
|
|
15699
16658
|
process.exit(0);
|
|
15700
16659
|
}
|
|
@@ -15708,8 +16667,8 @@ var sync_default = defineCommand({
|
|
|
15708
16667
|
if (allowMergeCommit) {
|
|
15709
16668
|
error(`Pull failed: ${pullResult.stderr.trim()}`);
|
|
15710
16669
|
} else {
|
|
15711
|
-
error(`Fast-forward pull failed. Your local ${
|
|
15712
|
-
info(`Use ${
|
|
16670
|
+
error(`Fast-forward pull failed. Your local ${import_picocolors22.default.bold(baseBranch)} may have diverged.`);
|
|
16671
|
+
info(`Use ${import_picocolors22.default.bold("cn sync")} again and choose "Move my commits to a new feature branch" to fix this.`, "");
|
|
15713
16672
|
}
|
|
15714
16673
|
process.exit(1);
|
|
15715
16674
|
}
|
|
@@ -15717,7 +16676,7 @@ var sync_default = defineCommand({
|
|
|
15717
16676
|
if (hasDevBranch(workflow) && role === "maintainer") {
|
|
15718
16677
|
const mainDiv = await getDivergence(config.mainBranch, `${origin}/${config.mainBranch}`);
|
|
15719
16678
|
if (mainDiv.behind > 0) {
|
|
15720
|
-
info(`Also syncing ${
|
|
16679
|
+
info(`Also syncing ${import_picocolors22.default.bold(config.mainBranch)}...`);
|
|
15721
16680
|
const mainCoResult = await checkoutBranch(config.mainBranch);
|
|
15722
16681
|
if (mainCoResult.exitCode === 0) {
|
|
15723
16682
|
const mainPullResult = await pullFastForwardOnly(origin, config.mainBranch);
|
|
@@ -15758,20 +16717,20 @@ var sync_default = defineCommand({
|
|
|
15758
16717
|
}
|
|
15759
16718
|
}
|
|
15760
16719
|
console.log();
|
|
15761
|
-
console.log(` ${
|
|
16720
|
+
console.log(` ${import_picocolors22.default.bold("\uD83D\uDD17 Branch Alignment")}`);
|
|
15762
16721
|
for (const [hash, names] of groups) {
|
|
15763
16722
|
const short = hash.slice(0, 7);
|
|
15764
|
-
const nameStr = names.map((n2) =>
|
|
15765
|
-
console.log(` ${
|
|
16723
|
+
const nameStr = names.map((n2) => import_picocolors22.default.bold(n2)).join(import_picocolors22.default.dim(" \xB7 "));
|
|
16724
|
+
console.log(` ${import_picocolors22.default.yellow(short)} ${import_picocolors22.default.dim("\u2500\u2500")} ${nameStr}`);
|
|
15766
16725
|
const subject = await getCommitSubject(hash);
|
|
15767
16726
|
if (subject) {
|
|
15768
|
-
console.log(` ${
|
|
16727
|
+
console.log(` ${import_picocolors22.default.dim(subject)}`);
|
|
15769
16728
|
}
|
|
15770
16729
|
}
|
|
15771
16730
|
if (groups.size === 1) {
|
|
15772
|
-
console.log(` ${
|
|
16731
|
+
console.log(` ${import_picocolors22.default.green("\u2713")} ${import_picocolors22.default.green("All branches aligned")} ${import_picocolors22.default.dim("\u2014 ready to start")}`);
|
|
15773
16732
|
} else {
|
|
15774
|
-
console.log(` ${
|
|
16733
|
+
console.log(` ${import_picocolors22.default.yellow("\u26A0")} ${import_picocolors22.default.yellow("Branches are not fully aligned")}`);
|
|
15775
16734
|
}
|
|
15776
16735
|
}
|
|
15777
16736
|
}
|
|
@@ -15779,8 +16738,9 @@ var sync_default = defineCommand({
|
|
|
15779
16738
|
});
|
|
15780
16739
|
|
|
15781
16740
|
// src/commands/update.ts
|
|
15782
|
-
import { readFileSync as
|
|
15783
|
-
var
|
|
16741
|
+
import { readFileSync as readFileSync7 } from "fs";
|
|
16742
|
+
var import_picocolors23 = __toESM(require_picocolors(), 1);
|
|
16743
|
+
init_gh();
|
|
15784
16744
|
function hasStaleBranchWorkToPreserve(uniqueCommitsAheadOfBase, hasUncommittedChanges2) {
|
|
15785
16745
|
return hasUncommittedChanges2 || uniqueCommitsAheadOfBase > 0;
|
|
15786
16746
|
}
|
|
@@ -15821,7 +16781,7 @@ var update_default = defineCommand({
|
|
|
15821
16781
|
}
|
|
15822
16782
|
if (protectedBranches.includes(currentBranch)) {
|
|
15823
16783
|
await projectHeading("update", "\uD83D\uDD03");
|
|
15824
|
-
warn(`You're on ${
|
|
16784
|
+
warn(`You're on ${import_picocolors23.default.bold(currentBranch)}, which is a protected branch. Updates (rebase) apply to feature branches.`);
|
|
15825
16785
|
await fetchAll();
|
|
15826
16786
|
const { origin } = config;
|
|
15827
16787
|
const remoteRef = `${origin}/${currentBranch}`;
|
|
@@ -15830,12 +16790,12 @@ var update_default = defineCommand({
|
|
|
15830
16790
|
const hasCommits = localWork.unpushedCommits > 0;
|
|
15831
16791
|
const hasAnything = hasCommits || dirty;
|
|
15832
16792
|
if (!hasAnything) {
|
|
15833
|
-
info(`No local changes found on ${
|
|
15834
|
-
info(`Use ${
|
|
16793
|
+
info(`No local changes found on ${import_picocolors23.default.bold(currentBranch)}.`);
|
|
16794
|
+
info(`Use ${import_picocolors23.default.bold("cn sync")} to sync protected branches, or ${import_picocolors23.default.bold("cn start")} to create a feature branch.`);
|
|
15835
16795
|
process.exit(1);
|
|
15836
16796
|
}
|
|
15837
16797
|
if (hasCommits) {
|
|
15838
|
-
info(`Found ${
|
|
16798
|
+
info(`Found ${import_picocolors23.default.bold(String(localWork.unpushedCommits))} unpushed commit${localWork.unpushedCommits !== 1 ? "s" : ""} on ${import_picocolors23.default.bold(currentBranch)}.`);
|
|
15839
16799
|
}
|
|
15840
16800
|
if (dirty) {
|
|
15841
16801
|
info("You also have uncommitted changes in the working tree.");
|
|
@@ -15865,12 +16825,12 @@ var update_default = defineCommand({
|
|
|
15865
16825
|
error(`Failed to create branch: ${branchResult.stderr}`);
|
|
15866
16826
|
process.exit(1);
|
|
15867
16827
|
}
|
|
15868
|
-
success(`Created ${
|
|
16828
|
+
success(`Created ${import_picocolors23.default.bold(newBranchName)} with your changes.`);
|
|
15869
16829
|
await updateLocalBranch(currentBranch, remoteRef);
|
|
15870
|
-
info(`Reset ${
|
|
16830
|
+
info(`Reset ${import_picocolors23.default.bold(currentBranch)} back to ${import_picocolors23.default.bold(remoteRef)} \u2014 no damage done.`, "");
|
|
15871
16831
|
console.log();
|
|
15872
|
-
success(`You're now on ${
|
|
15873
|
-
info(`Run ${
|
|
16832
|
+
success(`You're now on ${import_picocolors23.default.bold(newBranchName)} with all your work intact.`);
|
|
16833
|
+
info(`Run ${import_picocolors23.default.bold("cn update")} again to rebase onto latest ${import_picocolors23.default.bold(baseBranch)}.`, "");
|
|
15874
16834
|
return;
|
|
15875
16835
|
}
|
|
15876
16836
|
if (await hasUncommittedChanges()) {
|
|
@@ -15880,8 +16840,8 @@ var update_default = defineCommand({
|
|
|
15880
16840
|
await projectHeading("update", "\uD83D\uDD03");
|
|
15881
16841
|
const mergedPR = await getMergedPRForBranch(currentBranch);
|
|
15882
16842
|
if (mergedPR) {
|
|
15883
|
-
warn(`PR #${mergedPR.number} (${
|
|
15884
|
-
info(`Link: ${
|
|
16843
|
+
warn(`PR #${mergedPR.number} (${import_picocolors23.default.bold(mergedPR.title)}) has already been merged.`);
|
|
16844
|
+
info(`Link: ${import_picocolors23.default.underline(mergedPR.url)}`, "");
|
|
15885
16845
|
const uniqueCommitsAheadOfBase = await countCommitsAhead(currentBranch, syncSource.ref);
|
|
15886
16846
|
const dirty = await hasUncommittedChanges();
|
|
15887
16847
|
const hasWork = hasStaleBranchWorkToPreserve(uniqueCommitsAheadOfBase, dirty);
|
|
@@ -15890,12 +16850,12 @@ var update_default = defineCommand({
|
|
|
15890
16850
|
info("You have uncommitted local changes.");
|
|
15891
16851
|
}
|
|
15892
16852
|
if (uniqueCommitsAheadOfBase > 0) {
|
|
15893
|
-
info(`You have ${uniqueCommitsAheadOfBase} local commit(s) not in ${
|
|
16853
|
+
info(`You have ${uniqueCommitsAheadOfBase} local commit(s) not in ${import_picocolors23.default.bold(syncSource.ref)}.`);
|
|
15894
16854
|
}
|
|
15895
16855
|
const SAVE_NEW_BRANCH = "Save changes to a new branch";
|
|
15896
16856
|
const DISCARD = "Discard all changes and clean up";
|
|
15897
16857
|
const CANCEL = "Cancel";
|
|
15898
|
-
const action = await selectPrompt(`${
|
|
16858
|
+
const action = await selectPrompt(`${import_picocolors23.default.bold(currentBranch)} is stale but has local work. What would you like to do?`, [SAVE_NEW_BRANCH, DISCARD, CANCEL]);
|
|
15899
16859
|
if (action === CANCEL) {
|
|
15900
16860
|
info("No changes made. You are still on your current branch.");
|
|
15901
16861
|
return;
|
|
@@ -15917,7 +16877,7 @@ var update_default = defineCommand({
|
|
|
15917
16877
|
error(`Failed to rename branch: ${renameResult.stderr}`);
|
|
15918
16878
|
process.exit(1);
|
|
15919
16879
|
}
|
|
15920
|
-
success(`Renamed ${
|
|
16880
|
+
success(`Renamed ${import_picocolors23.default.bold(currentBranch)} \u2192 ${import_picocolors23.default.bold(newBranchName)}`);
|
|
15921
16881
|
await unsetUpstream();
|
|
15922
16882
|
await fetchRemote(syncSource.remote);
|
|
15923
16883
|
let rebaseResult2;
|
|
@@ -15929,11 +16889,11 @@ var update_default = defineCommand({
|
|
|
15929
16889
|
}
|
|
15930
16890
|
if (rebaseResult2.exitCode !== 0) {
|
|
15931
16891
|
warn("Rebase encountered conflicts. Resolve them manually, then run:");
|
|
15932
|
-
info(` ${
|
|
16892
|
+
info(` ${import_picocolors23.default.bold("git rebase --continue")}`, "");
|
|
15933
16893
|
} else {
|
|
15934
|
-
success(`Rebased ${
|
|
16894
|
+
success(`Rebased ${import_picocolors23.default.bold(newBranchName)} onto ${import_picocolors23.default.bold(syncSource.ref)}.`);
|
|
15935
16895
|
}
|
|
15936
|
-
info(`All your changes are preserved. Run ${
|
|
16896
|
+
info(`All your changes are preserved. Run ${import_picocolors23.default.bold("cn submit")} when ready to create a new PR.`, "");
|
|
15937
16897
|
return;
|
|
15938
16898
|
}
|
|
15939
16899
|
warn("Discarding local changes...");
|
|
@@ -15952,24 +16912,24 @@ var update_default = defineCommand({
|
|
|
15952
16912
|
process.exit(1);
|
|
15953
16913
|
}
|
|
15954
16914
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
15955
|
-
success(`Synced ${
|
|
15956
|
-
info(`Deleting stale branch ${
|
|
16915
|
+
success(`Synced ${import_picocolors23.default.bold(baseBranch)} with ${import_picocolors23.default.bold(syncSource.ref)}.`);
|
|
16916
|
+
info(`Deleting stale branch ${import_picocolors23.default.bold(currentBranch)}...`);
|
|
15957
16917
|
await forceDeleteBranch(currentBranch);
|
|
15958
|
-
success(`Deleted ${
|
|
15959
|
-
info(`Run ${
|
|
16918
|
+
success(`Deleted ${import_picocolors23.default.bold(currentBranch)}.`);
|
|
16919
|
+
info(`Run ${import_picocolors23.default.bold("cn start")} to begin a new feature branch.`, "");
|
|
15960
16920
|
return;
|
|
15961
16921
|
}
|
|
15962
|
-
info(`Updating ${
|
|
16922
|
+
info(`Updating ${import_picocolors23.default.bold(currentBranch)} with latest ${import_picocolors23.default.bold(baseBranch)}...`);
|
|
15963
16923
|
await fetchRemote(syncSource.remote);
|
|
15964
16924
|
if (!await refExists(syncSource.ref)) {
|
|
15965
|
-
error(`Remote ref ${
|
|
16925
|
+
error(`Remote ref ${import_picocolors23.default.bold(syncSource.ref)} does not exist.`);
|
|
15966
16926
|
error("Run `git fetch --all` and verify your remote configuration.");
|
|
15967
16927
|
process.exit(1);
|
|
15968
16928
|
}
|
|
15969
16929
|
await updateLocalBranch(baseBranch, syncSource.ref);
|
|
15970
16930
|
const rebaseStrategy = await determineRebaseStrategy(currentBranch, syncSource.ref);
|
|
15971
16931
|
if (rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase) {
|
|
15972
|
-
info(
|
|
16932
|
+
info(import_picocolors23.default.dim(`Using --onto rebase (branch was based on a different ref)`));
|
|
15973
16933
|
}
|
|
15974
16934
|
const rebaseResult = rebaseStrategy.strategy === "onto" && rebaseStrategy.ontoOldBase ? await rebaseOnto(syncSource.ref, rebaseStrategy.ontoOldBase) : await rebase(syncSource.ref);
|
|
15975
16935
|
if (rebaseResult.exitCode !== 0) {
|
|
@@ -15983,7 +16943,7 @@ var update_default = defineCommand({
|
|
|
15983
16943
|
let conflictDiff = "";
|
|
15984
16944
|
for (const file of conflictFiles.slice(0, 3)) {
|
|
15985
16945
|
try {
|
|
15986
|
-
const content =
|
|
16946
|
+
const content = readFileSync7(file, "utf-8");
|
|
15987
16947
|
if (content.includes("<<<<<<<")) {
|
|
15988
16948
|
conflictDiff += `
|
|
15989
16949
|
--- ${file} ---
|
|
@@ -16000,10 +16960,10 @@ ${content.slice(0, 2000)}
|
|
|
16000
16960
|
if (suggestion) {
|
|
16001
16961
|
spinner.success("AI conflict guidance ready.");
|
|
16002
16962
|
console.log(`
|
|
16003
|
-
${
|
|
16004
|
-
console.log(
|
|
16963
|
+
${import_picocolors23.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance:")}`);
|
|
16964
|
+
console.log(import_picocolors23.default.dim("\u2500".repeat(60)));
|
|
16005
16965
|
console.log(suggestion);
|
|
16006
|
-
console.log(
|
|
16966
|
+
console.log(import_picocolors23.default.dim("\u2500".repeat(60)));
|
|
16007
16967
|
console.log();
|
|
16008
16968
|
} else {
|
|
16009
16969
|
spinner.fail("AI could not analyze the conflicts.");
|
|
@@ -16011,21 +16971,21 @@ ${import_picocolors22.default.bold("\uD83D\uDCA1 AI Conflict Resolution Guidance
|
|
|
16011
16971
|
}
|
|
16012
16972
|
}
|
|
16013
16973
|
}
|
|
16014
|
-
console.log(
|
|
16974
|
+
console.log(import_picocolors23.default.bold("To resolve:"));
|
|
16015
16975
|
console.log(` 1. Fix conflicts in the affected files`);
|
|
16016
|
-
console.log(` 2. ${
|
|
16017
|
-
console.log(` 3. ${
|
|
16976
|
+
console.log(` 2. ${import_picocolors23.default.cyan("git add <resolved-files>")}`);
|
|
16977
|
+
console.log(` 3. ${import_picocolors23.default.cyan("git rebase --continue")}`);
|
|
16018
16978
|
console.log();
|
|
16019
|
-
console.log(` Or abort: ${
|
|
16979
|
+
console.log(` Or abort: ${import_picocolors23.default.cyan("git rebase --abort")}`);
|
|
16020
16980
|
process.exit(1);
|
|
16021
16981
|
}
|
|
16022
|
-
success(`${
|
|
16982
|
+
success(`${import_picocolors23.default.bold(currentBranch)} has been rebased onto latest ${import_picocolors23.default.bold(baseBranch)}`);
|
|
16023
16983
|
}
|
|
16024
16984
|
});
|
|
16025
16985
|
|
|
16026
16986
|
// src/commands/validate.ts
|
|
16027
|
-
import { readFileSync as
|
|
16028
|
-
var
|
|
16987
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
16988
|
+
var import_picocolors24 = __toESM(require_picocolors(), 1);
|
|
16029
16989
|
var validate_default = defineCommand({
|
|
16030
16990
|
meta: {
|
|
16031
16991
|
name: "validate",
|
|
@@ -16054,7 +17014,7 @@ var validate_default = defineCommand({
|
|
|
16054
17014
|
info('Commit convention is set to "none". All messages are accepted.');
|
|
16055
17015
|
process.exit(0);
|
|
16056
17016
|
}
|
|
16057
|
-
const message = args.file ?
|
|
17017
|
+
const message = args.file ? readFileSync8(args.file, "utf-8").split(/\r?\n/, 1)[0] ?? "" : args.message;
|
|
16058
17018
|
if (!message) {
|
|
16059
17019
|
error("No commit message provided. Pass a message or use --file <path>.");
|
|
16060
17020
|
process.exit(1);
|
|
@@ -16065,14 +17025,14 @@ var validate_default = defineCommand({
|
|
|
16065
17025
|
}
|
|
16066
17026
|
const errors = getValidationError(convention);
|
|
16067
17027
|
for (const line of errors) {
|
|
16068
|
-
console.error(
|
|
17028
|
+
console.error(import_picocolors24.default.red(` \u2717 ${line}`));
|
|
16069
17029
|
}
|
|
16070
17030
|
process.exit(1);
|
|
16071
17031
|
}
|
|
16072
17032
|
});
|
|
16073
17033
|
|
|
16074
17034
|
// src/ui/banner.ts
|
|
16075
|
-
var
|
|
17035
|
+
var import_picocolors25 = __toESM(require_picocolors(), 1);
|
|
16076
17036
|
|
|
16077
17037
|
// src/data/announcements.json
|
|
16078
17038
|
var announcements_default = [
|
|
@@ -16133,9 +17093,9 @@ function getAuthor() {
|
|
|
16133
17093
|
return typeof package_default.author === "string" ? package_default.author : "unknown";
|
|
16134
17094
|
}
|
|
16135
17095
|
function showBanner(variant = "small") {
|
|
16136
|
-
console.log(
|
|
17096
|
+
console.log(import_picocolors25.default.cyan(`
|
|
16137
17097
|
${LOGO}`));
|
|
16138
|
-
console.log(` ${
|
|
17098
|
+
console.log(` ${import_picocolors25.default.dim(`v${getVersion()}`)} ${import_picocolors25.default.dim("\u2014")} ${import_picocolors25.default.dim(`Built by ${getAuthor()}`)}`);
|
|
16139
17099
|
const announcements = getActiveAnnouncements();
|
|
16140
17100
|
if (announcements.length > 0) {
|
|
16141
17101
|
console.log();
|
|
@@ -16144,27 +17104,27 @@ ${LOGO}`));
|
|
|
16144
17104
|
if (variant === "big") {
|
|
16145
17105
|
const panelLines = [
|
|
16146
17106
|
{
|
|
16147
|
-
label:
|
|
17107
|
+
label: import_picocolors25.default.bold(import_picocolors25.default.cyan("Getting Started")),
|
|
16148
17108
|
rawLabel: "Getting Started",
|
|
16149
17109
|
value: "",
|
|
16150
17110
|
rawValue: ""
|
|
16151
17111
|
},
|
|
16152
17112
|
{
|
|
16153
|
-
label:
|
|
17113
|
+
label: import_picocolors25.default.cyan("cn setup"),
|
|
16154
17114
|
rawLabel: "cn setup",
|
|
16155
|
-
value:
|
|
17115
|
+
value: import_picocolors25.default.dim("configure workflow, remotes, and defaults"),
|
|
16156
17116
|
rawValue: "configure workflow, remotes, and defaults"
|
|
16157
17117
|
},
|
|
16158
17118
|
{
|
|
16159
|
-
label:
|
|
17119
|
+
label: import_picocolors25.default.cyan("cn doctor"),
|
|
16160
17120
|
rawLabel: "cn doctor",
|
|
16161
|
-
value:
|
|
17121
|
+
value: import_picocolors25.default.dim("verify your environment before doing any work"),
|
|
16162
17122
|
rawValue: "verify your environment before doing any work"
|
|
16163
17123
|
},
|
|
16164
17124
|
{
|
|
16165
|
-
label:
|
|
17125
|
+
label: import_picocolors25.default.cyan("cn start"),
|
|
16166
17126
|
rawLabel: "cn start",
|
|
16167
|
-
value:
|
|
17127
|
+
value: import_picocolors25.default.dim("create a branch and begin the next task"),
|
|
16168
17128
|
rawValue: "create a branch and begin the next task"
|
|
16169
17129
|
},
|
|
16170
17130
|
{
|
|
@@ -16174,13 +17134,13 @@ ${LOGO}`));
|
|
|
16174
17134
|
rawValue: ""
|
|
16175
17135
|
},
|
|
16176
17136
|
{
|
|
16177
|
-
label:
|
|
17137
|
+
label: import_picocolors25.default.bold(import_picocolors25.default.cyan("Workflow")),
|
|
16178
17138
|
rawLabel: "Workflow",
|
|
16179
17139
|
value: "",
|
|
16180
17140
|
rawValue: ""
|
|
16181
17141
|
},
|
|
16182
17142
|
{
|
|
16183
|
-
label:
|
|
17143
|
+
label: import_picocolors25.default.dim("cn setup \u2192 cn commit \u2192 cn update \u2192 cn submit"),
|
|
16184
17144
|
rawLabel: "cn setup \u2192 cn commit \u2192 cn update \u2192 cn submit",
|
|
16185
17145
|
value: "",
|
|
16186
17146
|
rawValue: ""
|
|
@@ -16205,22 +17165,22 @@ ${LOGO}`));
|
|
|
16205
17165
|
return Math.max(max, lineLength);
|
|
16206
17166
|
}, 0));
|
|
16207
17167
|
console.log();
|
|
16208
|
-
console.log(` ${
|
|
17168
|
+
console.log(` ${import_picocolors25.default.dim(`\u250C${"\u2500".repeat(contentWidth + 2)}\u2510`)}`);
|
|
16209
17169
|
for (const line of rows) {
|
|
16210
17170
|
if (!line.rawLabel && !line.rawValue) {
|
|
16211
|
-
console.log(` ${
|
|
17171
|
+
console.log(` ${import_picocolors25.default.dim("\u2502")} ${" ".repeat(contentWidth)} ${import_picocolors25.default.dim("\u2502")}`);
|
|
16212
17172
|
continue;
|
|
16213
17173
|
}
|
|
16214
17174
|
const left = line.rawValue ? `${line.label}${" ".repeat(Math.max(0, labelWidth - line.rawLabel.length + 2))}` : line.label;
|
|
16215
|
-
const value = line.rawValue ?
|
|
17175
|
+
const value = line.rawValue ? import_picocolors25.default.dim(line.rawValue) : "";
|
|
16216
17176
|
const rawLength = line.rawValue ? labelWidth + 2 + line.rawValue.length : line.rawLabel.length;
|
|
16217
17177
|
const trailing = " ".repeat(Math.max(0, contentWidth - rawLength));
|
|
16218
|
-
console.log(` ${
|
|
17178
|
+
console.log(` ${import_picocolors25.default.dim("\u2502")} ${left}${value}${trailing} ${import_picocolors25.default.dim("\u2502")}`);
|
|
16219
17179
|
}
|
|
16220
|
-
console.log(` ${
|
|
17180
|
+
console.log(` ${import_picocolors25.default.dim(`\u2514${"\u2500".repeat(contentWidth + 2)}\u2518`)}`);
|
|
16221
17181
|
console.log();
|
|
16222
|
-
console.log(` ${
|
|
16223
|
-
console.log(` ${
|
|
17182
|
+
console.log(` ${import_picocolors25.default.dim("Star or contribute:")} ${import_picocolors25.default.dim(linkify("gh.waren.build/contribute-now", "https://gh.waren.build/contribute-now"))}`);
|
|
17183
|
+
console.log(` ${import_picocolors25.default.dim("Sponsor:")} ${import_picocolors25.default.dim(linkify("warengonzaga.com/sponsor", "https://warengonzaga.com/sponsor"))}`);
|
|
16224
17184
|
}
|
|
16225
17185
|
console.log();
|
|
16226
17186
|
}
|
|
@@ -16252,7 +17212,7 @@ function renderAnnouncementBanner(announcement) {
|
|
|
16252
17212
|
console.log(` ${tone.border(`\u250C${"\u2500".repeat(rawWidth + 2)}\u2510`)}`);
|
|
16253
17213
|
for (const line of lines) {
|
|
16254
17214
|
const trailing = " ".repeat(Math.max(0, rawWidth - line.length));
|
|
16255
|
-
const content = line === title ? tone.title(line) :
|
|
17215
|
+
const content = line === title ? tone.title(line) : import_picocolors25.default.dim(line);
|
|
16256
17216
|
console.log(` ${tone.border("\u2502")} ${content}${trailing} ${tone.border("\u2502")}`);
|
|
16257
17217
|
}
|
|
16258
17218
|
console.log(` ${tone.border(`\u2514${"\u2500".repeat(rawWidth + 2)}\u2518`)}`);
|
|
@@ -16288,20 +17248,20 @@ function getAnnouncementTone(kind) {
|
|
|
16288
17248
|
case "info":
|
|
16289
17249
|
return {
|
|
16290
17250
|
emoji: "\u2139",
|
|
16291
|
-
border:
|
|
16292
|
-
title: (value) =>
|
|
17251
|
+
border: import_picocolors25.default.blue,
|
|
17252
|
+
title: (value) => import_picocolors25.default.bold(import_picocolors25.default.blue(value))
|
|
16293
17253
|
};
|
|
16294
17254
|
case "warning":
|
|
16295
17255
|
return {
|
|
16296
17256
|
emoji: "\uD83D\uDEA8",
|
|
16297
|
-
border:
|
|
16298
|
-
title: (value) =>
|
|
17257
|
+
border: import_picocolors25.default.red,
|
|
17258
|
+
title: (value) => import_picocolors25.default.bold(import_picocolors25.default.red(value))
|
|
16299
17259
|
};
|
|
16300
17260
|
default:
|
|
16301
17261
|
return {
|
|
16302
17262
|
emoji: "\u26A0",
|
|
16303
|
-
border:
|
|
16304
|
-
title: (value) =>
|
|
17263
|
+
border: import_picocolors25.default.yellow,
|
|
17264
|
+
title: (value) => import_picocolors25.default.bold(import_picocolors25.default.yellow(value))
|
|
16305
17265
|
};
|
|
16306
17266
|
}
|
|
16307
17267
|
}
|
|
@@ -16340,7 +17300,8 @@ if (!isVersion) {
|
|
|
16340
17300
|
"branch",
|
|
16341
17301
|
"hook",
|
|
16342
17302
|
"validate",
|
|
16343
|
-
"doctor"
|
|
17303
|
+
"doctor",
|
|
17304
|
+
"label"
|
|
16344
17305
|
];
|
|
16345
17306
|
const isHelp = process.argv.includes("--help") || process.argv.includes("-h");
|
|
16346
17307
|
const hasSubCommand = subCommands.some((cmd) => process.argv.includes(cmd));
|
|
@@ -16377,7 +17338,8 @@ var main = defineCommand({
|
|
|
16377
17338
|
log: log_default,
|
|
16378
17339
|
hook: hook_default,
|
|
16379
17340
|
validate: validate_default,
|
|
16380
|
-
doctor: doctor_default
|
|
17341
|
+
doctor: doctor_default,
|
|
17342
|
+
label: label_default
|
|
16381
17343
|
},
|
|
16382
17344
|
run({ args }) {
|
|
16383
17345
|
if (args.version) {
|