@theholocron/cli 3.1.0 → 3.2.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 CHANGED
@@ -1,4 +1,12 @@
1
1
  #!/usr/bin/env node
2
+ (function() {
3
+ try {
4
+ var e = "undefined" != typeof window ? window : "undefined" != typeof global ? global : "undefined" != typeof globalThis ? globalThis : "undefined" != typeof self ? self : {};
5
+ e.SENTRY_RELEASE = { id: "holocron@3.1.1" };
6
+ var n = new e.Error().stack;
7
+ n && (e._sentryDebugIds = e._sentryDebugIds || {}, e._sentryDebugIds[n] = "9c44dac0-ac25-4e90-a623-6c67cc9d66a1", e._sentryDebugIdIdentifier = "sentry-dbid-9c44dac0-ac25-4e90-a623-6c67cc9d66a1");
8
+ } catch (e) {}
9
+ })();
2
10
  import { createRequire } from "node:module";
3
11
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
12
  import path, { basename, dirname, join, relative, resolve } from "node:path";
@@ -17,6 +25,7 @@ import { access, copyFile, mkdir, readFile, readdir, rm, stat, symlink, unlink,
17
25
  import { pathToFileURL } from "node:url";
18
26
  import { createGitHubClient } from "@theholocron/github-client";
19
27
  import { promisify } from "node:util";
28
+ import * as Sentry from "@sentry/node";
20
29
  //#region src/env.ts
21
30
  function createEnvLookup(source = process.env) {
22
31
  return {
@@ -2485,7 +2494,7 @@ var stale_default$1 = "name: Stale\n\non: # yamllint disable-line rule:truthy\n
2485
2494
  var sync_github_default$1 = "name: Sync GitHub Templates\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n paths:\n - packages/cli/src/templates/index.ts\n - packages/cli/src/commands/setup-workflows.ts\n\nconcurrency:\n group: sync-github-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n\njobs:\n sync:\n name: Sync\n uses: theholocron/.github/.github/workflows/sync-github.yml@main\n with:\n secondary-repos: theholocron/.github-private\n secrets:\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n";
2486
2495
  //#endregion
2487
2496
  //#region src/commands/workflows/test.yml
2488
- var test_default$1 = "name: Test\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: test-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n\njobs:\n test:\n name: Test\n uses: theholocron/.github/.github/workflows/test.yml@main\n secrets: inherit\n";
2497
+ var test_default$1 = "name: Test\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: test-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n id-token: write\n\njobs:\n test:\n name: Test\n uses: theholocron/.github/.github/workflows/test.yml@main\n secrets: inherit\n";
2489
2498
  //#endregion
2490
2499
  //#region src/commands/workflows/typecheck.yml
2491
2500
  var typecheck_default$1 = "name: Typecheck\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: typecheck-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: read\n\njobs:\n typecheck:\n name: Typecheck\n uses: theholocron/.github/.github/workflows/typecheck.yml@main\n secrets: inherit\n";
@@ -4477,6 +4486,58 @@ async function fileExists(path) {
4477
4486
  }
4478
4487
  }
4479
4488
  //#endregion
4489
+ //#region src/telemetry.ts
4490
+ const DSN = "https://95cbb72ad5636c94e119a5405ee8f55f@o4508238154104832.ingest.us.sentry.io/4511810950791168";
4491
+ function isEnabled() {
4492
+ return !process.env.NO_HOLOCRON_TELEMETRY && true;
4493
+ }
4494
+ function init(version) {
4495
+ if (!isEnabled()) return;
4496
+ Sentry.init({
4497
+ dsn: DSN,
4498
+ release: `holocron@${version}`,
4499
+ environment: process.env.CI ? "ci" : "local",
4500
+ tracesSampleRate: 1,
4501
+ beforeSend: scrubError
4502
+ });
4503
+ Sentry.startSession();
4504
+ Sentry.setTag("os", process.platform);
4505
+ Sentry.setTag("node", process.version);
4506
+ Sentry.setTag("ci", String(Boolean(process.env.CI)));
4507
+ }
4508
+ function startCommand(name) {
4509
+ if (!isEnabled()) return () => {};
4510
+ Sentry.setTag("command", name);
4511
+ const span = Sentry.startInactiveSpan({
4512
+ name,
4513
+ op: "holocron.command",
4514
+ forceTransaction: true
4515
+ });
4516
+ return (ok) => {
4517
+ span.setStatus({ code: ok ? 1 : 2 });
4518
+ span.end();
4519
+ };
4520
+ }
4521
+ function captureException(err) {
4522
+ if (!isEnabled()) return;
4523
+ Sentry.captureException(err);
4524
+ }
4525
+ function endSession() {
4526
+ if (!isEnabled()) return;
4527
+ Sentry.endSession();
4528
+ }
4529
+ async function flush() {
4530
+ if (!isEnabled()) return;
4531
+ await Sentry.close(2e3);
4532
+ }
4533
+ const TOKEN_RE = /\b(ghp_|ghs_|glpat-|xoxb-|xoxp-|npm_|sk-|[A-Z][A-Z0-9_]{2,}_TOKEN[=\s])[^\s"]*/g;
4534
+ function redact(raw) {
4535
+ return raw.replace(TOKEN_RE, "[REDACTED]");
4536
+ }
4537
+ function scrubError(event, _hint) {
4538
+ return JSON.parse(redact(JSON.stringify(event)));
4539
+ }
4540
+ //#endregion
4480
4541
  //#region src/token-args.ts
4481
4542
  var TokenParseError = class extends Error {
4482
4543
  name = "TokenParseError";
@@ -4651,463 +4712,475 @@ function tokenContext(rawTokens) {
4651
4712
  throw err;
4652
4713
  }
4653
4714
  }
4715
+ init(CLI_VERSION);
4654
4716
  const updateCheckPromise = checkForUpdates(CLI_VERSION);
4655
- await yargs(hideBin(process.argv)).scriptName("").usage("holocron <command> [options]").option("dry-run", {
4656
- type: "boolean",
4657
- default: false,
4658
- describe: "Print what would be mutated without calling capability mutators. Commands branch on this; read-only commands ignore it."
4659
- }).option("token", {
4660
- type: "string",
4661
- array: true,
4662
- describe: "Vendor token override for plugins. Bare form: --token <value> (fallback for all plugins, single-plugin commands). Keyed form: --token vendor=value (targets a specific provider; repeat for each). Example: --token github=ghp_xxx --token vercel=v_yyy"
4663
- }).option("cwd", {
4664
- type: "string",
4665
- default: process.cwd(),
4666
- describe: "Directory to search for holocron.config.json"
4667
- }).command("version", "Print the CLI version", () => {}, () => {
4668
- console.log(`holocron ${CLI_VERSION}`);
4669
- }).command("clone", "Clone all repos in a GitHub org as siblings under a single directory", (y) => y.option("org", {
4670
- type: "string",
4671
- demandOption: true,
4672
- describe: "GitHub org to clone (e.g., theholocron)"
4673
- }).option("dir", {
4674
- type: "string",
4675
- describe: "Parent directory to clone into (default: ~/Code/<org>)"
4676
- }), async (argv) => {
4677
- const tokens = tokenContext(argv.token);
4678
- if (!tokens) return;
4679
- let token;
4680
- try {
4681
- token = resolveCloneToken({ cliToken: tokens.cliTokens?.["github"] ?? tokens.cliToken });
4682
- } catch (err) {
4683
- console.error(`clone: ${err instanceof AuthError ? err.message : String(err)}`);
4684
- process.exitCode = 1;
4685
- return;
4686
- }
4687
- if ((await runClone({
4688
- org: argv.org,
4689
- token,
4690
- dryRun: argv.dryRun,
4691
- ...argv.dir ? { dir: argv.dir } : {}
4692
- })).status === "fail") process.exitCode = 1;
4693
- }).command("doctor", "Load the config and run a smoke check against every provider", (y) => y.option("repo", {
4694
- type: "string",
4695
- describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
4696
- }), async (argv) => {
4697
- const tokens = tokenContext(argv.token);
4698
- if (!tokens) return;
4699
- if ((await runDoctor({
4700
- loaded: await loadConfig(argv.cwd),
4701
- context: {
4702
- repoRoot: argv.cwd,
4703
- dryRun: argv.dryRun,
4704
- ...argv.repo ? { repo: argv.repo } : {},
4705
- ...tokens
4717
+ let finishCommand = () => {};
4718
+ try {
4719
+ await yargs(hideBin(process.argv)).middleware((argv) => {
4720
+ finishCommand = startCommand(argv._.slice(0, 2).join(" ") || "unknown");
4721
+ }).scriptName("").usage("holocron <command> [options]").option("dry-run", {
4722
+ type: "boolean",
4723
+ default: false,
4724
+ describe: "Print what would be mutated without calling capability mutators. Commands branch on this; read-only commands ignore it."
4725
+ }).option("token", {
4726
+ type: "string",
4727
+ array: true,
4728
+ describe: "Vendor token override for plugins. Bare form: --token <value> (fallback for all plugins, single-plugin commands). Keyed form: --token vendor=value (targets a specific provider; repeat for each). Example: --token github=ghp_xxx --token vercel=v_yyy"
4729
+ }).option("cwd", {
4730
+ type: "string",
4731
+ default: process.cwd(),
4732
+ describe: "Directory to search for holocron.config.json"
4733
+ }).command("version", "Print the CLI version", () => {}, () => {
4734
+ console.log(`holocron ${CLI_VERSION}`);
4735
+ }).command("clone", "Clone all repos in a GitHub org as siblings under a single directory", (y) => y.option("org", {
4736
+ type: "string",
4737
+ demandOption: true,
4738
+ describe: "GitHub org to clone (e.g., theholocron)"
4739
+ }).option("dir", {
4740
+ type: "string",
4741
+ describe: "Parent directory to clone into (default: ~/Code/<org>)"
4742
+ }), async (argv) => {
4743
+ const tokens = tokenContext(argv.token);
4744
+ if (!tokens) return;
4745
+ let token;
4746
+ try {
4747
+ token = resolveCloneToken({ cliToken: tokens.cliTokens?.["github"] ?? tokens.cliToken });
4748
+ } catch (err) {
4749
+ console.error(`clone: ${err instanceof AuthError ? err.message : String(err)}`);
4750
+ process.exitCode = 1;
4751
+ return;
4706
4752
  }
4707
- })).summary.fail > 0) process.exitCode = 1;
4708
- }).command("setup", "Apply infra setup actions across every configured capability", (y) => y.option("repo", {
4709
- type: "string",
4710
- describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
4711
- }), async (argv) => {
4712
- const tokens = tokenContext(argv.token);
4713
- if (!tokens) return;
4714
- if ((await runSetup({
4715
- loaded: await loadConfig(argv.cwd),
4716
- context: {
4717
- repoRoot: argv.cwd,
4753
+ if ((await runClone({
4754
+ org: argv.org,
4755
+ token,
4718
4756
  dryRun: argv.dryRun,
4719
- ...argv.repo ? { repo: argv.repo } : {},
4720
- ...tokens
4721
- }
4722
- })).summary.fail > 0) process.exitCode = 1;
4723
- }).command("skills", "Manage agent skills from the @theholocron/skills registry", (y) => y.command("install", "Copy skills from @theholocron/skills into .agents/ with agent symlinks", () => {}, async (argv) => {
4724
- await runSkillsInstall({
4725
- loaded: await loadConfig(argv.cwd),
4726
- context: {
4727
- repoRoot: argv.cwd,
4728
- dryRun: argv.dryRun
4729
- }
4730
- });
4731
- }).command("remove [names..]", "Remove installed skills via npx skills remove", (yy) => yy.positional("names", {
4732
- type: "string",
4733
- array: true,
4734
- describe: "Skill name(s) to remove (omit to remove all installed skills)"
4735
- }), (argv) => {
4736
- if (runSkillsRemove({
4737
- context: {
4738
- repoRoot: argv.cwd,
4739
- dryRun: argv.dryRun
4740
- },
4741
- ...argv.names?.length ? { names: argv.names } : {}
4742
- }).status === "fail") process.exitCode = 1;
4743
- }).command("update [name]", "Update installed skills to their latest upstream versions via npx skills update", (yy) => yy.positional("name", {
4744
- type: "string",
4745
- describe: "Skill name to update (omit to update all installed skills)"
4746
- }), (argv) => {
4747
- if (runSkillsUpdate({
4748
- context: {
4749
- repoRoot: argv.cwd,
4757
+ ...argv.dir ? { dir: argv.dir } : {}
4758
+ })).status === "fail") process.exitCode = 1;
4759
+ }).command("doctor", "Load the config and run a smoke check against every provider", (y) => y.option("repo", {
4760
+ type: "string",
4761
+ describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
4762
+ }), async (argv) => {
4763
+ const tokens = tokenContext(argv.token);
4764
+ if (!tokens) return;
4765
+ if ((await runDoctor({
4766
+ loaded: await loadConfig(argv.cwd),
4767
+ context: {
4768
+ repoRoot: argv.cwd,
4769
+ dryRun: argv.dryRun,
4770
+ ...argv.repo ? { repo: argv.repo } : {},
4771
+ ...tokens
4772
+ }
4773
+ })).summary.fail > 0) process.exitCode = 1;
4774
+ }).command("setup", "Apply infra setup actions across every configured capability", (y) => y.option("repo", {
4775
+ type: "string",
4776
+ describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
4777
+ }), async (argv) => {
4778
+ const tokens = tokenContext(argv.token);
4779
+ if (!tokens) return;
4780
+ if ((await runSetup({
4781
+ loaded: await loadConfig(argv.cwd),
4782
+ context: {
4783
+ repoRoot: argv.cwd,
4784
+ dryRun: argv.dryRun,
4785
+ ...argv.repo ? { repo: argv.repo } : {},
4786
+ ...tokens
4787
+ }
4788
+ })).summary.fail > 0) process.exitCode = 1;
4789
+ }).command("skills", "Manage agent skills from the @theholocron/skills registry", (y) => y.command("install", "Copy skills from @theholocron/skills into .agents/ with agent symlinks", () => {}, async (argv) => {
4790
+ await runSkillsInstall({
4791
+ loaded: await loadConfig(argv.cwd),
4792
+ context: {
4793
+ repoRoot: argv.cwd,
4794
+ dryRun: argv.dryRun
4795
+ }
4796
+ });
4797
+ }).command("remove [names..]", "Remove installed skills via npx skills remove", (yy) => yy.positional("names", {
4798
+ type: "string",
4799
+ array: true,
4800
+ describe: "Skill name(s) to remove (omit to remove all installed skills)"
4801
+ }), (argv) => {
4802
+ if (runSkillsRemove({
4803
+ context: {
4804
+ repoRoot: argv.cwd,
4805
+ dryRun: argv.dryRun
4806
+ },
4807
+ ...argv.names?.length ? { names: argv.names } : {}
4808
+ }).status === "fail") process.exitCode = 1;
4809
+ }).command("update [name]", "Update installed skills to their latest upstream versions via npx skills update", (yy) => yy.positional("name", {
4810
+ type: "string",
4811
+ describe: "Skill name to update (omit to update all installed skills)"
4812
+ }), (argv) => {
4813
+ if (runSkillsUpdate({
4814
+ context: {
4815
+ repoRoot: argv.cwd,
4816
+ dryRun: argv.dryRun
4817
+ },
4818
+ ...argv.name ? { name: argv.name } : {}
4819
+ }).status === "fail") process.exitCode = 1;
4820
+ }).demandCommand(1, "Run `holocron skills --help` to see available skills subcommands."), () => {}).command("secret set <name> [value]", "Set a single secret via the configured `secrets` capability", (y) => y.positional("name", {
4821
+ type: "string",
4822
+ demandOption: true,
4823
+ describe: "Secret name (e.g., NPM_TOKEN)"
4824
+ }).positional("value", {
4825
+ type: "string",
4826
+ describe: "Secret value (positional). If omitted, sources from --from-stdin, --from-env, or env var matching <name>."
4827
+ }).option("from-stdin", {
4828
+ type: "boolean",
4829
+ default: false,
4830
+ describe: "Read the secret value from stdin"
4831
+ }).option("from-env", {
4832
+ type: "string",
4833
+ describe: "Read the secret value from the named env var (otherwise: env var matching <name>)"
4834
+ }).option("scope", {
4835
+ type: "string",
4836
+ default: "repo",
4837
+ describe: "Scope: \"repo\" (default), \"env=<name>\", or \"org=<name>\""
4838
+ }), async (argv) => {
4839
+ const tokens = tokenContext(argv.token);
4840
+ if (!tokens) return;
4841
+ const scopeArg = argv.scope;
4842
+ const scope = parseScope(scopeArg);
4843
+ if ((await runSecretSet({
4844
+ loaded: await loadConfig(argv.cwd),
4845
+ context: {
4846
+ repoRoot: argv.cwd,
4847
+ dryRun: argv.dryRun,
4848
+ ...tokens
4849
+ },
4850
+ name: argv.name,
4851
+ ...argv.value ? { value: argv.value } : {},
4852
+ ...argv.fromStdin ? { fromStdin: true } : {},
4853
+ ...argv.fromEnv ? { fromEnv: argv.fromEnv } : {},
4854
+ scope
4855
+ })).status === "fail") process.exitCode = 1;
4856
+ }).command("secrets sync <environmentId>", "Read a vault environment + fan KEY=VALUEs out to secrets + deployment env vars", (y) => y.positional("environmentId", {
4857
+ type: "string",
4858
+ demandOption: true,
4859
+ describe: "Vault environment id to read (1P Environment id, etc.)"
4860
+ }).option("project-id", {
4861
+ type: "string",
4862
+ describe: "Deployment project id (e.g., Vercel prj_*). Required when deployment is loaded."
4863
+ }).option("target", {
4864
+ type: "array",
4865
+ default: ["production", "preview"],
4866
+ describe: "Deployment targets to sync to. Defaults to production + preview."
4867
+ }), async (argv) => {
4868
+ const tokens = tokenContext(argv.token);
4869
+ if (!tokens) return;
4870
+ if ((await runSecretsSync({
4871
+ loaded: await loadConfig(argv.cwd),
4872
+ context: {
4873
+ repoRoot: argv.cwd,
4874
+ dryRun: argv.dryRun,
4875
+ ...tokens
4876
+ },
4877
+ environmentId: argv.environmentId,
4878
+ ...argv.projectId ? { projectId: argv.projectId } : {},
4879
+ targets: argv.target
4880
+ })).summary.fail > 0) process.exitCode = 1;
4881
+ }).command("deploy <branch>", "Trigger a deployment via the configured `deployment` capability", (y) => y.positional("branch", {
4882
+ type: "string",
4883
+ demandOption: true,
4884
+ describe: "Git branch to deploy"
4885
+ }).option("project-id", {
4886
+ type: "string",
4887
+ demandOption: true,
4888
+ describe: "Deployment project id (e.g., Vercel prj_*)"
4889
+ }).option("target", {
4890
+ type: "string",
4891
+ choices: ["production", "staging"],
4892
+ describe: "Named environment to deploy into. Omit for a branch preview."
4893
+ }), async (argv) => {
4894
+ const tokens = tokenContext(argv.token);
4895
+ if (!tokens) return;
4896
+ if ((await runDeploy({
4897
+ loaded: await loadConfig(argv.cwd),
4898
+ context: {
4899
+ repoRoot: argv.cwd,
4900
+ dryRun: argv.dryRun,
4901
+ ...tokens
4902
+ },
4903
+ projectId: argv.projectId,
4904
+ branch: argv.branch,
4905
+ ...argv.target ? { target: argv.target } : {}
4906
+ })).status === "fail") process.exitCode = 1;
4907
+ }).command("npm", "npm-related monorepo utilities", (y) => y.command("bump-versions <new-version>", "Bump all non-private package versions in lockstep (semantic-release prepareCmd)", (yy) => yy.positional("new-version", {
4908
+ type: "string",
4909
+ demandOption: true,
4910
+ describe: "Version to set (e.g., 4.2.0 or 2.0.0-alpha.1)"
4911
+ }), async (argv) => {
4912
+ if ((await runNpmBumpVersions({
4913
+ version: argv.newVersion,
4914
+ cwd: argv.cwd,
4750
4915
  dryRun: argv.dryRun
4751
- },
4752
- ...argv.name ? { name: argv.name } : {}
4753
- }).status === "fail") process.exitCode = 1;
4754
- }).demandCommand(1, "Run `holocron skills --help` to see available skills subcommands."), () => {}).command("secret set <name> [value]", "Set a single secret via the configured `secrets` capability", (y) => y.positional("name", {
4755
- type: "string",
4756
- demandOption: true,
4757
- describe: "Secret name (e.g., NPM_TOKEN)"
4758
- }).positional("value", {
4759
- type: "string",
4760
- describe: "Secret value (positional). If omitted, sources from --from-stdin, --from-env, or env var matching <name>."
4761
- }).option("from-stdin", {
4762
- type: "boolean",
4763
- default: false,
4764
- describe: "Read the secret value from stdin"
4765
- }).option("from-env", {
4766
- type: "string",
4767
- describe: "Read the secret value from the named env var (otherwise: env var matching <name>)"
4768
- }).option("scope", {
4769
- type: "string",
4770
- default: "repo",
4771
- describe: "Scope: \"repo\" (default), \"env=<name>\", or \"org=<name>\""
4772
- }), async (argv) => {
4773
- const tokens = tokenContext(argv.token);
4774
- if (!tokens) return;
4775
- const scopeArg = argv.scope;
4776
- const scope = parseScope(scopeArg);
4777
- if ((await runSecretSet({
4778
- loaded: await loadConfig(argv.cwd),
4779
- context: {
4780
- repoRoot: argv.cwd,
4781
- dryRun: argv.dryRun,
4782
- ...tokens
4783
- },
4784
- name: argv.name,
4785
- ...argv.value ? { value: argv.value } : {},
4786
- ...argv.fromStdin ? { fromStdin: true } : {},
4787
- ...argv.fromEnv ? { fromEnv: argv.fromEnv } : {},
4788
- scope
4789
- })).status === "fail") process.exitCode = 1;
4790
- }).command("secrets sync <environmentId>", "Read a vault environment + fan KEY=VALUEs out to secrets + deployment env vars", (y) => y.positional("environmentId", {
4791
- type: "string",
4792
- demandOption: true,
4793
- describe: "Vault environment id to read (1P Environment id, etc.)"
4794
- }).option("project-id", {
4795
- type: "string",
4796
- describe: "Deployment project id (e.g., Vercel prj_*). Required when deployment is loaded."
4797
- }).option("target", {
4798
- type: "array",
4799
- default: ["production", "preview"],
4800
- describe: "Deployment targets to sync to. Defaults to production + preview."
4801
- }), async (argv) => {
4802
- const tokens = tokenContext(argv.token);
4803
- if (!tokens) return;
4804
- if ((await runSecretsSync({
4805
- loaded: await loadConfig(argv.cwd),
4806
- context: {
4807
- repoRoot: argv.cwd,
4808
- dryRun: argv.dryRun,
4809
- ...tokens
4810
- },
4811
- environmentId: argv.environmentId,
4812
- ...argv.projectId ? { projectId: argv.projectId } : {},
4813
- targets: argv.target
4814
- })).summary.fail > 0) process.exitCode = 1;
4815
- }).command("deploy <branch>", "Trigger a deployment via the configured `deployment` capability", (y) => y.positional("branch", {
4816
- type: "string",
4817
- demandOption: true,
4818
- describe: "Git branch to deploy"
4819
- }).option("project-id", {
4820
- type: "string",
4821
- demandOption: true,
4822
- describe: "Deployment project id (e.g., Vercel prj_*)"
4823
- }).option("target", {
4824
- type: "string",
4825
- choices: ["production", "staging"],
4826
- describe: "Named environment to deploy into. Omit for a branch preview."
4827
- }), async (argv) => {
4828
- const tokens = tokenContext(argv.token);
4829
- if (!tokens) return;
4830
- if ((await runDeploy({
4831
- loaded: await loadConfig(argv.cwd),
4832
- context: {
4833
- repoRoot: argv.cwd,
4834
- dryRun: argv.dryRun,
4835
- ...tokens
4836
- },
4837
- projectId: argv.projectId,
4838
- branch: argv.branch,
4839
- ...argv.target ? { target: argv.target } : {}
4840
- })).status === "fail") process.exitCode = 1;
4841
- }).command("npm", "npm-related monorepo utilities", (y) => y.command("bump-versions <new-version>", "Bump all non-private package versions in lockstep (semantic-release prepareCmd)", (yy) => yy.positional("new-version", {
4842
- type: "string",
4843
- demandOption: true,
4844
- describe: "Version to set (e.g., 4.2.0 or 2.0.0-alpha.1)"
4845
- }), async (argv) => {
4846
- if ((await runNpmBumpVersions({
4847
- version: argv.newVersion,
4848
- cwd: argv.cwd,
4849
- dryRun: argv.dryRun
4850
- })).status === "fail") process.exitCode = 1;
4851
- }).command("publish-initial", "One-shot bootstrap publish for trusted-publishing-eligible packages", (yy) => yy.option("tag", {
4852
- type: "string",
4853
- default: "alpha",
4854
- describe: "npm distribution tag (defaults to alpha)"
4855
- }).option("otp", {
4856
- type: "string",
4857
- describe: "One-time password from your authenticator (required if npm needs 2FA for writes)"
4858
- }), async (argv) => {
4859
- if ((await runNpmPublishInitial({
4860
- cwd: argv.cwd,
4861
- tag: argv.tag,
4862
- dryRun: argv.dryRun,
4863
- ...argv.otp ? { otp: argv.otp } : {}
4864
- })).status === "fail") process.exitCode = 1;
4865
- }).demandCommand(1, "Run `holocron npm --help` to see available npm subcommands."), () => {}).command("sync [steps..]", "Sync state from config to the provider and local files (labels, properties, topics, keywords, description)", (y) => y.positional("steps", {
4866
- type: "string",
4867
- array: true,
4868
- describe: "Steps to run: labels, properties, topics, keywords, description (default: all)"
4869
- }).option("repo", {
4870
- type: "string",
4871
- describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
4872
- }), async (argv) => {
4873
- const tokens = tokenContext(argv.token);
4874
- if (!tokens) return;
4875
- if ((await runSync({
4876
- loaded: await loadConfig(argv.cwd),
4877
- context: {
4878
- repoRoot: argv.cwd,
4916
+ })).status === "fail") process.exitCode = 1;
4917
+ }).command("publish-initial", "One-shot bootstrap publish for trusted-publishing-eligible packages", (yy) => yy.option("tag", {
4918
+ type: "string",
4919
+ default: "alpha",
4920
+ describe: "npm distribution tag (defaults to alpha)"
4921
+ }).option("otp", {
4922
+ type: "string",
4923
+ describe: "One-time password from your authenticator (required if npm needs 2FA for writes)"
4924
+ }), async (argv) => {
4925
+ if ((await runNpmPublishInitial({
4926
+ cwd: argv.cwd,
4927
+ tag: argv.tag,
4879
4928
  dryRun: argv.dryRun,
4880
- ...argv.repo ? { repo: argv.repo } : {},
4881
- ...tokens
4882
- },
4883
- ...argv.steps && argv.steps.length > 0 ? { steps: argv.steps } : {}
4884
- })).summary.fail > 0) process.exitCode = 1;
4885
- }).command("sync-github", "Sync workflow templates and composite actions to theholocron/.github via the GitHub API", (y) => y.option("repo", {
4886
- type: "string",
4887
- default: "theholocron/.github",
4888
- describe: "Target org/repo (default: theholocron/.github)"
4889
- }).option("branch", {
4890
- type: "string",
4891
- describe: "Push to this branch instead of the default branch (enables PR-based workflow for protected repos)"
4892
- }).option("pr", {
4893
- type: "boolean",
4894
- default: false,
4895
- describe: "Open a PR after pushing to --branch (no-op without --branch)"
4896
- }).option("message", {
4897
- type: "string",
4898
- describe: "Commit message (default: chore: sync from theholocron/holocron)"
4899
- }).option("output-dir", {
4900
- type: "string",
4901
- describe: "Write generated files to this local directory instead of pushing (for validation)"
4902
- }), async (argv) => {
4903
- const outputDir = argv["output-dir"];
4904
- const parsed = tokenContext(argv.token);
4905
- if (!parsed) return;
4906
- let token;
4907
- if (outputDir) token = "no-token-needed";
4908
- else try {
4909
- token = resolveSyncToken({ cliToken: parsed.cliTokens?.["github"] ?? parsed.cliToken });
4910
- } catch (err) {
4911
- console.error(`sync-github: ${err instanceof AuthError ? err.message : String(err)}`);
4912
- process.exitCode = 1;
4913
- return;
4914
- }
4915
- if ((await runSyncGithub({
4916
- token,
4917
- repo: argv.repo,
4918
- dryRun: argv.dryRun,
4919
- ...argv.branch ? { branch: argv.branch } : {},
4920
- ...argv.pr ? { createPr: true } : {},
4921
- ...argv.message ? { message: argv.message } : {},
4922
- ...outputDir ? { outputDir } : {}
4923
- })).status === "fail") process.exitCode = 1;
4924
- }).command("config show", "Print the resolved holocron config", () => {}, async (argv) => {
4925
- const loaded = await loadConfig(argv.cwd);
4926
- console.log(JSON.stringify(loaded.resolved, null, 2));
4927
- }).command("new [type] [name]", "Scaffold a new repo from a GitHub template (e.g. cli, react, nextjs, node, monorepo, base)", (y) => y.positional("type", {
4928
- type: "string",
4929
- describe: "Template type — maps to theholocron/<type>-template (e.g. cli, react, nextjs, node, monorepo, base)"
4930
- }).positional("name", {
4931
- type: "string",
4932
- describe: "New repo name (kebab-case, e.g. my-tool)"
4933
- }).option("description", {
4934
- type: "string",
4935
- describe: "Short description — replaces <description> placeholders in the template"
4936
- }).option("org", {
4937
- type: "string",
4938
- default: "theholocron",
4939
- describe: "GitHub org that owns the template and will own the new repo"
4940
- }).option("verify", {
4941
- type: "boolean",
4942
- default: true,
4943
- describe: "Run pnpm install after bootstrapping (default true; --no-verify skips)"
4944
- }), async (argv) => {
4945
- try {
4946
- let type = argv.type;
4947
- let name = argv.name;
4948
- let description = argv.description;
4949
- if (!type || !name || description === void 0) {
4950
- const rl = createInterface({
4951
- input: stdin,
4952
- output: stdout
4953
- });
4954
- const ask = (question) => new Promise((resolve) => rl.question(` ${question} `, (answer) => resolve(answer.trim())));
4955
- try {
4956
- if (!type) {
4957
- console.log(" Known types: base, cli, monorepo, nextjs, node, react");
4958
- type = await ask("Template type:");
4959
- }
4960
- if (!name) name = await ask("Repo name (kebab-case):");
4961
- if (description === void 0) description = await ask("Short description (Enter to skip):");
4962
- } finally {
4963
- rl.close();
4964
- }
4965
- }
4966
- if (!type) {
4967
- console.error("new: template type is required");
4968
- process.exitCode = 1;
4969
- return;
4970
- }
4971
- if (!name) {
4972
- console.error("new: repo name is required");
4929
+ ...argv.otp ? { otp: argv.otp } : {}
4930
+ })).status === "fail") process.exitCode = 1;
4931
+ }).demandCommand(1, "Run `holocron npm --help` to see available npm subcommands."), () => {}).command("sync [steps..]", "Sync state from config to the provider and local files (labels, properties, topics, keywords, description)", (y) => y.positional("steps", {
4932
+ type: "string",
4933
+ array: true,
4934
+ describe: "Steps to run: labels, properties, topics, keywords, description (default: all)"
4935
+ }).option("repo", {
4936
+ type: "string",
4937
+ describe: "Repo coords (\"owner/name\"). Defaults to plugin-specific resolution."
4938
+ }), async (argv) => {
4939
+ const tokens = tokenContext(argv.token);
4940
+ if (!tokens) return;
4941
+ if ((await runSync({
4942
+ loaded: await loadConfig(argv.cwd),
4943
+ context: {
4944
+ repoRoot: argv.cwd,
4945
+ dryRun: argv.dryRun,
4946
+ ...argv.repo ? { repo: argv.repo } : {},
4947
+ ...tokens
4948
+ },
4949
+ ...argv.steps && argv.steps.length > 0 ? { steps: argv.steps } : {}
4950
+ })).summary.fail > 0) process.exitCode = 1;
4951
+ }).command("sync-github", "Sync workflow templates and composite actions to theholocron/.github via the GitHub API", (y) => y.option("repo", {
4952
+ type: "string",
4953
+ default: "theholocron/.github",
4954
+ describe: "Target org/repo (default: theholocron/.github)"
4955
+ }).option("branch", {
4956
+ type: "string",
4957
+ describe: "Push to this branch instead of the default branch (enables PR-based workflow for protected repos)"
4958
+ }).option("pr", {
4959
+ type: "boolean",
4960
+ default: false,
4961
+ describe: "Open a PR after pushing to --branch (no-op without --branch)"
4962
+ }).option("message", {
4963
+ type: "string",
4964
+ describe: "Commit message (default: chore: sync from theholocron/holocron)"
4965
+ }).option("output-dir", {
4966
+ type: "string",
4967
+ describe: "Write generated files to this local directory instead of pushing (for validation)"
4968
+ }), async (argv) => {
4969
+ const outputDir = argv["output-dir"];
4970
+ const parsed = tokenContext(argv.token);
4971
+ if (!parsed) return;
4972
+ let token;
4973
+ if (outputDir) token = "no-token-needed";
4974
+ else try {
4975
+ token = resolveSyncToken({ cliToken: parsed.cliTokens?.["github"] ?? parsed.cliToken });
4976
+ } catch (err) {
4977
+ console.error(`sync-github: ${err instanceof AuthError ? err.message : String(err)}`);
4973
4978
  process.exitCode = 1;
4974
4979
  return;
4975
4980
  }
4976
- if ((await runNew({
4977
- type,
4978
- name,
4979
- ...description ? { description } : {},
4980
- org: argv.org,
4981
+ if ((await runSyncGithub({
4982
+ token,
4983
+ repo: argv.repo,
4981
4984
  dryRun: argv.dryRun,
4982
- noVerify: !argv.verify,
4983
- cwd: argv.cwd
4985
+ ...argv.branch ? { branch: argv.branch } : {},
4986
+ ...argv.pr ? { createPr: true } : {},
4987
+ ...argv.message ? { message: argv.message } : {},
4988
+ ...outputDir ? { outputDir } : {}
4984
4989
  })).status === "fail") process.exitCode = 1;
4985
- } catch (err) {
4986
- if (err instanceof NewError) {
4987
- console.error(`new: ${err.message}`);
4988
- process.exitCode = 1;
4989
- return;
4990
+ }).command("config show", "Print the resolved holocron config", () => {}, async (argv) => {
4991
+ const loaded = await loadConfig(argv.cwd);
4992
+ console.log(JSON.stringify(loaded.resolved, null, 2));
4993
+ }).command("new [type] [name]", "Scaffold a new repo from a GitHub template (e.g. cli, react, nextjs, node, monorepo, base)", (y) => y.positional("type", {
4994
+ type: "string",
4995
+ describe: "Template type — maps to theholocron/<type>-template (e.g. cli, react, nextjs, node, monorepo, base)"
4996
+ }).positional("name", {
4997
+ type: "string",
4998
+ describe: "New repo name (kebab-case, e.g. my-tool)"
4999
+ }).option("description", {
5000
+ type: "string",
5001
+ describe: "Short description — replaces <description> placeholders in the template"
5002
+ }).option("org", {
5003
+ type: "string",
5004
+ default: "theholocron",
5005
+ describe: "GitHub org that owns the template and will own the new repo"
5006
+ }).option("verify", {
5007
+ type: "boolean",
5008
+ default: true,
5009
+ describe: "Run pnpm install after bootstrapping (default true; --no-verify skips)"
5010
+ }), async (argv) => {
5011
+ try {
5012
+ let type = argv.type;
5013
+ let name = argv.name;
5014
+ let description = argv.description;
5015
+ if (!type || !name || description === void 0) {
5016
+ const rl = createInterface({
5017
+ input: stdin,
5018
+ output: stdout
5019
+ });
5020
+ const ask = (question) => new Promise((resolve) => rl.question(` ${question} `, (answer) => resolve(answer.trim())));
5021
+ try {
5022
+ if (!type) {
5023
+ console.log(" Known types: base, cli, monorepo, nextjs, node, react");
5024
+ type = await ask("Template type:");
5025
+ }
5026
+ if (!name) name = await ask("Repo name (kebab-case):");
5027
+ if (description === void 0) description = await ask("Short description (Enter to skip):");
5028
+ } finally {
5029
+ rl.close();
5030
+ }
5031
+ }
5032
+ if (!type) {
5033
+ console.error("new: template type is required");
5034
+ process.exitCode = 1;
5035
+ return;
5036
+ }
5037
+ if (!name) {
5038
+ console.error("new: repo name is required");
5039
+ process.exitCode = 1;
5040
+ return;
5041
+ }
5042
+ if ((await runNew({
5043
+ type,
5044
+ name,
5045
+ ...description ? { description } : {},
5046
+ org: argv.org,
5047
+ dryRun: argv.dryRun,
5048
+ noVerify: !argv.verify,
5049
+ cwd: argv.cwd
5050
+ })).status === "fail") process.exitCode = 1;
5051
+ } catch (err) {
5052
+ if (err instanceof NewError) {
5053
+ console.error(`new: ${err.message}`);
5054
+ process.exitCode = 1;
5055
+ return;
5056
+ }
5057
+ throw err;
4990
5058
  }
4991
- throw err;
4992
- }
4993
- }).command("plugin create <slug> <vendor>", "Scaffold a new @theholocron/holocron-plugin-<slug> package", (y) => y.positional("slug", {
4994
- type: "string",
4995
- demandOption: true,
4996
- describe: "Package slug (kebab-case)"
4997
- }).positional("vendor", {
4998
- type: "string",
4999
- demandOption: true,
5000
- describe: "Vendor display name (PascalCase)"
5001
- }).option("capability", {
5002
- type: "string",
5003
- describe: "Capability key: source|ci|secrets|environments|issues|deployment|storage|auth|vault|dns|tooling|notifications|analytics|observability"
5004
- }).option("token-env", {
5005
- type: "string",
5006
- describe: "Holocron env var name (defaults to HOLOCRON_<VENDOR>_TOKEN)"
5007
- }).option("vendor-env", {
5008
- type: "string",
5009
- describe: "Vendor-native env var name"
5010
- }).option("base-url", {
5011
- type: "string",
5012
- describe: "REST base URL"
5013
- }).option("verify", {
5014
- type: "boolean",
5015
- default: true,
5016
- describe: "Run post-scaffold pnpm install + typecheck + lint + test (default true; --no-verify skips)"
5017
- }), async (argv) => {
5018
- try {
5019
- const capabilityKeys = Object.keys(CARDINALITY).join(", ");
5020
- const needsPrompt = !argv.capability || !argv.vendorEnv || !argv.baseUrl;
5021
- let capability;
5022
- let vendorEnv;
5023
- let baseUrl;
5024
- if (needsPrompt) {
5025
- const rl = createInterface({
5026
- input: stdin,
5027
- output: stdout
5028
- });
5029
- const ask = (question) => new Promise((resolve) => rl.question(` ${question} `, (answer) => resolve(answer.trim())));
5030
- try {
5031
- if (!argv.capability) {
5032
- console.log(` Available capabilities: ${capabilityKeys}`);
5033
- capability = await ask("Capability:");
5034
- } else capability = argv.capability;
5035
- vendorEnv = argv.vendorEnv ? argv.vendorEnv : await ask(`Vendor-native env var for the ${argv.vendor} token (e.g. MYVENDOR_API_KEY):`);
5036
- baseUrl = argv.baseUrl ? argv.baseUrl : await ask(`REST base URL for the ${argv.vendor} API (e.g. https://api.myvendor.com):`);
5037
- } finally {
5038
- rl.close();
5059
+ }).command("plugin create <slug> <vendor>", "Scaffold a new @theholocron/holocron-plugin-<slug> package", (y) => y.positional("slug", {
5060
+ type: "string",
5061
+ demandOption: true,
5062
+ describe: "Package slug (kebab-case)"
5063
+ }).positional("vendor", {
5064
+ type: "string",
5065
+ demandOption: true,
5066
+ describe: "Vendor display name (PascalCase)"
5067
+ }).option("capability", {
5068
+ type: "string",
5069
+ describe: "Capability key: source|ci|secrets|environments|issues|deployment|storage|auth|vault|dns|tooling|notifications|analytics|observability"
5070
+ }).option("token-env", {
5071
+ type: "string",
5072
+ describe: "Holocron env var name (defaults to HOLOCRON_<VENDOR>_TOKEN)"
5073
+ }).option("vendor-env", {
5074
+ type: "string",
5075
+ describe: "Vendor-native env var name"
5076
+ }).option("base-url", {
5077
+ type: "string",
5078
+ describe: "REST base URL"
5079
+ }).option("verify", {
5080
+ type: "boolean",
5081
+ default: true,
5082
+ describe: "Run post-scaffold pnpm install + typecheck + lint + test (default true; --no-verify skips)"
5083
+ }), async (argv) => {
5084
+ try {
5085
+ const capabilityKeys = Object.keys(CARDINALITY).join(", ");
5086
+ const needsPrompt = !argv.capability || !argv.vendorEnv || !argv.baseUrl;
5087
+ let capability;
5088
+ let vendorEnv;
5089
+ let baseUrl;
5090
+ if (needsPrompt) {
5091
+ const rl = createInterface({
5092
+ input: stdin,
5093
+ output: stdout
5094
+ });
5095
+ const ask = (question) => new Promise((resolve) => rl.question(` ${question} `, (answer) => resolve(answer.trim())));
5096
+ try {
5097
+ if (!argv.capability) {
5098
+ console.log(` Available capabilities: ${capabilityKeys}`);
5099
+ capability = await ask("Capability:");
5100
+ } else capability = argv.capability;
5101
+ vendorEnv = argv.vendorEnv ? argv.vendorEnv : await ask(`Vendor-native env var for the ${argv.vendor} token (e.g. MYVENDOR_API_KEY):`);
5102
+ baseUrl = argv.baseUrl ? argv.baseUrl : await ask(`REST base URL for the ${argv.vendor} API (e.g. https://api.myvendor.com):`);
5103
+ } finally {
5104
+ rl.close();
5105
+ }
5106
+ } else {
5107
+ capability = argv.capability;
5108
+ vendorEnv = argv.vendorEnv;
5109
+ baseUrl = argv.baseUrl;
5039
5110
  }
5040
- } else {
5041
- capability = argv.capability;
5042
- vendorEnv = argv.vendorEnv;
5043
- baseUrl = argv.baseUrl;
5111
+ if (runPluginCreate({
5112
+ slug: argv.slug,
5113
+ vendorName: argv.vendor,
5114
+ capability,
5115
+ vendorEnv,
5116
+ baseUrl,
5117
+ ...argv.tokenEnv ? { tokenEnv: argv.tokenEnv } : {},
5118
+ dryRun: argv.dryRun,
5119
+ noVerify: !argv.verify,
5120
+ cwd: argv.cwd
5121
+ }).status === "fail") process.exitCode = 1;
5122
+ } catch (err) {
5123
+ if (err instanceof PluginCreateError) {
5124
+ console.error(`plugin create: ${err.message}`);
5125
+ process.exitCode = 1;
5126
+ return;
5127
+ }
5128
+ throw err;
5044
5129
  }
5045
- if (runPluginCreate({
5046
- slug: argv.slug,
5047
- vendorName: argv.vendor,
5048
- capability,
5049
- vendorEnv,
5050
- baseUrl,
5051
- ...argv.tokenEnv ? { tokenEnv: argv.tokenEnv } : {},
5130
+ }).command("upgrade", "Upgrade toolchain version pins across the repo", (y) => y.command("node <to>", "Scan the repo and update every Node.js version pin to a new major", (yy) => yy.positional("to", {
5131
+ type: "number",
5132
+ demandOption: true,
5133
+ describe: "Target Node.js major version (e.g., 22)"
5134
+ }).option("from", {
5135
+ type: "number",
5136
+ describe: "Current major version to replace. Auto-detected from .nvmrc / engines.node when omitted."
5137
+ }), async (argv) => {
5138
+ let extra = [];
5139
+ try {
5140
+ const raw = readFileSync(join(argv.cwd, "holocron.config.json"), "utf8");
5141
+ const upgradeNode = JSON.parse(raw).upgrade?.node;
5142
+ if (Array.isArray(upgradeNode?.extra)) extra = upgradeNode.extra;
5143
+ } catch {}
5144
+ const report = await runUpgradeNode({
5145
+ to: argv.to,
5146
+ ...argv.from != null ? { from: argv.from } : {},
5147
+ cwd: argv.cwd,
5052
5148
  dryRun: argv.dryRun,
5053
- noVerify: !argv.verify,
5054
- cwd: argv.cwd
5055
- }).status === "fail") process.exitCode = 1;
5056
- } catch (err) {
5057
- if (err instanceof PluginCreateError) {
5058
- console.error(`plugin create: ${err.message}`);
5149
+ extra
5150
+ });
5151
+ if (report.status === "fail") {
5152
+ if (report.message) console.error(`upgrade node: ${report.message}`);
5059
5153
  process.exitCode = 1;
5060
- return;
5061
5154
  }
5062
- throw err;
5063
- }
5064
- }).command("upgrade", "Upgrade toolchain version pins across the repo", (y) => y.command("node <to>", "Scan the repo and update every Node.js version pin to a new major", (yy) => yy.positional("to", {
5065
- type: "number",
5066
- demandOption: true,
5067
- describe: "Target Node.js major version (e.g., 22)"
5068
- }).option("from", {
5069
- type: "number",
5070
- describe: "Current major version to replace. Auto-detected from .nvmrc / engines.node when omitted."
5071
- }), async (argv) => {
5072
- let extra = [];
5073
- try {
5074
- const raw = readFileSync(join(argv.cwd, "holocron.config.json"), "utf8");
5075
- const upgradeNode = JSON.parse(raw).upgrade?.node;
5076
- if (Array.isArray(upgradeNode?.extra)) extra = upgradeNode.extra;
5077
- } catch {}
5078
- const report = await runUpgradeNode({
5079
- to: argv.to,
5080
- ...argv.from != null ? { from: argv.from } : {},
5081
- cwd: argv.cwd,
5082
- dryRun: argv.dryRun,
5083
- extra
5084
- });
5085
- if (report.status === "fail") {
5086
- if (report.message) console.error(`upgrade node: ${report.message}`);
5087
- process.exitCode = 1;
5088
- }
5089
- }).demandCommand(1, "Run `holocron upgrade --help` to see available upgrade subcommands."), () => {}).command("auth <subcommand>", "Manage bootstrap credentials in the OS keyring", (y) => y.command("set <provider> [value]", "Verify + store a bootstrap token for a provider", (yy) => yy.positional("provider", {
5090
- type: "string",
5091
- demandOption: true
5092
- }).positional("value", { type: "string" }), async (argv) => {
5093
- if ((await runAuthSet({
5094
- provider: argv.provider,
5095
- ...argv.value ? { positional: argv.value } : {}
5096
- })).status === "fail") process.exitCode = 1;
5097
- }).command("unset <provider>", "Remove a stored bootstrap token", (yy) => yy.positional("provider", {
5098
- type: "string",
5099
- demandOption: true
5100
- }), (argv) => {
5101
- runAuthUnset({ provider: argv.provider });
5102
- }).command("check <provider>", "Re-verify a stored bootstrap token", (yy) => yy.positional("provider", {
5103
- type: "string",
5104
- demandOption: true
5105
- }), async (argv) => {
5106
- if ((await runAuthCheck({ provider: argv.provider })).status === "fail") process.exitCode = 1;
5107
- }).command("list", "List every provider with a stored bootstrap token", () => {}, async () => {
5108
- await runAuthList();
5109
- }).demandCommand(1, "Run `holocron auth --help` to see available auth subcommands."), () => {}).demandCommand(1, "Run `holocron --help` to see available commands.").strict().help().parse();
5155
+ }).demandCommand(1, "Run `holocron upgrade --help` to see available upgrade subcommands."), () => {}).command("auth <subcommand>", "Manage bootstrap credentials in the OS keyring", (y) => y.command("set <provider> [value]", "Verify + store a bootstrap token for a provider", (yy) => yy.positional("provider", {
5156
+ type: "string",
5157
+ demandOption: true
5158
+ }).positional("value", { type: "string" }), async (argv) => {
5159
+ if ((await runAuthSet({
5160
+ provider: argv.provider,
5161
+ ...argv.value ? { positional: argv.value } : {}
5162
+ })).status === "fail") process.exitCode = 1;
5163
+ }).command("unset <provider>", "Remove a stored bootstrap token", (yy) => yy.positional("provider", {
5164
+ type: "string",
5165
+ demandOption: true
5166
+ }), (argv) => {
5167
+ runAuthUnset({ provider: argv.provider });
5168
+ }).command("check <provider>", "Re-verify a stored bootstrap token", (yy) => yy.positional("provider", {
5169
+ type: "string",
5170
+ demandOption: true
5171
+ }), async (argv) => {
5172
+ if ((await runAuthCheck({ provider: argv.provider })).status === "fail") process.exitCode = 1;
5173
+ }).command("list", "List every provider with a stored bootstrap token", () => {}, async () => {
5174
+ await runAuthList();
5175
+ }).demandCommand(1, "Run `holocron auth --help` to see available auth subcommands."), () => {}).demandCommand(1, "Run `holocron --help` to see available commands.").strict().help().parse();
5176
+ } catch (err) {
5177
+ captureException(err);
5178
+ if (!process.exitCode) process.exitCode = 1;
5179
+ }
5180
+ finishCommand(!process.exitCode);
5181
+ endSession();
5110
5182
  (await updateCheckPromise)?.();
5183
+ await flush();
5111
5184
  /**
5112
5185
  * Parse `--scope` strings: `repo` | `env=NAME` | `org=NAME`.
5113
5186
  */
@@ -5125,3 +5198,5 @@ function parseScope(s) {
5125
5198
  }
5126
5199
  //#endregion
5127
5200
  export {};
5201
+
5202
+ //# sourceMappingURL=cli.mjs.map