@theholocron/cli 3.17.1 → 3.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.mjs +91 -14
- package/dist/cli.mjs.map +1 -1
- package/package.json +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -970,7 +970,10 @@ function patchFiles(dir, variants, description, homepage, runtimeEnvironment, pr
|
|
|
970
970
|
if (isBinary(content)) continue;
|
|
971
971
|
const original = content;
|
|
972
972
|
for (const [search, replacement] of variants) content = content.split(search).join(replacement);
|
|
973
|
-
if (description !== void 0)
|
|
973
|
+
if (description !== void 0) {
|
|
974
|
+
content = content.split("<description>").join(description);
|
|
975
|
+
content = content.replace(/(<!-- holocron:description -->)[\s\S]*?(<!-- \/holocron:description -->)/g, `$1\n${description}\n$2`);
|
|
976
|
+
}
|
|
974
977
|
if (homepage !== void 0) content = content.split("<homepage>").join(homepage);
|
|
975
978
|
if (runtimeEnvironment !== void 0) content = content.split("<runtime_environment>").join(runtimeEnvironment);
|
|
976
979
|
content = content.replace(/\n?<!-- holocron:template-only -->[\s\S]*?<!-- \/holocron:template-only -->\n?/g, "");
|
|
@@ -989,9 +992,24 @@ function preflight$1() {
|
|
|
989
992
|
function defaultExec$3(cmd, args, opts) {
|
|
990
993
|
execFileSync(cmd, args, {
|
|
991
994
|
cwd: opts.cwd,
|
|
992
|
-
stdio: opts.stdio
|
|
995
|
+
stdio: opts.stdio,
|
|
996
|
+
...opts.env && Object.keys(opts.env).length > 0 ? { env: {
|
|
997
|
+
...process.env,
|
|
998
|
+
...opts.env
|
|
999
|
+
} } : {}
|
|
993
1000
|
});
|
|
994
1001
|
}
|
|
1002
|
+
function keychainLookup(key) {
|
|
1003
|
+
const result = spawnSync("security", [
|
|
1004
|
+
"find-generic-password",
|
|
1005
|
+
"-s",
|
|
1006
|
+
"com.theholocron.cli",
|
|
1007
|
+
"-a",
|
|
1008
|
+
key,
|
|
1009
|
+
"-w"
|
|
1010
|
+
], { encoding: "utf8" });
|
|
1011
|
+
return result.status === 0 ? result.stdout?.trim() || void 0 : void 0;
|
|
1012
|
+
}
|
|
995
1013
|
function defaultReadFile(filepath) {
|
|
996
1014
|
return readFileSync(filepath, "utf-8");
|
|
997
1015
|
}
|
|
@@ -1023,13 +1041,14 @@ async function runNew(input) {
|
|
|
1023
1041
|
}
|
|
1024
1042
|
if (existsSync(repoDir)) throw new NewError(`\`${repoDir}\` already exists — delete it or pick a different name.`);
|
|
1025
1043
|
print(` Creating ${newRepo} from template ${templateRepo}…`);
|
|
1044
|
+
const visibility = input.openSource ? "--public" : "--private";
|
|
1026
1045
|
try {
|
|
1027
1046
|
execFn("gh", [
|
|
1028
1047
|
"repo",
|
|
1029
1048
|
"create",
|
|
1030
1049
|
newRepo,
|
|
1031
1050
|
`--template=${templateRepo}`,
|
|
1032
|
-
|
|
1051
|
+
visibility,
|
|
1033
1052
|
"--clone"
|
|
1034
1053
|
], {
|
|
1035
1054
|
cwd,
|
|
@@ -1038,6 +1057,28 @@ async function runNew(input) {
|
|
|
1038
1057
|
} catch (err) {
|
|
1039
1058
|
throw new NewError(`gh repo create failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
1040
1059
|
}
|
|
1060
|
+
if (input.isTemplate) try {
|
|
1061
|
+
execFn("gh", [
|
|
1062
|
+
"repo",
|
|
1063
|
+
"edit",
|
|
1064
|
+
newRepo,
|
|
1065
|
+
"--template=true"
|
|
1066
|
+
], {
|
|
1067
|
+
cwd,
|
|
1068
|
+
stdio: "inherit"
|
|
1069
|
+
});
|
|
1070
|
+
} catch {}
|
|
1071
|
+
try {
|
|
1072
|
+
execFn("gh", [
|
|
1073
|
+
"repo",
|
|
1074
|
+
"edit",
|
|
1075
|
+
newRepo,
|
|
1076
|
+
`--visibility=${input.openSource ? "public" : "private"}`
|
|
1077
|
+
], {
|
|
1078
|
+
cwd,
|
|
1079
|
+
stdio: "inherit"
|
|
1080
|
+
});
|
|
1081
|
+
} catch {}
|
|
1041
1082
|
let templateSlug = `${input.type}-template`;
|
|
1042
1083
|
const pkgJsonPath = path.join(repoDir, "package.json");
|
|
1043
1084
|
if (existsSync(pkgJsonPath)) try {
|
|
@@ -1048,6 +1089,8 @@ async function runNew(input) {
|
|
|
1048
1089
|
print(` Patching files…`);
|
|
1049
1090
|
const variants = deriveVariants(templateSlug, input.name);
|
|
1050
1091
|
variants.unshift([`theholocron/${templateSlug}`, `${org}/${input.name}`]);
|
|
1092
|
+
const displayName = input.name.split("-").map(cap).join(" ");
|
|
1093
|
+
variants.push(["<display_name>", displayName]);
|
|
1051
1094
|
const filesPatched = patchFiles(repoDir, variants, input.description, input.homepage, input.runtimeEnvironment, print, readFn, writeFn, walkFn);
|
|
1052
1095
|
print(` ${filesPatched.length} file${filesPatched.length === 1 ? "" : "s"} patched`);
|
|
1053
1096
|
if (input.description !== void 0 || input.homepage !== void 0) try {
|
|
@@ -1110,9 +1153,22 @@ async function runNew(input) {
|
|
|
1110
1153
|
print("");
|
|
1111
1154
|
print(" Running holocron setup…");
|
|
1112
1155
|
try {
|
|
1113
|
-
|
|
1156
|
+
const setupArgs = ["setup"];
|
|
1157
|
+
const setupEnv = {};
|
|
1158
|
+
const adminToken = input.token ?? keychainLookup("github.admin");
|
|
1159
|
+
if (adminToken) setupArgs.push("--token", adminToken);
|
|
1160
|
+
if (!process.env["HOLOCRON_ORG_TOKEN"]) {
|
|
1161
|
+
const orgToken = keychainLookup("github.org");
|
|
1162
|
+
if (orgToken) setupEnv["HOLOCRON_ORG_TOKEN"] = orgToken;
|
|
1163
|
+
}
|
|
1164
|
+
if (!process.env["HOLOCRON_DEPLOY_TOKEN"]) {
|
|
1165
|
+
const deployToken = keychainLookup("github.deploy");
|
|
1166
|
+
if (deployToken) setupEnv["HOLOCRON_DEPLOY_TOKEN"] = deployToken;
|
|
1167
|
+
}
|
|
1168
|
+
execFn("holocron", setupArgs, {
|
|
1114
1169
|
cwd: repoDir,
|
|
1115
|
-
stdio: "inherit"
|
|
1170
|
+
stdio: "inherit",
|
|
1171
|
+
env: setupEnv
|
|
1116
1172
|
});
|
|
1117
1173
|
} catch {
|
|
1118
1174
|
print(" ✗ holocron setup failed — run it manually after checking your config");
|
|
@@ -3144,6 +3200,10 @@ async function runSetup(input) {
|
|
|
3144
3200
|
if (loader.has("source")) {
|
|
3145
3201
|
const source = loader.get("source");
|
|
3146
3202
|
print(style.step("source"));
|
|
3203
|
+
const SECURITY_SKIP_CODES = {
|
|
3204
|
+
enableSecretScanning: [422],
|
|
3205
|
+
enablePrivateVulnerabilityReporting: [404]
|
|
3206
|
+
};
|
|
3147
3207
|
for (const method of [
|
|
3148
3208
|
"enableVulnerabilityAlerts",
|
|
3149
3209
|
"enableAutomatedSecurityFixes",
|
|
@@ -3153,7 +3213,7 @@ async function runSetup(input) {
|
|
|
3153
3213
|
]) {
|
|
3154
3214
|
steps.push(await runStep("source", method, dryRun, async () => {
|
|
3155
3215
|
await source[method]();
|
|
3156
|
-
}));
|
|
3216
|
+
}, { skipCodes: SECURITY_SKIP_CODES[method] }));
|
|
3157
3217
|
print(formatStep(steps[steps.length - 1]));
|
|
3158
3218
|
}
|
|
3159
3219
|
const usesAdvancedCodeQL = (config.workflows ?? []).map((e) => typeof e === "string" ? e : e.name).includes("codeql");
|
|
@@ -3287,7 +3347,7 @@ async function runSetup(input) {
|
|
|
3287
3347
|
}
|
|
3288
3348
|
const teams = repo?.teams ?? [];
|
|
3289
3349
|
if (teams.length > 0) if (source.syncTeams) {
|
|
3290
|
-
steps.push(await runStep("source", "sync teams", dryRun, () => source.syncTeams(teams)));
|
|
3350
|
+
steps.push(await runStep("source", "sync teams", dryRun, () => source.syncTeams(teams), { skipCodes: [422] }));
|
|
3291
3351
|
print(formatStep(steps[steps.length - 1]));
|
|
3292
3352
|
const repoCoord = input.context.repo ?? repo?.name ?? "";
|
|
3293
3353
|
const org = repoCoord.includes("/") ? repoCoord.split("/")[0] : "";
|
|
@@ -3654,7 +3714,7 @@ async function updateSkillsGitignore(gitignorePath, existingContent, skills, sym
|
|
|
3654
3714
|
} else content = (existingContent.trimEnd() ? existingContent.trimEnd() + "\n\n" : "") + block + "\n";
|
|
3655
3715
|
await writeFile(gitignorePath, content, "utf8");
|
|
3656
3716
|
}
|
|
3657
|
-
async function runStep(capability, step, dryRun, body) {
|
|
3717
|
+
async function runStep(capability, step, dryRun, body, opts = {}) {
|
|
3658
3718
|
if (dryRun) return {
|
|
3659
3719
|
capability,
|
|
3660
3720
|
step,
|
|
@@ -3670,15 +3730,23 @@ async function runStep(capability, step, dryRun, body) {
|
|
|
3670
3730
|
if (typeof note === "string") result.message = note;
|
|
3671
3731
|
return result;
|
|
3672
3732
|
} catch (err) {
|
|
3673
|
-
if (err instanceof ProviderApiError$1
|
|
3674
|
-
|
|
3675
|
-
return {
|
|
3733
|
+
if (err instanceof ProviderApiError$1) {
|
|
3734
|
+
if (err.status !== void 0 && opts.skipCodes?.includes(err.status)) return {
|
|
3676
3735
|
capability,
|
|
3677
3736
|
step,
|
|
3678
|
-
status: "
|
|
3679
|
-
message: err.message
|
|
3680
|
-
reason
|
|
3737
|
+
status: "skip",
|
|
3738
|
+
message: err.message
|
|
3681
3739
|
};
|
|
3740
|
+
if (err.status === 403) {
|
|
3741
|
+
const reason = classify403(err);
|
|
3742
|
+
return {
|
|
3743
|
+
capability,
|
|
3744
|
+
step,
|
|
3745
|
+
status: reason === "plan" ? "skip" : "fail",
|
|
3746
|
+
message: err.message,
|
|
3747
|
+
reason
|
|
3748
|
+
};
|
|
3749
|
+
}
|
|
3682
3750
|
}
|
|
3683
3751
|
return {
|
|
3684
3752
|
capability,
|
|
@@ -5385,6 +5453,9 @@ try {
|
|
|
5385
5453
|
}).option("skills", {
|
|
5386
5454
|
type: "string",
|
|
5387
5455
|
describe: "Comma-separated agent skill names (e.g. git-safety,pr-workflow)"
|
|
5456
|
+
}).option("is-template", {
|
|
5457
|
+
type: "boolean",
|
|
5458
|
+
describe: "Mark the new repo as a GitHub template repository"
|
|
5388
5459
|
}).option("org", {
|
|
5389
5460
|
type: "string",
|
|
5390
5461
|
default: "theholocron",
|
|
@@ -5395,6 +5466,9 @@ try {
|
|
|
5395
5466
|
describe: "Run pnpm install + holocron setup after bootstrapping (--no-verify skips)"
|
|
5396
5467
|
}), async (argv) => {
|
|
5397
5468
|
try {
|
|
5469
|
+
const tokens = tokenContext(argv.token);
|
|
5470
|
+
if (!tokens) return;
|
|
5471
|
+
const adminToken = tokens.cliTokens?.["github"] ?? tokens.cliToken;
|
|
5398
5472
|
let type = argv.type;
|
|
5399
5473
|
let name = argv.name;
|
|
5400
5474
|
let description = argv.description;
|
|
@@ -5410,6 +5484,7 @@ try {
|
|
|
5410
5484
|
let openSource = argv.openSource;
|
|
5411
5485
|
let usesExternalPackages = argv.usesExternalPackages;
|
|
5412
5486
|
let skills = parseTopics(argv.skills);
|
|
5487
|
+
const isTemplate = argv.isTemplate;
|
|
5413
5488
|
if (!type) type = await select({
|
|
5414
5489
|
message: "Template type:",
|
|
5415
5490
|
choices: [
|
|
@@ -5589,7 +5664,9 @@ try {
|
|
|
5589
5664
|
usesExternalPackages: usesExternalPackages ?? true,
|
|
5590
5665
|
topics,
|
|
5591
5666
|
skills,
|
|
5667
|
+
isTemplate,
|
|
5592
5668
|
org: argv.org,
|
|
5669
|
+
token: adminToken,
|
|
5593
5670
|
dryRun: argv.dryRun,
|
|
5594
5671
|
noVerify: !argv.verify,
|
|
5595
5672
|
cwd: argv.cwd
|