@theholocron/cli 3.55.1 → 3.57.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 +129 -67
- package/dist/cli.mjs.map +1 -1
- package/package.json +3 -3
package/dist/cli.mjs
CHANGED
|
@@ -13,8 +13,8 @@ import ora from "ora";
|
|
|
13
13
|
import chalk from "chalk";
|
|
14
14
|
import { execFile, execFileSync, spawnSync } from "node:child_process";
|
|
15
15
|
import { homedir } from "node:os";
|
|
16
|
-
import { access, copyFile, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
17
16
|
import { createLogger, parseLogLevel } from "@theholocron/logger";
|
|
17
|
+
import { access, copyFile, mkdir, readFile, readdir, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
|
|
18
18
|
import { createHash } from "node:crypto";
|
|
19
19
|
import { generateReadme } from "@theholocron/components-doc/markdown";
|
|
20
20
|
import { getClients, getConfigs, getDocs, getPlugins, getSkills, getThemes, getUtils } from "@theholocron/registry-doc";
|
|
@@ -896,6 +896,66 @@ async function runDeploy(input) {
|
|
|
896
896
|
}
|
|
897
897
|
}
|
|
898
898
|
//#endregion
|
|
899
|
+
//#region src/logger.ts
|
|
900
|
+
/**
|
|
901
|
+
* CLI-side wiring for `@theholocron/logger`.
|
|
902
|
+
*
|
|
903
|
+
* `logger` is the operational-output channel — internal state, debug
|
|
904
|
+
* traces, errors, structured context that routes to Axiom. It runs in
|
|
905
|
+
* parallel to `print` (user-facing UX output) and does not replace it.
|
|
906
|
+
*/
|
|
907
|
+
/**
|
|
908
|
+
* Resolve the explicit level to hand to `createLogger`, in priority order:
|
|
909
|
+
*
|
|
910
|
+
* 1. `--verbose` → `"debug"` 2. `--quiet` → `"error"`
|
|
911
|
+
* 3. `HOLOCRON_LOG_LEVEL` env var
|
|
912
|
+
* 4. `holocron.config` `log.level` (`configLevel`)
|
|
913
|
+
*
|
|
914
|
+
* Returns `undefined` when nothing applies — `createLogger` then defaults
|
|
915
|
+
* to `"info"`. Resolving the full chain here (rather than passing
|
|
916
|
+
* `configLevel` straight through) keeps config below the env var.
|
|
917
|
+
*/
|
|
918
|
+
function resolveLogLevel(argv, configLevel) {
|
|
919
|
+
if (argv.verbose) return "debug";
|
|
920
|
+
if (argv.quiet) return "error";
|
|
921
|
+
return parseLogLevel(env.get("HOLOCRON_LOG_LEVEL")) ?? configLevel;
|
|
922
|
+
}
|
|
923
|
+
let root;
|
|
924
|
+
let rootLevel;
|
|
925
|
+
let rootCommand;
|
|
926
|
+
/**
|
|
927
|
+
* The process-wide root logger. Built once (from `cli.ts`'s middleware,
|
|
928
|
+
* with the command name + flags + env). Rebuilt at most once more when a
|
|
929
|
+
* command's handler supplies its `holocron.config` `log.level` — a case
|
|
930
|
+
* the flag/env-only first pass could not have known — as long as no
|
|
931
|
+
* higher-priority `--verbose` / `--quiet` already fixed the level. That
|
|
932
|
+
* rebuild generates a fresh `runId`, which is harmless: nothing logs
|
|
933
|
+
* between the middleware and the handler.
|
|
934
|
+
*/
|
|
935
|
+
function buildCliLogger(argv, opts = {}) {
|
|
936
|
+
const { command, configLevel } = opts;
|
|
937
|
+
if (command) rootCommand = command;
|
|
938
|
+
const level = resolveLogLevel(argv, configLevel);
|
|
939
|
+
const rebuildForConfig = configLevel !== void 0 && level !== rootLevel && !argv.verbose && !argv.quiet;
|
|
940
|
+
if (!root || rebuildForConfig) {
|
|
941
|
+
const built = createLogger(level ? { level } : {});
|
|
942
|
+
root = {
|
|
943
|
+
logger: rootCommand ? built.logger.child({ command: rootCommand }) : built.logger,
|
|
944
|
+
runId: built.runId
|
|
945
|
+
};
|
|
946
|
+
rootLevel = level;
|
|
947
|
+
}
|
|
948
|
+
return root;
|
|
949
|
+
}
|
|
950
|
+
/** Lazily-memoized `Logger` for module-level call sites with no `argv` in scope. */
|
|
951
|
+
function getLogger() {
|
|
952
|
+
return (root ??= createLogger()).logger;
|
|
953
|
+
}
|
|
954
|
+
/** The current root logger's correlation id, if a root has been built. */
|
|
955
|
+
function getRunId() {
|
|
956
|
+
return root?.runId;
|
|
957
|
+
}
|
|
958
|
+
//#endregion
|
|
899
959
|
//#region src/commands/doctor.ts
|
|
900
960
|
async function runDoctor(input) {
|
|
901
961
|
const print = input.print ?? ((line) => console.log(line));
|
|
@@ -924,7 +984,14 @@ async function runDoctor(input) {
|
|
|
924
984
|
rows.push(row);
|
|
925
985
|
}
|
|
926
986
|
}
|
|
987
|
+
const log = getLogger();
|
|
927
988
|
for (const row of rows) {
|
|
989
|
+
log[row.status === "fail" ? "warn" : "info"]({
|
|
990
|
+
capability: row.capability,
|
|
991
|
+
provider: row.provider,
|
|
992
|
+
status: row.status,
|
|
993
|
+
detail: row.message
|
|
994
|
+
}, `doctor: ${row.capability}`);
|
|
928
995
|
const label = `${pad(row.capability, 14)} via ${pad(row.provider, 14)} ${row.message}`;
|
|
929
996
|
if (row.status === "ok") print(` ${style.success(label)}`);
|
|
930
997
|
else if (row.status === "fail") print(` ${style.fail(label)}`);
|
|
@@ -3112,59 +3179,6 @@ var dependabot_default = "version: 2\nupdates:\n - package-ecosystem: npm\n
|
|
|
3112
3179
|
//#region src/templates/labeler.yml
|
|
3113
3180
|
var labeler_default = "bug:\n - '^fix'\n\nchore:\n - '^chore(?!\\(deps)'\n\nci:\n - '^ci'\n\ndependencies:\n - '^chore\\(deps'\n\ndocumentation:\n - '^docs'\n\nenhancement:\n - '^feat'\n\nperformance:\n - '^perf'\n\nrefactor:\n - '^refactor'\n\ntest:\n - '^test'\n";
|
|
3114
3181
|
//#endregion
|
|
3115
|
-
//#region src/logger.ts
|
|
3116
|
-
/**
|
|
3117
|
-
* CLI-side wiring for `@theholocron/logger`.
|
|
3118
|
-
*
|
|
3119
|
-
* `logger` is the operational-output channel — internal state, debug
|
|
3120
|
-
* traces, errors, structured context that routes to Axiom. It runs in
|
|
3121
|
-
* parallel to `print` (user-facing UX output) and does not replace it.
|
|
3122
|
-
*/
|
|
3123
|
-
/**
|
|
3124
|
-
* Resolve the explicit level to hand to `createLogger`, in priority order:
|
|
3125
|
-
*
|
|
3126
|
-
* 1. `--verbose` → `"debug"` 2. `--quiet` → `"error"`
|
|
3127
|
-
* 3. `HOLOCRON_LOG_LEVEL` env var
|
|
3128
|
-
* 4. `holocron.config` `log.level` (`configLevel`)
|
|
3129
|
-
*
|
|
3130
|
-
* Returns `undefined` when nothing applies — `createLogger` then defaults
|
|
3131
|
-
* to `"info"`. Resolving the full chain here (rather than passing
|
|
3132
|
-
* `configLevel` straight through) keeps config below the env var.
|
|
3133
|
-
*/
|
|
3134
|
-
function resolveLogLevel(argv, configLevel) {
|
|
3135
|
-
if (argv.verbose) return "debug";
|
|
3136
|
-
if (argv.quiet) return "error";
|
|
3137
|
-
return parseLogLevel(env.get("HOLOCRON_LOG_LEVEL")) ?? configLevel;
|
|
3138
|
-
}
|
|
3139
|
-
let root;
|
|
3140
|
-
let rootLevel;
|
|
3141
|
-
/**
|
|
3142
|
-
* The process-wide root logger. Built once (from `cli.ts`'s middleware,
|
|
3143
|
-
* with flags + env only). Rebuilt at most once more when a command's
|
|
3144
|
-
* handler supplies its `holocron.config` `log.level` — a case the
|
|
3145
|
-
* flag/env-only first pass could not have known — as long as no
|
|
3146
|
-
* higher-priority `--verbose` / `--quiet` already fixed the level. That
|
|
3147
|
-
* rebuild generates a fresh `runId`, which is harmless: nothing logs
|
|
3148
|
-
* between the middleware and the handler.
|
|
3149
|
-
*/
|
|
3150
|
-
function buildCliLogger(argv, configLevel) {
|
|
3151
|
-
const level = resolveLogLevel(argv, configLevel);
|
|
3152
|
-
const rebuildForConfig = configLevel !== void 0 && level !== rootLevel && !argv.verbose && !argv.quiet;
|
|
3153
|
-
if (!root || rebuildForConfig) {
|
|
3154
|
-
root = createLogger(level ? { level } : {});
|
|
3155
|
-
rootLevel = level;
|
|
3156
|
-
}
|
|
3157
|
-
return root;
|
|
3158
|
-
}
|
|
3159
|
-
/** Lazily-memoized `Logger` for module-level call sites with no `argv` in scope. */
|
|
3160
|
-
function getLogger() {
|
|
3161
|
-
return (root ??= createLogger()).logger;
|
|
3162
|
-
}
|
|
3163
|
-
/** The current root logger's correlation id, if a root has been built. */
|
|
3164
|
-
function getRunId() {
|
|
3165
|
-
return root?.runId;
|
|
3166
|
-
}
|
|
3167
|
-
//#endregion
|
|
3168
3182
|
//#region src/commands/setup-workflows/index.ts
|
|
3169
3183
|
/**
|
|
3170
3184
|
* Thin workflow wrapper templates for `holocron setup`.
|
|
@@ -3989,6 +4003,18 @@ const BALANCED_REPO_SETTINGS = {
|
|
|
3989
4003
|
//#endregion
|
|
3990
4004
|
//#region src/commands/setup/run-step.ts
|
|
3991
4005
|
async function runStep(capability, step, dryRun, body, opts = {}) {
|
|
4006
|
+
const result = await execStep(capability, step, dryRun, body, opts);
|
|
4007
|
+
const { status, message, reason } = result;
|
|
4008
|
+
getLogger()[status === "fail" ? "warn" : "info"]({
|
|
4009
|
+
capability,
|
|
4010
|
+
step,
|
|
4011
|
+
status,
|
|
4012
|
+
...message ? { detail: message } : {},
|
|
4013
|
+
...reason ? { reason } : {}
|
|
4014
|
+
}, `${capability}.${step}`);
|
|
4015
|
+
return result;
|
|
4016
|
+
}
|
|
4017
|
+
async function execStep(capability, step, dryRun, body, opts = {}) {
|
|
3992
4018
|
if (dryRun) return {
|
|
3993
4019
|
capability,
|
|
3994
4020
|
step,
|
|
@@ -4629,6 +4655,29 @@ async function runSetup(input) {
|
|
|
4629
4655
|
}
|
|
4630
4656
|
print(formatStep(steps[steps.length - 1]));
|
|
4631
4657
|
}
|
|
4658
|
+
if (loader.has("errors")) {
|
|
4659
|
+
const errors = loader.get("errors");
|
|
4660
|
+
print(style.step("errors"));
|
|
4661
|
+
if (errors.ensureProject) {
|
|
4662
|
+
let dsn;
|
|
4663
|
+
steps.push(await runStep("errors", `ensureProject ${config.name}`, dryRun, async () => {
|
|
4664
|
+
const result = await errors.ensureProject({ name: config.name });
|
|
4665
|
+
dsn = result.dsn;
|
|
4666
|
+
return `project ${result.alreadyExists ? "exists" : "created"}`;
|
|
4667
|
+
}));
|
|
4668
|
+
print(formatStep(steps[steps.length - 1]));
|
|
4669
|
+
if (dsn && loader.has("secrets")) {
|
|
4670
|
+
const secrets = loader.get("secrets");
|
|
4671
|
+
const { envKeys } = await errors.describe();
|
|
4672
|
+
for (const key of envKeys) {
|
|
4673
|
+
steps.push(await runStep("errors", `secrets set ${key}`, dryRun, async () => {
|
|
4674
|
+
await secrets.setSecret({ kind: "repo" }, key, dsn);
|
|
4675
|
+
}));
|
|
4676
|
+
print(formatStep(steps[steps.length - 1]));
|
|
4677
|
+
}
|
|
4678
|
+
}
|
|
4679
|
+
}
|
|
4680
|
+
}
|
|
4632
4681
|
if (loader.has("logs")) {
|
|
4633
4682
|
const logs = loader.get("logs");
|
|
4634
4683
|
print(style.step("logs"));
|
|
@@ -6164,14 +6213,26 @@ async function fileExists(path) {
|
|
|
6164
6213
|
}
|
|
6165
6214
|
//#endregion
|
|
6166
6215
|
//#region src/telemetry.ts
|
|
6167
|
-
const
|
|
6216
|
+
const FALLBACK_DSN = "https://95cbb72ad5636c94e119a5405ee8f55f@o4508238154104832.ingest.us.sentry.io/4511810950791168";
|
|
6217
|
+
/**
|
|
6218
|
+
* Resolve the Sentry DSN — the `errors` capability's self-contained
|
|
6219
|
+
* activation, per ADR-0007:
|
|
6220
|
+
*
|
|
6221
|
+
* HOLOCRON_SENTRY_DSN → SENTRY_DSN → built-in fallback
|
|
6222
|
+
*
|
|
6223
|
+
* A consumer repo with `SENTRY_DSN` set gets CLI errors routed to its own
|
|
6224
|
+
* project with no config. Opt out entirely with `HOLOCRON_TELEMETRY=false`.
|
|
6225
|
+
*/
|
|
6226
|
+
function resolveDsn() {
|
|
6227
|
+
return env.get("HOLOCRON_SENTRY_DSN") ?? env.get("SENTRY_DSN") ?? FALLBACK_DSN;
|
|
6228
|
+
}
|
|
6168
6229
|
function isEnabled() {
|
|
6169
|
-
return !(env.get("HOLOCRON_TELEMETRY") === "false" || Boolean(env.get("NO_HOLOCRON_TELEMETRY"))) &&
|
|
6230
|
+
return !(env.get("HOLOCRON_TELEMETRY") === "false" || Boolean(env.get("NO_HOLOCRON_TELEMETRY"))) && resolveDsn() !== "";
|
|
6170
6231
|
}
|
|
6171
6232
|
function init(version) {
|
|
6172
6233
|
if (!isEnabled()) return;
|
|
6173
6234
|
Sentry.init({
|
|
6174
|
-
dsn:
|
|
6235
|
+
dsn: resolveDsn(),
|
|
6175
6236
|
release: `holocron@${version}`,
|
|
6176
6237
|
environment: env.get("CI") ? "ci" : "local",
|
|
6177
6238
|
tracesSampleRate: 1,
|
|
@@ -6394,9 +6455,10 @@ try {
|
|
|
6394
6455
|
default: false,
|
|
6395
6456
|
describe: "Set the log level to error — suppress info and warn."
|
|
6396
6457
|
}).middleware((argv) => {
|
|
6397
|
-
|
|
6458
|
+
const name = argv._.slice(0, 2).join(" ") || "unknown";
|
|
6459
|
+
finishCommand = startCommand(name);
|
|
6398
6460
|
printRunId = Boolean(argv.debug || argv.verbose);
|
|
6399
|
-
buildCliLogger(argv);
|
|
6461
|
+
buildCliLogger(argv, { command: name });
|
|
6400
6462
|
}).command("version", "Print the CLI version", () => {}, () => {
|
|
6401
6463
|
console.log(`holocron ${CLI_VERSION}`);
|
|
6402
6464
|
}).command("clone", "Clone all repos in a GitHub org as siblings under a single directory", (y) => y.option("org", {
|
|
@@ -6430,7 +6492,7 @@ try {
|
|
|
6430
6492
|
const tokens = tokenContext(argv.token);
|
|
6431
6493
|
if (!tokens) return;
|
|
6432
6494
|
const loaded = await loadConfig(argv.cwd);
|
|
6433
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6495
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6434
6496
|
if ((await runDoctor({
|
|
6435
6497
|
loaded,
|
|
6436
6498
|
context: {
|
|
@@ -6448,7 +6510,7 @@ try {
|
|
|
6448
6510
|
const tokens = tokenContext(argv.token);
|
|
6449
6511
|
if (!tokens) return;
|
|
6450
6512
|
const loaded = await loadConfig(argv.cwd);
|
|
6451
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6513
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6452
6514
|
if ((await runSetup({
|
|
6453
6515
|
loaded,
|
|
6454
6516
|
context: {
|
|
@@ -6514,7 +6576,7 @@ try {
|
|
|
6514
6576
|
const scopeArg = argv.scope;
|
|
6515
6577
|
const scope = parseScope(scopeArg);
|
|
6516
6578
|
const loaded = await loadConfig(argv.cwd);
|
|
6517
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6579
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6518
6580
|
if ((await runSecretSet({
|
|
6519
6581
|
loaded,
|
|
6520
6582
|
context: {
|
|
@@ -6544,7 +6606,7 @@ try {
|
|
|
6544
6606
|
const tokens = tokenContext(argv.token);
|
|
6545
6607
|
if (!tokens) return;
|
|
6546
6608
|
const loaded = await loadConfig(argv.cwd);
|
|
6547
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6609
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6548
6610
|
if ((await runSecretsSync({
|
|
6549
6611
|
loaded,
|
|
6550
6612
|
context: {
|
|
@@ -6573,7 +6635,7 @@ try {
|
|
|
6573
6635
|
const tokens = tokenContext(argv.token);
|
|
6574
6636
|
if (!tokens) return;
|
|
6575
6637
|
const loaded = await loadConfig(argv.cwd);
|
|
6576
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6638
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6577
6639
|
if ((await runDeploy({
|
|
6578
6640
|
loaded,
|
|
6579
6641
|
context: {
|
|
@@ -6601,7 +6663,7 @@ try {
|
|
|
6601
6663
|
const tokens = tokenContext(argv.token);
|
|
6602
6664
|
if (!tokens) return;
|
|
6603
6665
|
const loaded = await loadConfig(argv.cwd);
|
|
6604
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6666
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6605
6667
|
if ((await runCleanupPreview({
|
|
6606
6668
|
loaded,
|
|
6607
6669
|
context: {
|
|
@@ -6649,7 +6711,7 @@ try {
|
|
|
6649
6711
|
const tokens = tokenContext(argv.token);
|
|
6650
6712
|
if (!tokens) return;
|
|
6651
6713
|
const loaded = await loadConfig(argv.cwd);
|
|
6652
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6714
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6653
6715
|
if ((await runSync({
|
|
6654
6716
|
loaded,
|
|
6655
6717
|
context: {
|
|
@@ -6706,7 +6768,7 @@ try {
|
|
|
6706
6768
|
default: false
|
|
6707
6769
|
}), async (argv) => {
|
|
6708
6770
|
const loaded = await loadConfig(argv.cwd);
|
|
6709
|
-
buildCliLogger(argv, loaded.resolved.log?.level);
|
|
6771
|
+
buildCliLogger(argv, { configLevel: loaded.resolved.log?.level });
|
|
6710
6772
|
if ((await runSyncReadme({
|
|
6711
6773
|
loaded,
|
|
6712
6774
|
context: {
|