@theholocron/cli 3.62.1 → 3.63.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/README.md CHANGED
@@ -192,8 +192,16 @@ Axiom shipping activates when a token (`HOLOCRON_AXIOM_TOKEN` / `AXIOM_TOKEN`
192
192
  → OS keyring `axiom.<org>` → `axiom`) **and** a dataset
193
193
  (`HOLOCRON_AXIOM_DATASET` / `AXIOM_DATASET` → `log.axiom.dataset` in
194
194
  `holocron.config`) both resolve; env vars win. The token is never read from a
195
- config file. `HOLOCRON_TELEMETRY=false` disables the Axiom transport (and
196
- Sentry). See the [logging guide](https://docs.theholocron.dev/holocron/logging/).
195
+ config file. See the [logging guide](https://docs.theholocron.dev/holocron/logging/).
196
+
197
+ ### Telemetry
198
+
199
+ The CLI reports on itself through three sinks: **Sentry** (errors), **Axiom**
200
+ (logs), and **PostHog** (anonymous usage analytics — command frequency,
201
+ duration, failure rates, keyed to a one-way `sha256(hostname + username)`
202
+ fingerprint). PostHog activates from `HOLOCRON_POSTHOG_PROJECT_TOKEN` → `POSTHOG_PROJECT_TOKEN`
203
+ → a shipped ingest-only key (on by default). `HOLOCRON_TELEMETRY=false` disables
204
+ all three. See the [telemetry guide](https://docs.theholocron.dev/holocron/telemetry/).
197
205
 
198
206
  ## What's in here
199
207
 
package/dist/cli.mjs CHANGED
@@ -13,14 +13,15 @@ import { createLogger, parseLogLevel, resolveAxiomFromEnv } from "@theholocron/l
13
13
  import ora from "ora";
14
14
  import chalk from "chalk";
15
15
  import { execFile, execFileSync, spawnSync } from "node:child_process";
16
- import { homedir } from "node:os";
16
+ import { homedir, hostname, userInfo } from "node:os";
17
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";
21
21
  import { createGitHubClient } from "@theholocron/github-client";
22
- import { promisify } from "node:util";
23
22
  import * as Sentry from "@sentry/node";
23
+ import { PostHog } from "posthog-node";
24
+ import { promisify } from "node:util";
24
25
  //#region src/env.ts
25
26
  /** Singleton env for simple global lookups throughout the CLI. */
26
27
  const env = createEnvLookup();
@@ -5960,6 +5961,168 @@ async function writeWorkflowFile(repoRoot, filename, content) {
5960
5961
  await writeFile(join(dir, filename), content, "utf8");
5961
5962
  }
5962
5963
  //#endregion
5964
+ //#region src/telemetry.ts
5965
+ const FALLBACK_DSN = "https://95cbb72ad5636c94e119a5405ee8f55f@o4508238154104832.ingest.us.sentry.io/4511810950791168";
5966
+ /**
5967
+ * Resolve the Sentry DSN — the `errors` capability's self-contained
5968
+ * activation, per ADR-0007:
5969
+ *
5970
+ * HOLOCRON_SENTRY_DSN → SENTRY_DSN → built-in fallback
5971
+ *
5972
+ * A consumer repo with `SENTRY_DSN` set gets CLI errors routed to its own
5973
+ * project with no config. Opt out entirely with `HOLOCRON_TELEMETRY=false`.
5974
+ */
5975
+ function resolveDsn() {
5976
+ return env.get("HOLOCRON_SENTRY_DSN") ?? env.get("SENTRY_DSN") ?? FALLBACK_DSN;
5977
+ }
5978
+ const FALLBACK_POSTHOG_PROJECT_TOKEN = "phc_AC4vFCvYzwnzmG7Vg5nEc3PZKZztoPyfKp4Lb9BbXcLK";
5979
+ const DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com";
5980
+ /**
5981
+ * Resolve the PostHog project key — mirrors {@link resolveDsn} and the
5982
+ * `HOLOCRON_AXIOM_TOKEN` / `AXIOM_TOKEN` pattern, per ADR-0008:
5983
+ *
5984
+ * HOLOCRON_POSTHOG_PROJECT_TOKEN → POSTHOG_PROJECT_TOKEN → built-in fallback
5985
+ *
5986
+ * `POSTHOG_PROJECT_TOKEN` is the vendor-native name a consumer repo sets (as a
5987
+ * plain variable, not a secret — the `phc_…` key is publishable) to route CLI
5988
+ * usage to its own project.
5989
+ */
5990
+ function resolvePostHogKey() {
5991
+ return env.get("HOLOCRON_POSTHOG_PROJECT_TOKEN") ?? env.get("POSTHOG_PROJECT_TOKEN") ?? FALLBACK_POSTHOG_PROJECT_TOKEN;
5992
+ }
5993
+ function resolvePostHogHost() {
5994
+ return env.get("HOLOCRON_POSTHOG_HOST") ?? env.get("POSTHOG_HOST") ?? DEFAULT_POSTHOG_HOST;
5995
+ }
5996
+ function isEnabled() {
5997
+ return env.get("HOLOCRON_TELEMETRY") !== "false" && !env.get("NO_HOLOCRON_TELEMETRY");
5998
+ }
5999
+ const sentryEnabled = () => isEnabled() && resolveDsn() !== "";
6000
+ const posthogEnabled = () => isEnabled() && resolvePostHogKey() !== "";
6001
+ let posthog;
6002
+ let commandStartedAt = 0;
6003
+ let currentCommand = "unknown";
6004
+ /**
6005
+ * Anonymous, stable per-machine id — `sha256(hostname + username)` truncated
6006
+ * (ADR-0008). A one-way fingerprint: no raw hostname or username is ever
6007
+ * transmitted, and it is not personal data under this project's rule.
6008
+ */
6009
+ function machineId() {
6010
+ try {
6011
+ return createHash("sha256").update(`${hostname()} ${userInfo().username}`).digest("hex").slice(0, 32);
6012
+ } catch {
6013
+ return "unknown";
6014
+ }
6015
+ }
6016
+ /** Properties every event carries, so any event can be pivoted to its Axiom trace. */
6017
+ function baseProps() {
6018
+ const runId = getRunId();
6019
+ return {
6020
+ command: currentCommand,
6021
+ ci: Boolean(env.get("CI")),
6022
+ ...runId ? { runId } : {}
6023
+ };
6024
+ }
6025
+ /**
6026
+ * Capture a PostHog event. Runs every `properties` object through the same
6027
+ * token scrubber Sentry uses (defence-in-depth — values are controlled).
6028
+ */
6029
+ function event(name, properties = {}) {
6030
+ if (!posthogEnabled() || !posthog) return;
6031
+ const merged = {
6032
+ ...baseProps(),
6033
+ ...properties
6034
+ };
6035
+ posthog.capture({
6036
+ distinctId: machineId(),
6037
+ event: name,
6038
+ properties: JSON.parse(redact(JSON.stringify(merged)))
6039
+ });
6040
+ }
6041
+ function init(version) {
6042
+ initSentry(version);
6043
+ initPostHog(version);
6044
+ }
6045
+ function initSentry(version) {
6046
+ if (!sentryEnabled()) return;
6047
+ Sentry.init({
6048
+ dsn: resolveDsn(),
6049
+ release: `holocron@${version}`,
6050
+ environment: env.get("CI") ? "ci" : "local",
6051
+ tracesSampleRate: 1,
6052
+ beforeSend: scrubError
6053
+ });
6054
+ Sentry.startSession();
6055
+ Sentry.setTag("os", process.platform);
6056
+ Sentry.setTag("node", process.version);
6057
+ Sentry.setTag("ci", String(Boolean(env.get("CI"))));
6058
+ }
6059
+ function initPostHog(version) {
6060
+ if (!posthogEnabled()) return;
6061
+ posthog = new PostHog(resolvePostHogKey(), { host: resolvePostHogHost() });
6062
+ posthog.identify({
6063
+ distinctId: machineId(),
6064
+ properties: {
6065
+ ci: Boolean(env.get("CI")),
6066
+ os: process.platform,
6067
+ node: process.version,
6068
+ cli: version,
6069
+ ...env.get("HOLOCRON_ORG") ? { org: env.get("HOLOCRON_ORG") } : {}
6070
+ }
6071
+ });
6072
+ }
6073
+ let lastErrorType;
6074
+ /** Start a Sentry command span, or `undefined` when Sentry is off. */
6075
+ function startSpan(name) {
6076
+ if (!sentryEnabled()) return void 0;
6077
+ Sentry.setTag("command", name);
6078
+ return Sentry.startInactiveSpan({
6079
+ name,
6080
+ op: "holocron.command",
6081
+ forceTransaction: true
6082
+ });
6083
+ }
6084
+ function startCommand(name) {
6085
+ currentCommand = name;
6086
+ commandStartedAt = Date.now();
6087
+ lastErrorType = void 0;
6088
+ const span = startSpan(name);
6089
+ event("command_started", { dry_run: process.argv.includes("--dry-run") });
6090
+ return (ok) => {
6091
+ if (span) {
6092
+ span.setStatus({ code: ok ? 1 : 2 });
6093
+ span.end();
6094
+ }
6095
+ event(ok ? "command_completed" : "command_failed", {
6096
+ status: ok ? "ok" : "fail",
6097
+ duration_ms: Date.now() - commandStartedAt,
6098
+ ...ok || !lastErrorType ? {} : { error_type: lastErrorType }
6099
+ });
6100
+ };
6101
+ }
6102
+ function captureException(err) {
6103
+ lastErrorType = err instanceof Error ? err.constructor.name : "Error";
6104
+ if (!sentryEnabled()) return;
6105
+ Sentry.captureException(err);
6106
+ }
6107
+ function endSession() {
6108
+ if (!sentryEnabled()) return;
6109
+ Sentry.endSession();
6110
+ }
6111
+ async function flush() {
6112
+ if (sentryEnabled()) await Sentry.close(2e3);
6113
+ if (posthog) {
6114
+ await posthog.shutdown();
6115
+ posthog = void 0;
6116
+ }
6117
+ }
6118
+ const TOKEN_RE = /\b(ghp_|ghs_|glpat-|xoxb-|xoxp-|npm_|sk-|[A-Z][A-Z0-9_]{2,}_TOKEN[=\s])[^\s"]*/g;
6119
+ function redact(raw) {
6120
+ return raw.replace(TOKEN_RE, "[REDACTED]");
6121
+ }
6122
+ function scrubError(event, _hint) {
6123
+ return JSON.parse(redact(JSON.stringify(event)));
6124
+ }
6125
+ //#endregion
5963
6126
  //#region src/templates/actions/auto-commit.yml
5964
6127
  var auto_commit_default = "name: Auto Commit\ndescription: >\n Commit and push any file changes using stefanzweifel/git-auto-commit-action.\n If a branch name is provided and differs from the current branch, a new branch\n is created automatically.\n\ninputs:\n token:\n description: >\n GitHub token with push access to the target branch. Used to authenticate\n the git remote so the commit can be pushed.\n required: true\n branch:\n description: >\n Branch to commit to. Leave empty to commit to the current branch (useful\n in PR contexts). Provide an explicit name (e.g. chore/auto-sync) to push\n to a separate branch.\n required: false\n default: \"\"\n commit-message:\n description: Commit message.\n required: true\n commit-options:\n description: >\n Additional options passed to git commit (e.g. --no-verify).\n required: false\n default: \"\"\n commit-user-name:\n description: Git user.name for the commit.\n required: false\n default: \"github-actions[bot]\"\n commit-user-email:\n description: Git user.email for the commit.\n required: false\n default: \"41898282+github-actions[bot]@users.noreply.github.com\"\n commit-author:\n description: >\n Git author string in \"Name <email>\" format.\n required: false\n default: \"github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>\"\n\noutputs:\n changes-detected:\n description: \"'true' if any files were committed, 'false' otherwise.\"\n value: ${{ steps.commit.outputs.changes_detected }}\n\nruns:\n using: composite\n steps:\n - name: Configure git remote with token\n shell: bash\n run: |\n REMOTE_URL=$(git remote get-url origin)\n AUTHED_URL=$(echo \"$REMOTE_URL\" | sed \"s|https://|https://x-access-token:${TOKEN}@|\")\n git remote set-url origin \"$AUTHED_URL\"\n env:\n TOKEN: ${{ inputs.token }}\n\n - name: Reset branch to current HEAD\n if: ${{ inputs.branch != '' }}\n shell: bash\n run: git checkout -B \"$BRANCH\"\n env:\n BRANCH: ${{ inputs.branch }}\n\n - name: Build signed commit message\n id: msg\n shell: bash\n run: |\n TRAILER=\"Signed-off-by: ${COMMIT_USER_NAME} <${COMMIT_USER_EMAIL}>\"\n if echo \"$COMMIT_MESSAGE\" | grep -qF \"$TRAILER\"; then\n FINAL=\"$COMMIT_MESSAGE\"\n else\n FINAL=\"${COMMIT_MESSAGE}\"$'\\n\\n'\"${TRAILER}\"\n fi\n {\n echo \"value<<EOF_TRAILER\"\n printf '%s' \"$FINAL\"\n echo \"\"\n echo \"EOF_TRAILER\"\n } >> \"$GITHUB_OUTPUT\"\n env:\n COMMIT_MESSAGE: ${{ inputs.commit-message }}\n COMMIT_USER_NAME: ${{ inputs.commit-user-name }}\n COMMIT_USER_EMAIL: ${{ inputs.commit-user-email }}\n\n - uses: stefanzweifel/git-auto-commit-action@4a55954c782fc1ea30b9056cd3e7a2b40ca8887d # v7.2.0\n id: commit\n with:\n branch: ${{ inputs.branch }}\n commit_message: ${{ steps.msg.outputs.value }}\n commit_options: ${{ inputs.commit-options }}\n commit_user_name: ${{ inputs.commit-user-name }}\n commit_user_email: ${{ inputs.commit-user-email }}\n commit_author: ${{ inputs.commit-author }}\n";
5965
6128
  //#endregion
@@ -6006,13 +6169,13 @@ var security_default = "name: Security\n\non: # yamllint disable-line rule:truth
6006
6169
  var stale_default = "name: Stale\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n days-before-stale:\n description: Days of inactivity before an issue or PR is marked stale (applies to both unless type-specific value is set)\n type: number\n required: false\n default: 30\n days-before-close:\n description: Days after stale label before closing (applies to both unless type-specific value is set)\n type: number\n required: false\n default: 5\n days-before-issue-stale:\n description: Days of inactivity before an issue is marked stale; overrides days-before-stale when set to a non-negative value\n type: number\n required: false\n default: -1\n days-before-issue-close:\n description: Days after stale label before closing an issue; overrides days-before-close when set to a non-negative value\n type: number\n required: false\n default: -1\n days-before-pr-stale:\n description: Days of inactivity before a PR is marked stale; overrides days-before-stale when set to a non-negative value\n type: number\n required: false\n default: -1\n days-before-pr-close:\n description: Days after stale label before closing a PR; overrides days-before-close when set to a non-negative value\n type: number\n required: false\n default: -1\n exempt-issue-labels:\n description: Comma-separated labels that exempt an issue from being marked stale\n type: string\n required: false\n default: \"in-progress,wip\"\n exempt-pr-labels:\n description: Comma-separated labels that exempt a PR from being marked stale\n type: string\n required: false\n default: \"\"\n exempt-all-issue-milestones:\n description: Issues assigned to any milestone are never marked stale\n type: boolean\n required: false\n default: true\n exempt-all-pr-milestones:\n description: PRs assigned to any milestone are never marked stale\n type: boolean\n required: false\n default: true\n exempt-all-issue-projects:\n description: Issues assigned to any project are never marked stale\n type: boolean\n required: false\n default: true\n exempt-all-pr-projects:\n description: PRs assigned to any project are never marked stale\n type: boolean\n required: false\n default: true\n\njobs:\n stale:\n name: Mark stale issues and pull requests\n permissions:\n contents: write\n issues: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0\n name: Run Stale\n with:\n close-issue-message: >\n This issue was closed because it has been stalled for\n ${{ inputs.days-before-close }} days with no activity.\n days-before-close: ${{ inputs.days-before-close }}\n days-before-issue-close: ${{ inputs.days-before-issue-close }}\n days-before-issue-stale: ${{ inputs.days-before-issue-stale }}\n days-before-pr-close: ${{ inputs.days-before-pr-close }}\n days-before-pr-stale: ${{ inputs.days-before-pr-stale }}\n days-before-stale: ${{ inputs.days-before-stale }}\n exempt-all-issue-milestones: ${{ inputs.exempt-all-issue-milestones }}\n exempt-all-issue-projects: ${{ inputs.exempt-all-issue-projects }}\n exempt-all-pr-milestones: ${{ inputs.exempt-all-pr-milestones }}\n exempt-all-pr-projects: ${{ inputs.exempt-all-pr-projects }}\n exempt-issue-labels: ${{ inputs.exempt-issue-labels }}\n exempt-pr-labels: ${{ inputs.exempt-pr-labels }}\n stale-issue-label: wontfix\n stale-issue-message: >\n This issue is stale because it has been open ${{ inputs.days-before-stale }}\n days with no activity. Remove the stale label or comment, or this will be\n closed in ${{ inputs.days-before-close }} days.\n stale-pr-label: wontfix\n stale-pr-message: >\n This PR is stale because it has been open ${{ inputs.days-before-stale }}\n days with no activity. Remove the stale label or comment, or this will be\n closed in ${{ inputs.days-before-close }} days.\n";
6007
6170
  //#endregion
6008
6171
  //#region src/templates/workflows/sync.yml
6009
- var sync_default = "name: Sync\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n steps:\n description: >\n Sync steps to run (default: all). Valid values:\n labels, properties, teams, topics, keywords, description, homepage, readme, workflows, wiki.\n Pass a space-separated list to run a subset, e.g. \"readme\" or \"readme wiki\".\n type: string\n required: false\n secrets:\n HOLOCRON_ADMIN_TOKEN:\n description: Fine-grained PAT with admin scopes (labels, properties, teams).\n required: false\n HOLOCRON_AXIOM_TOKEN:\n description: >\n Axiom API token. When set alongside the HOLOCRON_AXIOM_DATASET\n repo/org variable, the CLI ships this run's structured logs to Axiom.\n Falls back to the vendor-native AXIOM_TOKEN secret.\n required: false\n AXIOM_TOKEN:\n description: Vendor-native fallback for HOLOCRON_AXIOM_TOKEN.\n required: false\n HOLOCRON_DEPLOY_TOKEN:\n description: Fine-grained PAT for GitHub Pages configuration.\n required: false\n HOLOCRON_ISSUES_TOKEN:\n description: Fine-grained PAT for issue management.\n required: false\n HOLOCRON_ORG_TOKEN:\n description: Org-scoped fine-grained PAT for team sync and org properties.\n required: false\n HOLOCRON_READ_TOKEN:\n description: Fine-grained PAT for read-only GitHub API calls.\n required: false\n HOLOCRON_SYNC_TOKEN:\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for gh CLI calls. Used when\n HOLOCRON_SYNC_TOKEN is not set.\n required: false\n\njobs:\n sync:\n name: Sync repo from config\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: write\n pull-requests: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Run holocron sync\n # Call the built entry directly — `pnpm exec holocron` relies on a\n # node_modules/.bin/holocron symlink that pnpm cannot create at install\n # time (dist/ does not exist yet), same as sync-github.yml.\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n node packages/cli/dist/cli.mjs sync --steps $STEPS\n else\n node packages/cli/dist/cli.mjs sync\n fi\n env:\n HOLOCRON_ADMIN_TOKEN: ${{ secrets.HOLOCRON_ADMIN_TOKEN }}\n HOLOCRON_AXIOM_TOKEN: ${{ secrets.HOLOCRON_AXIOM_TOKEN || secrets.AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET || vars.AXIOM_DATASET }}\n HOLOCRON_DEPLOY_TOKEN: ${{ secrets.HOLOCRON_DEPLOY_TOKEN }}\n HOLOCRON_ISSUES_TOKEN: ${{ secrets.HOLOCRON_ISSUES_TOKEN }}\n HOLOCRON_ORG_TOKEN: ${{ secrets.HOLOCRON_ORG_TOKEN }}\n HOLOCRON_READ_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\n\n - name: Format generated files\n run: |\n pnpm exec prettier --write README.md docs/src/content/docs/index.mdx 2>/dev/null || true\n # `holocron sync` writes package.json fields (keywords, description,\n # homepage) with a plain assignment, which appends new keys at the\n # end — re-apply the canonical order the lint config enforces.\n pnpm exec eslint --fix --no-warn-ignored package.json 2>/dev/null || true\n\n - uses: theholocron/.github/.github/actions/auto-commit@main\n id: auto-commit\n name: Commit sync changes\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n branch: chore/auto-sync\n commit-message: \"chore: sync from holocron.config\"\n commit-options: \"--no-verify\"\n\n - name: Open PR if changes were committed\n if: steps.auto-commit.outputs.changes-detected == 'true'\n run: |\n gh pr create \\\n --title \"chore: sync README and repo metadata\" \\\n --body \"Automated sync triggered by changes to config or package files. Merge to apply.\" \\\n --base main \\\n --head chore/auto-sync \\\n || echo \"PR already open — branch updated.\"\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - name: Broadcast wiki sync if navbar changed\n if: >-\n steps.auto-commit.outputs.changes-detected == 'true' &&\n github.event_name == 'push' &&\n (inputs.steps == '' || contains(inputs.steps, 'wiki'))\n run: |\n if git diff HEAD~1 --name-only | grep -q 'fern/docs.yml'; then\n gh workflow run sync-dispatch.yml \\\n --repo theholocron/.github \\\n --field \"steps=wiki\" \\\n || echo \"skipping broadcast — insufficient permissions\"\n fi\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n";
6172
+ var sync_default = "name: Sync\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n steps:\n description: >\n Sync steps to run (default: all). Valid values:\n labels, properties, teams, topics, keywords, description, homepage, readme, workflows, wiki.\n Pass a space-separated list to run a subset, e.g. \"readme\" or \"readme wiki\".\n type: string\n required: false\n secrets:\n HOLOCRON_ADMIN_TOKEN:\n description: Fine-grained PAT with admin scopes (labels, properties, teams).\n required: false\n HOLOCRON_AXIOM_TOKEN:\n description: >\n Axiom API token. When set alongside the HOLOCRON_AXIOM_DATASET\n repo/org variable, the CLI ships this run's structured logs to Axiom.\n Falls back to the vendor-native AXIOM_TOKEN secret.\n required: false\n AXIOM_TOKEN:\n description: Vendor-native fallback for HOLOCRON_AXIOM_TOKEN.\n required: false\n HOLOCRON_DEPLOY_TOKEN:\n description: Fine-grained PAT for GitHub Pages configuration.\n required: false\n HOLOCRON_ISSUES_TOKEN:\n description: Fine-grained PAT for issue management.\n required: false\n HOLOCRON_ORG_TOKEN:\n description: Org-scoped fine-grained PAT for team sync and org properties.\n required: false\n HOLOCRON_READ_TOKEN:\n description: Fine-grained PAT for read-only GitHub API calls.\n required: false\n HOLOCRON_SYNC_TOKEN:\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for gh CLI calls. Used when\n HOLOCRON_SYNC_TOKEN is not set.\n required: false\n\njobs:\n sync:\n name: Sync repo from config\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: write\n pull-requests: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Run holocron sync\n # Call the built entry directly — `pnpm exec holocron` relies on a\n # node_modules/.bin/holocron symlink that pnpm cannot create at install\n # time (dist/ does not exist yet), same as sync-github.yml.\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n node packages/cli/dist/cli.mjs sync --steps $STEPS\n else\n node packages/cli/dist/cli.mjs sync\n fi\n env:\n HOLOCRON_ADMIN_TOKEN: ${{ secrets.HOLOCRON_ADMIN_TOKEN }}\n HOLOCRON_AXIOM_TOKEN: ${{ secrets.HOLOCRON_AXIOM_TOKEN || secrets.AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET || vars.AXIOM_DATASET }}\n POSTHOG_PROJECT_TOKEN: ${{ vars.POSTHOG_PROJECT_TOKEN }}\n HOLOCRON_DEPLOY_TOKEN: ${{ secrets.HOLOCRON_DEPLOY_TOKEN }}\n HOLOCRON_ISSUES_TOKEN: ${{ secrets.HOLOCRON_ISSUES_TOKEN }}\n HOLOCRON_ORG_TOKEN: ${{ secrets.HOLOCRON_ORG_TOKEN }}\n HOLOCRON_READ_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\n\n - name: Format generated files\n run: |\n pnpm exec prettier --write README.md docs/src/content/docs/index.mdx 2>/dev/null || true\n # `holocron sync` writes package.json fields (keywords, description,\n # homepage) with a plain assignment, which appends new keys at the\n # end — re-apply the canonical order the lint config enforces.\n pnpm exec eslint --fix --no-warn-ignored package.json 2>/dev/null || true\n\n - uses: theholocron/.github/.github/actions/auto-commit@main\n id: auto-commit\n name: Commit sync changes\n with:\n token: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n branch: chore/auto-sync\n commit-message: \"chore: sync from holocron.config\"\n commit-options: \"--no-verify\"\n\n - name: Open PR if changes were committed\n if: steps.auto-commit.outputs.changes-detected == 'true'\n run: |\n gh pr create \\\n --title \"chore: sync README and repo metadata\" \\\n --body \"Automated sync triggered by changes to config or package files. Merge to apply.\" \\\n --base main \\\n --head chore/auto-sync \\\n || echo \"PR already open — branch updated.\"\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n\n - name: Broadcast wiki sync if navbar changed\n if: >-\n steps.auto-commit.outputs.changes-detected == 'true' &&\n github.event_name == 'push' &&\n (inputs.steps == '' || contains(inputs.steps, 'wiki'))\n run: |\n if git diff HEAD~1 --name-only | grep -q 'fern/docs.yml'; then\n gh workflow run sync-dispatch.yml \\\n --repo theholocron/.github \\\n --field \"steps=wiki\" \\\n || echo \"skipping broadcast — insufficient permissions\"\n fi\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n";
6010
6173
  //#endregion
6011
6174
  //#region src/templates/workflows/sync-dispatch.yml
6012
6175
  var sync_dispatch_default = "name: Sync Dispatch\n\non: # yamllint disable-line rule:truthy\n workflow_dispatch:\n inputs:\n steps:\n description: >\n Sync steps to pass to each repo's sync.yml. Default is \"readme\"\n (only README marker blocks are updated).\n type: string\n required: false\n default: readme\n\npermissions:\n contents: read\n\njobs:\n broadcast:\n name: Broadcast sync to all repos\n runs-on: ubuntu-latest\n timeout-minutes: 15\n steps:\n - name: Dispatch sync to all repos with sync.yml\n run: |\n gh api /orgs/theholocron/repos --paginate --jq '.[].name' \\\n | while IFS= read -r repo; do\n gh api \"/repos/theholocron/$repo/contents/.github/workflows/sync.yml\" --silent 2>/dev/null || continue\n echo \"Dispatching sync to theholocron/$repo\"\n gh workflow run sync.yml \\\n --repo \"theholocron/$repo\" \\\n --field \"steps=$STEPS\" \\\n || echo \"Warning: could not dispatch to theholocron/$repo — skipping\"\n done\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n STEPS: ${{ inputs.steps }}\n";
6013
6176
  //#endregion
6014
6177
  //#region src/templates/workflows/sync-github.yml
6015
- var sync_github_default = "name: Sync workflow templates\n\n# Builds the holocron CLI from source and pushes updated workflow templates\n# and composite actions to downstream .github repos. Runs whenever the\n# template source files change on main or alpha.\n#\n# Secrets required:\n# HOLOCRON_SYNC_TOKEN — fine-grained PAT (resource owner: org) with:\n# Actions: Read and write (dispatch workflow runs via gh workflow run)\n# Contents: Read and write (git trees, blobs, refs)\n# Pull requests: Read and write (open sync PR)\n# Workflows: Read and write (write .github/workflows/*.yml)\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n primary-repo:\n description: >\n Primary .github repo — receives composite actions, reusable workflows,\n and thin-caller templates. Requires a PR (branch protection assumed).\n type: string\n required: false\n default: theholocron/.github\n secondary-repos:\n description: >\n Space-separated list of secondary repos (reusable workflows + thin\n callers only, no composite actions). Changes are delivered via pull\n request, same as the primary repo.\n type: string\n required: false\n default: \"\"\n sync-branch:\n description: Branch name used for the primary and secondary repo PRs\n type: string\n required: false\n default: chore/sync-templates\n secrets:\n HOLOCRON_SYNC_TOKEN:\n required: true\n HOLOCRON_READ_TOKEN:\n description: >\n Fine-grained PAT for read-only GitHub API calls (e.g. resolving git\n committer identity via `gh api user`). Falls back to HOLOCRON_SYNC_TOKEN.\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for `gh` CLI calls. Used when neither\n HOLOCRON_READ_TOKEN nor HOLOCRON_SYNC_TOKEN is set.\n required: false\n HOLOCRON_AXIOM_TOKEN:\n description: >\n Axiom API token. When set alongside the HOLOCRON_AXIOM_DATASET\n repo/org variable, the CLI ships this run's structured logs to Axiom.\n Falls back to the vendor-native AXIOM_TOKEN secret.\n required: false\n AXIOM_TOKEN:\n description: Vendor-native fallback for HOLOCRON_AXIOM_TOKEN.\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n sync:\n name: Sync templates\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n HOLOCRON_AXIOM_TOKEN: ${{ secrets.HOLOCRON_AXIOM_TOKEN || secrets.AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET || vars.AXIOM_DATASET }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Cache actionlint\n id: cache-actionlint\n uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0\n with:\n path: /tmp/actionlint\n key: actionlint-v1.7.7-linux-amd64\n\n - name: Download actionlint\n if: steps.cache-actionlint.outputs.cache-hit != 'true'\n run: |\n curl -fsSL https://github.com/rhysd/actionlint/releases/download/v1.7.7/actionlint_1.7.7_linux_amd64.tar.gz \\\n | tar -xz -C /tmp actionlint\n\n - name: Validate generated workflows\n run: |\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$PRIMARY_REPO\" \\\n --output-dir /tmp/sync-validate\n /tmp/actionlint /tmp/sync-validate/.github/workflows/*.yml\n env:\n PRIMARY_REPO: ${{ inputs.primary-repo }}\n\n - name: Sync primary repo (PR)\n run: |\n GIT_NAME=$(gh api user --jq .name 2>/dev/null || echo \"github-actions[bot]\")\n GIT_EMAIL=$(gh api user --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"' 2>/dev/null || echo \"41898282+github-actions[bot]@users.noreply.github.com\")\n COMMIT_MSG=\"chore: sync from theholocron/holocron\"$'\\n\\n'\"Signed-off-by: $GIT_NAME <$GIT_EMAIL>\"\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$PRIMARY_REPO\" \\\n --branch \"$SYNC_BRANCH\" \\\n --pr \\\n --message \"$COMMIT_MSG\"\n GH_TOKEN=\"$HOLOCRON_SYNC_TOKEN\" gh pr merge --auto --squash \\\n --repo \"$PRIMARY_REPO\" \"$SYNC_BRANCH\" 2>/dev/null || true\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n PRIMARY_REPO: ${{ inputs.primary-repo }}\n SYNC_BRANCH: ${{ inputs.sync-branch }}\n\n - name: Sync secondary repos (PR)\n if: ${{ inputs.secondary-repos != '' }}\n run: |\n GIT_NAME=$(gh api user --jq .name 2>/dev/null || echo \"github-actions[bot]\")\n GIT_EMAIL=$(gh api user --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"' 2>/dev/null || echo \"41898282+github-actions[bot]@users.noreply.github.com\")\n COMMIT_MSG=\"chore: sync from theholocron/holocron\"$'\\n\\n'\"Signed-off-by: $GIT_NAME <$GIT_EMAIL>\"\n for repo in $SECONDARY_REPOS; do\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$repo\" \\\n --branch \"$SYNC_BRANCH\" \\\n --pr \\\n --message \"$COMMIT_MSG\"\n GH_TOKEN=\"$HOLOCRON_SYNC_TOKEN\" gh pr merge --auto --squash \\\n --repo \"$repo\" \"$SYNC_BRANCH\" 2>/dev/null || true\n done\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n SECONDARY_REPOS: ${{ inputs.secondary-repos }}\n SYNC_BRANCH: ${{ inputs.sync-branch }}\n";
6178
+ var sync_github_default = "name: Sync workflow templates\n\n# Builds the holocron CLI from source and pushes updated workflow templates\n# and composite actions to downstream .github repos. Runs whenever the\n# template source files change on main or alpha.\n#\n# Secrets required:\n# HOLOCRON_SYNC_TOKEN — fine-grained PAT (resource owner: org) with:\n# Actions: Read and write (dispatch workflow runs via gh workflow run)\n# Contents: Read and write (git trees, blobs, refs)\n# Pull requests: Read and write (open sync PR)\n# Workflows: Read and write (write .github/workflows/*.yml)\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n primary-repo:\n description: >\n Primary .github repo — receives composite actions, reusable workflows,\n and thin-caller templates. Requires a PR (branch protection assumed).\n type: string\n required: false\n default: theholocron/.github\n secondary-repos:\n description: >\n Space-separated list of secondary repos (reusable workflows + thin\n callers only, no composite actions). Changes are delivered via pull\n request, same as the primary repo.\n type: string\n required: false\n default: \"\"\n sync-branch:\n description: Branch name used for the primary and secondary repo PRs\n type: string\n required: false\n default: chore/sync-templates\n secrets:\n HOLOCRON_SYNC_TOKEN:\n required: true\n HOLOCRON_READ_TOKEN:\n description: >\n Fine-grained PAT for read-only GitHub API calls (e.g. resolving git\n committer identity via `gh api user`). Falls back to HOLOCRON_SYNC_TOKEN.\n required: false\n GH_TOKEN:\n description: >\n Generic GitHub token fallback for `gh` CLI calls. Used when neither\n HOLOCRON_READ_TOKEN nor HOLOCRON_SYNC_TOKEN is set.\n required: false\n HOLOCRON_AXIOM_TOKEN:\n description: >\n Axiom API token. When set alongside the HOLOCRON_AXIOM_DATASET\n repo/org variable, the CLI ships this run's structured logs to Axiom.\n Falls back to the vendor-native AXIOM_TOKEN secret.\n required: false\n AXIOM_TOKEN:\n description: Vendor-native fallback for HOLOCRON_AXIOM_TOKEN.\n required: false\n TURBO_TOKEN:\n required: false\n\njobs:\n sync:\n name: Sync templates\n runs-on: ubuntu-latest\n timeout-minutes: 15\n permissions:\n contents: read\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\n HOLOCRON_AXIOM_TOKEN: ${{ secrets.HOLOCRON_AXIOM_TOKEN || secrets.AXIOM_TOKEN }}\n HOLOCRON_AXIOM_DATASET: ${{ vars.HOLOCRON_AXIOM_DATASET || vars.AXIOM_DATASET }}\n POSTHOG_PROJECT_TOKEN: ${{ vars.POSTHOG_PROJECT_TOKEN }}\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - uses: theholocron/.github/.github/actions/setup@main\n name: Setup\n\n - run: pnpm build\n name: Build CLI\n\n - name: Cache actionlint\n id: cache-actionlint\n uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0\n with:\n path: /tmp/actionlint\n key: actionlint-v1.7.7-linux-amd64\n\n - name: Download actionlint\n if: steps.cache-actionlint.outputs.cache-hit != 'true'\n run: |\n curl -fsSL https://github.com/rhysd/actionlint/releases/download/v1.7.7/actionlint_1.7.7_linux_amd64.tar.gz \\\n | tar -xz -C /tmp actionlint\n\n - name: Validate generated workflows\n run: |\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$PRIMARY_REPO\" \\\n --output-dir /tmp/sync-validate\n /tmp/actionlint /tmp/sync-validate/.github/workflows/*.yml\n env:\n PRIMARY_REPO: ${{ inputs.primary-repo }}\n\n - name: Sync primary repo (PR)\n run: |\n GIT_NAME=$(gh api user --jq .name 2>/dev/null || echo \"github-actions[bot]\")\n GIT_EMAIL=$(gh api user --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"' 2>/dev/null || echo \"41898282+github-actions[bot]@users.noreply.github.com\")\n COMMIT_MSG=\"chore: sync from theholocron/holocron\"$'\\n\\n'\"Signed-off-by: $GIT_NAME <$GIT_EMAIL>\"\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$PRIMARY_REPO\" \\\n --branch \"$SYNC_BRANCH\" \\\n --pr \\\n --message \"$COMMIT_MSG\"\n GH_TOKEN=\"$HOLOCRON_SYNC_TOKEN\" gh pr merge --auto --squash \\\n --repo \"$PRIMARY_REPO\" \"$SYNC_BRANCH\" 2>/dev/null || true\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n PRIMARY_REPO: ${{ inputs.primary-repo }}\n SYNC_BRANCH: ${{ inputs.sync-branch }}\n\n - name: Sync secondary repos (PR)\n if: ${{ inputs.secondary-repos != '' }}\n run: |\n GIT_NAME=$(gh api user --jq .name 2>/dev/null || echo \"github-actions[bot]\")\n GIT_EMAIL=$(gh api user --jq '\"\\(.id)+\\(.login)@users.noreply.github.com\"' 2>/dev/null || echo \"41898282+github-actions[bot]@users.noreply.github.com\")\n COMMIT_MSG=\"chore: sync from theholocron/holocron\"$'\\n\\n'\"Signed-off-by: $GIT_NAME <$GIT_EMAIL>\"\n for repo in $SECONDARY_REPOS; do\n node packages/cli/dist/cli.mjs sync-github \\\n --repo \"$repo\" \\\n --branch \"$SYNC_BRANCH\" \\\n --pr \\\n --message \"$COMMIT_MSG\"\n GH_TOKEN=\"$HOLOCRON_SYNC_TOKEN\" gh pr merge --auto --squash \\\n --repo \"$repo\" \"$SYNC_BRANCH\" 2>/dev/null || true\n done\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_READ_TOKEN || secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN }}\n HOLOCRON_SYNC_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN }}\n SECONDARY_REPOS: ${{ inputs.secondary-repos }}\n SYNC_BRANCH: ${{ inputs.sync-branch }}\n";
6016
6179
  //#endregion
6017
6180
  //#region src/templates/workflows/tag.yml
6018
6181
  var tag_default = "name: Tag\n\n# Release Please — fully automated tag and GitHub Release from Conventional Commits.\n# No package.json or npm publishing required. Operates in \"simple\" mode by default:\n# analyzes commits since the last tag, maintains a rolling Release PR, and creates\n# a tag + GitHub Release when that PR is merged.\n#\n# The calling repo must have two files at the root:\n# release-please-config.json — declares packages and release-type\n# .release-please-manifest.json — tracks the current version\n\non: # yamllint disable-line rule:truthy\n # Self-trigger: when this workflow lives in theholocron/.github itself,\n # push to main runs Release Please for that repo's own releases.\n push:\n branches:\n - main\n workflow_call:\n inputs:\n release-type:\n description: Release Please release type (simple, node, python, etc.)\n type: string\n required: false\n default: simple\n config-file:\n description: Path to release-please-config.json\n type: string\n required: false\n default: release-please-config.json\n manifest-file:\n description: Path to .release-please-manifest.json\n type: string\n required: false\n default: .release-please-manifest.json\n\njobs:\n tag:\n name: Tag release\n permissions:\n contents: write\n pull-requests: write\n runs-on: ubuntu-latest\n timeout-minutes: 10\n steps:\n - uses: google-github-actions/release-please-action@e4dc86ba9405554aeba3c6bb2d169500e7d3b4ee # v4.1.1\n name: Run Release Please\n with:\n release-type: ${{ inputs.release-type }}\n config-file: ${{ inputs.config-file }}\n manifest-file: ${{ inputs.manifest-file }}\n";
@@ -6146,6 +6309,14 @@ async function runSyncGithub(input) {
6146
6309
  ...r.message ? { reason: r.message } : {},
6147
6310
  ...r.prUrl ? { pr: r.prUrl } : {}
6148
6311
  }, "sync-github: done");
6312
+ event("sync_github_run", {
6313
+ repo,
6314
+ status: r.status,
6315
+ ...branch ? { branch } : {},
6316
+ repos_targeted: 1,
6317
+ files_changed: r.created + r.updated,
6318
+ pr_opened: Boolean(r.prUrl)
6319
+ });
6149
6320
  return r;
6150
6321
  };
6151
6322
  if (input.outputDir) {
@@ -6631,70 +6802,6 @@ async function fileExists(path) {
6631
6802
  }
6632
6803
  }
6633
6804
  //#endregion
6634
- //#region src/telemetry.ts
6635
- const FALLBACK_DSN = "https://95cbb72ad5636c94e119a5405ee8f55f@o4508238154104832.ingest.us.sentry.io/4511810950791168";
6636
- /**
6637
- * Resolve the Sentry DSN — the `errors` capability's self-contained
6638
- * activation, per ADR-0007:
6639
- *
6640
- * HOLOCRON_SENTRY_DSN → SENTRY_DSN → built-in fallback
6641
- *
6642
- * A consumer repo with `SENTRY_DSN` set gets CLI errors routed to its own
6643
- * project with no config. Opt out entirely with `HOLOCRON_TELEMETRY=false`.
6644
- */
6645
- function resolveDsn() {
6646
- return env.get("HOLOCRON_SENTRY_DSN") ?? env.get("SENTRY_DSN") ?? FALLBACK_DSN;
6647
- }
6648
- function isEnabled() {
6649
- return !(env.get("HOLOCRON_TELEMETRY") === "false" || Boolean(env.get("NO_HOLOCRON_TELEMETRY"))) && resolveDsn() !== "";
6650
- }
6651
- function init(version) {
6652
- if (!isEnabled()) return;
6653
- Sentry.init({
6654
- dsn: resolveDsn(),
6655
- release: `holocron@${version}`,
6656
- environment: env.get("CI") ? "ci" : "local",
6657
- tracesSampleRate: 1,
6658
- beforeSend: scrubError
6659
- });
6660
- Sentry.startSession();
6661
- Sentry.setTag("os", process.platform);
6662
- Sentry.setTag("node", process.version);
6663
- Sentry.setTag("ci", String(Boolean(env.get("CI"))));
6664
- }
6665
- function startCommand(name) {
6666
- if (!isEnabled()) return () => {};
6667
- Sentry.setTag("command", name);
6668
- const span = Sentry.startInactiveSpan({
6669
- name,
6670
- op: "holocron.command",
6671
- forceTransaction: true
6672
- });
6673
- return (ok) => {
6674
- span.setStatus({ code: ok ? 1 : 2 });
6675
- span.end();
6676
- };
6677
- }
6678
- function captureException(err) {
6679
- if (!isEnabled()) return;
6680
- Sentry.captureException(err);
6681
- }
6682
- function endSession() {
6683
- if (!isEnabled()) return;
6684
- Sentry.endSession();
6685
- }
6686
- async function flush() {
6687
- if (!isEnabled()) return;
6688
- await Sentry.close(2e3);
6689
- }
6690
- const TOKEN_RE = /\b(ghp_|ghs_|glpat-|xoxb-|xoxp-|npm_|sk-|[A-Z][A-Z0-9_]{2,}_TOKEN[=\s])[^\s"]*/g;
6691
- function redact(raw) {
6692
- return raw.replace(TOKEN_RE, "[REDACTED]");
6693
- }
6694
- function scrubError(event, _hint) {
6695
- return JSON.parse(redact(JSON.stringify(event)));
6696
- }
6697
- //#endregion
6698
6805
  //#region src/update-notifier.ts
6699
6806
  const PACKAGE_NAME = "@theholocron/cli";
6700
6807
  const CACHE_TTL_MS = 1440 * 60 * 1e3;
@@ -6887,12 +6994,12 @@ try {
6887
6994
  describe: "Set the log level to error — suppress info and warn."
6888
6995
  }).middleware((argv) => {
6889
6996
  const name = argv._.slice(0, 2).join(" ") || "unknown";
6890
- finishCommand = startCommand(name);
6891
6997
  printRunId = Boolean(argv.debug || argv.verbose);
6892
6998
  buildCliLogger(argv, {
6893
6999
  command: name,
6894
7000
  org: argv.org
6895
7001
  });
7002
+ finishCommand = startCommand(name);
6896
7003
  }).command("version", "Print the CLI version", () => {}, () => {
6897
7004
  console.log(`holocron ${CLI_VERSION}`);
6898
7005
  }).command("clone", "Clone all repos in a GitHub org as siblings under a single directory", (y) => y.option("org", {