@theholocron/cli 3.48.0 → 3.50.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
@@ -3130,7 +3130,7 @@ var labeler_default = "bug:\n - '^fix'\n\nchore:\n - '^chore(?!\\(deps)'\n\nci
3130
3130
  * Files are overwritten on each setup run — they are generated artifacts.
3131
3131
  */
3132
3132
  const WORKFLOW_TEMPLATES = {
3133
- lint: "name: Lint\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: lint-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: write\n issues: write\n statuses: write\n\njobs:\n lint:\n name: Lint\n uses: theholocron/.github/.github/workflows/lint.yml@main\n secrets: inherit\n with:\n enable-auto-commit: true\n",
3133
+ lint: "name: Lint\n\non: # yamllint disable-line rule:truthy\n push:\n branches: [main, alpha]\n pull_request:\n\nconcurrency:\n group: lint-${{ github.ref }}\n cancel-in-progress: true\n\npermissions:\n contents: write\n issues: write\n statuses: write\n\njobs:\n lint:\n name: Lint\n uses: theholocron/.github/.github/workflows/lint.yml@main\n secrets: inherit\n",
3134
3134
  test: "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 statuses: write\n\njobs:\n test:\n name: Test\n uses: theholocron/.github/.github/workflows/test.yml@main\n with:\n run-unit: true\n secrets: inherit\n",
3135
3135
  typecheck: "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",
3136
3136
  security: "name: Security\n\non: # yamllint disable-line rule:truthy\n push:\n branches:\n - main\n pull_request:\n branches:\n - main\n schedule:\n - cron: \"0 0 * * 1\"\n\npermissions:\n actions: read\n contents: read\n security-events: write\n\njobs:\n security:\n uses: theholocron/.github/.github/workflows/security.yml@main\n secrets: inherit\n",
@@ -4239,7 +4239,11 @@ async function runSetup(input) {
4239
4239
  for (const entry of workflows) {
4240
4240
  const name = typeof entry === "string" ? entry : entry.name;
4241
4241
  const rawWith = typeof entry === "object" ? entry.with : void 0;
4242
- const withOverrides = rawWith ? normalizeWorkflowWith(rawWith) : void 0;
4242
+ const normalized = rawWith ? normalizeWorkflowWith(rawWith) : void 0;
4243
+ const withOverrides = name === "lint" ? {
4244
+ "enable-auto-commit": true,
4245
+ ...normalized ?? {}
4246
+ } : normalized;
4243
4247
  const additionalPaths = (typeof entry === "object" ? entry.paths : void 0) ?? (name === "deploy" && rawWith ? deriveDeployPaths(rawWith) : void 0);
4244
4248
  if (name === "test" && withOverrides) {
4245
4249
  const runUnit = withOverrides["run-unit"];
@@ -4891,8 +4895,8 @@ function deriveBasepath(domain, repoName) {
4891
4895
  }
4892
4896
  return repoName;
4893
4897
  }
4894
- function titleCase(s) {
4895
- return s.charAt(0).toUpperCase() + s.slice(1);
4898
+ function toNavLabel(basepath) {
4899
+ return basepath.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
4896
4900
  }
4897
4901
  function extractFromJson(raw, repoName) {
4898
4902
  let config;
@@ -4905,40 +4909,31 @@ function extractFromJson(raw, repoName) {
4905
4909
  if (!providers?.wiki) return null;
4906
4910
  const wikiEntry = providers.wiki;
4907
4911
  let domain;
4908
- let subtitle;
4909
- let icon;
4910
4912
  if (Array.isArray(wikiEntry) && wikiEntry.length === 2) {
4911
4913
  const opts = wikiEntry[1];
4912
4914
  domain = typeof opts.domain === "string" ? opts.domain : void 0;
4913
- subtitle = typeof opts.subtitle === "string" ? opts.subtitle : void 0;
4914
- icon = typeof opts.icon === "string" ? opts.icon : void 0;
4915
4915
  }
4916
- if (!subtitle && typeof config.description === "string") subtitle = config.description;
4917
4916
  const basepath = deriveBasepath(domain, repoName);
4918
4917
  return {
4919
- displayName: titleCase(basepath),
4918
+ displayName: toNavLabel(basepath),
4920
4919
  basepath,
4921
- ...subtitle ? { subtitle } : {},
4922
- ...icon ? { icon } : {}
4920
+ ...domain ? { domain } : {}
4923
4921
  };
4924
4922
  }
4925
4923
  function extractFromTs(raw, repoName) {
4926
4924
  const hasWikiProvider = /providers\s*:\s*\{[^}]*\bwiki\b/s.test(raw);
4927
4925
  const hasWikiPreset = /\bwikiCapability\b|\bwiki\s*\(\s*\)/.test(raw);
4926
+ /* c8 ignore next */
4928
4927
  if (!hasWikiProvider && !hasWikiPreset) return null;
4929
4928
  const domain = raw.match(/\bdomain\s*:\s*["']([^"']+)["']/)?.[1];
4930
- let subtitle = raw.match(/\bsubtitle\s*:\s*["']([^"']+)["']/)?.[1];
4931
- const icon = raw.match(/\bicon\s*:\s*["']([^"']+)["']/)?.[1];
4932
- if (!subtitle) subtitle = raw.match(/\bdescription\s*:\s*["']([^"']+)["']/)?.[1];
4933
4929
  const basepath = deriveBasepath(domain, repoName);
4934
4930
  return {
4935
- displayName: titleCase(basepath),
4931
+ displayName: toNavLabel(basepath),
4936
4932
  basepath,
4937
- ...subtitle ? { subtitle } : {},
4938
- ...icon ? { icon } : {}
4933
+ ...domain ? { domain } : {}
4939
4934
  };
4940
4935
  }
4941
- async function discoverWikiProducts(org, token, fetchFn) {
4936
+ async function discoverWikiRepos(org, token, fetchFn) {
4942
4937
  const rest = createRestClient({
4943
4938
  baseUrl: "https://api.github.com",
4944
4939
  token,
@@ -4961,52 +4956,81 @@ async function discoverWikiProducts(org, token, fetchFn) {
4961
4956
  if (batch.length < 100) break;
4962
4957
  page++;
4963
4958
  }
4964
- const products = [];
4959
+ const repos = [];
4965
4960
  for (const repo of allRepos.filter((r) => !r.archived)) {
4966
- let product = null;
4961
+ let found = null;
4967
4962
  try {
4968
4963
  const contents = await rest.request(`/repos/${repo.full_name}/contents/holocron.config.json`);
4969
- if (contents.encoding === "base64") product = extractFromJson(Buffer.from(contents.content.replace(/\s/g, ""), "base64").toString("utf8"), repo.name);
4964
+ if (contents.encoding === "base64") found = extractFromJson(Buffer.from(contents.content.replace(/\s/g, ""), "base64").toString("utf8"), repo.name);
4970
4965
  } catch {}
4971
- if (!product) try {
4966
+ if (!found) try {
4972
4967
  const contents = await rest.request(`/repos/${repo.full_name}/contents/holocron.config.ts`);
4973
- if (contents.encoding === "base64") product = extractFromTs(Buffer.from(contents.content.replace(/\s/g, ""), "base64").toString("utf8"), repo.name);
4968
+ if (contents.encoding === "base64") found = extractFromTs(Buffer.from(contents.content.replace(/\s/g, ""), "base64").toString("utf8"), repo.name);
4974
4969
  } catch {}
4975
- if (product) products.push(product);
4970
+ if (found) repos.push(found);
4976
4971
  }
4977
- products.sort((a, b) => a.basepath.localeCompare(b.basepath));
4978
- return products;
4972
+ repos.sort((a, b) => a.basepath.localeCompare(b.basepath));
4973
+ return repos;
4979
4974
  }
4980
- function buildProductsBlock(products) {
4981
- const lines = ["products:"];
4982
- for (const p of products) {
4983
- lines.push(` - display-name: ${p.displayName}`);
4984
- if (p.subtitle) lines.push(` subtitle: ${p.subtitle}`);
4985
- if (p.icon) lines.push(` icon: ${p.icon}`);
4986
- lines.push(` href: /${p.basepath}`);
4975
+ /**
4976
+ * Builds the complete navbar-links YAML block for a given repo.
4977
+ *
4978
+ * Includes:
4979
+ * - A GitHub button linking to the repo itself (always first)
4980
+ * - Minimal nav links to every other wiki-enabled repo (excluding self)
4981
+ *
4982
+ * The block is replaced wholesale on each sync so removed wikis disappear
4983
+ * and new ones appear automatically.
4984
+ */
4985
+ function buildNavbarLinks(repos, currentBasepath, repoFullName) {
4986
+ const lines = [
4987
+ `navbar-links:`,
4988
+ ` - type: github`,
4989
+ ` value: https://github.com/${repoFullName}`
4990
+ ];
4991
+ for (const repo of repos) {
4992
+ if (repo.basepath === currentBasepath) continue;
4993
+ const url = repo.domain ? `https://${repo.domain}` : `https://wiki.theholocron.dev/${repo.basepath}`;
4994
+ lines.push(` - type: minimal`, ` value: ${url}`, ` label: ${repo.displayName}`);
4987
4995
  }
4988
4996
  return lines.join("\n");
4989
4997
  }
4990
- async function mergeProducts(docsYmlPath, products) {
4998
+ /**
4999
+ * Idempotently ensures `edit-this-page:` (inside instances) is present and
5000
+ * replaces `navbar-links:` with the provided shared block.
5001
+ *
5002
+ * Returns true when the file was modified.
5003
+ * Throws when fern/docs.yml does not exist.
5004
+ */
5005
+ async function mergeWikiConfig(docsYmlPath, config) {
4991
5006
  let content;
4992
5007
  try {
4993
5008
  content = await readFile(docsYmlPath, "utf8");
4994
5009
  } catch {
4995
5010
  throw new Error(`fern/docs.yml not found at ${docsYmlPath}`);
4996
5011
  }
4997
- const newBlock = buildProductsBlock(products);
4998
- const productBlockRe = /^products:(?:\n[ \t][^\n]*)*/m;
4999
- if (productBlockRe.test(content)) {
5000
- const updated = content.replace(productBlockRe, newBlock);
5001
- if (updated !== content) await writeFile(docsYmlPath, updated, "utf8");
5002
- return;
5003
- }
5004
- const instancesIdx = content.indexOf("\ninstances:");
5005
- if (instancesIdx !== -1) {
5006
- await writeFile(docsYmlPath, content.slice(0, instancesIdx + 1) + newBlock + "\n\n" + content.slice(instancesIdx + 1), "utf8");
5007
- return;
5012
+ let updated = content;
5013
+ if (!updated.includes("edit-this-page:")) {
5014
+ const editBlock = [
5015
+ ` edit-this-page:`,
5016
+ ` github:`,
5017
+ ` owner: ${config.owner}`,
5018
+ ` repo: ${config.repoName}`,
5019
+ ` branch: main`
5020
+ ].join("\n");
5021
+ if (/^ {4}multi-source: true$/m.test(updated)) updated = updated.replace(/^( {4}multi-source: true)$/m, `$1\n${editBlock}`);
5022
+ else if (/^ {4}custom-domain: .+$/m.test(updated)) updated = updated.replace(/^( {4}custom-domain: .+)$/m, `$1\n${editBlock}`);
5023
+ else updated = updated.replace(/^( {2}- url: .+)$/m, `$1\n${editBlock}`);
5024
+ }
5025
+ const navbarRe = /^navbar-links:(?:\n[ \t][^\n]*)*/m;
5026
+ if (navbarRe.test(updated)) updated = updated.replace(navbarRe, config.navbarBlock);
5027
+ else if (/^colors:/m.test(updated)) updated = updated.replace(/^(colors:)/m, `${config.navbarBlock}\n\n$1`);
5028
+ else updated = updated.trimEnd() + "\n\n" + config.navbarBlock + "\n";
5029
+ if (updated !== content) {
5030
+ await writeFile(docsYmlPath, updated, "utf8");
5031
+ return true;
5008
5032
  }
5009
- await writeFile(docsYmlPath, content.trimEnd() + "\n\n" + newBlock + "\n", "utf8");
5033
+ return false;
5010
5034
  }
5011
5035
  async function runSyncWiki(input) {
5012
5036
  const config = input.loaded.resolved;
@@ -5017,13 +5041,21 @@ async function runSyncWiki(input) {
5017
5041
  status: "skip",
5018
5042
  message: "no wiki provider configured"
5019
5043
  };
5020
- const org = config.org ?? input.context.repo?.split("/")[0];
5021
- if (!org) return {
5044
+ const rawRepo = input.context.repo ?? config.repo?.name;
5045
+ if (!rawRepo) return {
5046
+ capability: "local",
5047
+ step: "sync wiki",
5048
+ status: "skip",
5049
+ message: "no repo configured"
5050
+ };
5051
+ const [owner, repoName] = rawRepo.split("/");
5052
+ if (!owner || !repoName) return {
5022
5053
  capability: "local",
5023
5054
  step: "sync wiki",
5024
5055
  status: "skip",
5025
- message: "no org configured"
5056
+ message: "repo must be owner/name format"
5026
5057
  };
5058
+ const org = config.org ?? owner;
5027
5059
  const token = resolveToken(input);
5028
5060
  if (!token) return {
5029
5061
  capability: "local",
@@ -5038,26 +5070,24 @@ async function runSyncWiki(input) {
5038
5070
  };
5039
5071
  const docsYmlPath = join(input.context.repoRoot, "fern", "docs.yml");
5040
5072
  try {
5041
- const products = await discoverWikiProducts(org, token, input.fetch);
5042
- if (products.length === 0) return {
5043
- capability: "local",
5044
- step: "sync wiki",
5045
- status: "skip",
5046
- message: "no wiki-enabled repos found"
5047
- };
5048
- await mergeProducts(docsYmlPath, products);
5073
+ const repos = await discoverWikiRepos(org, token, input.fetch);
5074
+ const wikiOpts = config.providers.wiki?.tuple?.options;
5049
5075
  return {
5050
5076
  capability: "local",
5051
5077
  step: "sync wiki",
5052
5078
  status: "ok",
5053
- message: `${products.length} products`
5079
+ message: await mergeWikiConfig(docsYmlPath, {
5080
+ owner,
5081
+ repoName,
5082
+ navbarBlock: buildNavbarLinks(repos, deriveBasepath(typeof wikiOpts?.domain === "string" ? wikiOpts.domain : void 0, repoName), rawRepo)
5083
+ }) ? `updated (${repos.length} wiki repos discovered)` : "already up to date"
5054
5084
  };
5055
5085
  } catch (err) {
5056
5086
  return {
5057
5087
  capability: "local",
5058
5088
  step: "sync wiki",
5059
5089
  status: "fail",
5060
- message: err instanceof Error ? err.message : String(err)
5090
+ message: err instanceof Error ? err.message : /* c8 ignore next */ String(err)
5061
5091
  };
5062
5092
  }
5063
5093
  }
@@ -5321,7 +5351,11 @@ async function runSync(input) {
5321
5351
  } else for (const entry of workflowEntries) {
5322
5352
  const name = typeof entry === "string" ? entry : entry.name;
5323
5353
  const rawWith = typeof entry === "object" ? entry.with : void 0;
5324
- const withOverrides = rawWith ? normalizeWorkflowWith(rawWith) : void 0;
5354
+ const normalized = rawWith ? normalizeWorkflowWith(rawWith) : void 0;
5355
+ const withOverrides = name === "lint" ? {
5356
+ "enable-auto-commit": true,
5357
+ ...normalized ?? {}
5358
+ } : normalized;
5325
5359
  const additionalPaths = (typeof entry === "object" ? entry.paths : void 0) ?? (name === "deploy" && rawWith ? deriveDeployPaths(rawWith) : void 0);
5326
5360
  if (!KNOWN_WORKFLOWS.has(name)) {
5327
5361
  steps.push({
@@ -5501,7 +5535,7 @@ var security_default = "name: Security\n\non: # yamllint disable-line rule:truth
5501
5535
  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 is marked stale\n type: number\n required: false\n default: 30\n days-before-close:\n description: Days of inactivity after stale label before closing\n type: number\n required: false\n default: 5\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-stale: ${{ inputs.days-before-stale }}\n exempt-all-pr-milestones: true\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";
5502
5536
  //#endregion
5503
5537
  //#region src/templates/workflows/sync.yml
5504
- 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.\n Pass a space-separated list to run a subset, e.g. \"readme\" or \"readme description\".\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_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 - name: Run holocron sync\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n pnpm --workspace-root exec holocron sync --steps $STEPS\n else\n pnpm --workspace-root exec holocron sync\n fi\n env:\n HOLOCRON_ADMIN_TOKEN: ${{ secrets.HOLOCRON_ADMIN_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: pnpm exec prettier --write README.md docs/src/content/docs/index.mdx 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";
5538
+ 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_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 - name: Run holocron sync\n run: |\n if [ -n \"$STEPS\" ]; then\n # shellcheck disable=SC2086\n pnpm --workspace-root exec holocron sync --steps $STEPS\n else\n pnpm --workspace-root exec holocron sync\n fi\n env:\n HOLOCRON_ADMIN_TOKEN: ${{ secrets.HOLOCRON_ADMIN_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: pnpm exec prettier --write README.md docs/src/content/docs/index.mdx 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: Propagate wiki nav to all repos\n if: >-\n steps.auto-commit.outputs.changes-detected == 'true' &&\n (inputs.steps == '' || contains(inputs.steps, 'wiki'))\n run: |\n gh workflow run sync-dispatch.yml \\\n --repo theholocron/.github \\\n --field \"steps=wiki\"\n env:\n GH_TOKEN: ${{ secrets.HOLOCRON_SYNC_TOKEN || secrets.GH_TOKEN || github.token }}\n";
5505
5539
  //#endregion
5506
5540
  //#region src/templates/workflows/sync-dispatch.yml
5507
5541
  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";
@@ -5519,7 +5553,7 @@ var test_default = "name: Test\n\non: # yamllint disable-line rule:truthy\n wor
5519
5553
  var typecheck_default = "name: Typecheck\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n secrets:\n TURBO_TOKEN:\n required: false\n\njobs:\n typecheck:\n name: tsc --noEmit\n permissions:\n contents: read\n runs-on: ubuntu-latest\n timeout-minutes: 10\n env:\n TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}\n TURBO_TEAM: ${{ vars.TURBO_TEAM }}\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 typecheck\n name: Type check\n\n\n conclusion:\n name: Conclusion\n runs-on: ubuntu-latest\n if: always()\n needs: [typecheck]\n steps:\n - name: Check job statuses\n run: |\n if [[ \"$RESULTS\" == *\"failure\"* ]] || [[ \"$RESULTS\" == *\"cancelled\"* ]]; then\n exit 1\n fi\n env:\n RESULTS: ${{ join(needs.*.result, ',') }}\n";
5520
5554
  //#endregion
5521
5555
  //#region src/templates/workflows/wiki.yml
5522
- var wiki_default = "name: Wiki\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n fern-version:\n description: >\n Fern CLI version to install. Pin this to avoid breaking changes when\n Fern updates their config schema.\n type: string\n required: false\n default: \"5.35.4\"\n preview:\n description: >\n When true, publishes a preview instead of production.\n Requires preview-id to be set.\n type: boolean\n required: false\n default: false\n preview-id:\n description: >\n Stable ID for the preview URL. The preview is accessible at\n {fern-org}-preview-{id}.docs.buildwithfern.com. Use the PR number\n (e.g. \"pr-123\") so the same URL is reused on every push to\n the branch.\n type: string\n required: false\n default: \"\"\n fern-org:\n description: >\n Fern workspace org slug (e.g. \"holocron\"). When set, a GitHub\n deployment is created after a successful preview so the URL appears\n in the PR sidebar widget — the same pattern as Cloudflare Pages previews.\n type: string\n required: false\n default: \"\"\n base-path:\n description: >\n Basepath appended to the preview URL (e.g. \"holocron\" when using\n multi-source routing with wiki.theholocron.dev/holocron).\n Omit for single-instance Fern sites.\n type: string\n required: false\n default: \"\"\n secrets:\n HOLOCRON_FERN_TOKEN:\n required: false\n FERN_TOKEN:\n required: false\n\njobs:\n publish:\n name: ${{ inputs.preview && 'Preview' || 'Publish' }} to Wiki\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n deployments: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - name: Install Fern CLI\n run: npm install -g \"fern-api@$FERN_VERSION\"\n env:\n FERN_VERSION: ${{ inputs.fern-version }}\n\n - name: Publish docs\n if: ${{ !inputs.preview }}\n run: fern generate --docs\n env:\n FERN_TOKEN: ${{ secrets.HOLOCRON_FERN_TOKEN || secrets.FERN_TOKEN }}\n\n - name: Preview docs\n if: ${{ inputs.preview && inputs.preview-id != '' }}\n run: fern generate --docs --preview --id \"$PREVIEW_ID\"\n env:\n FERN_TOKEN: ${{ secrets.HOLOCRON_FERN_TOKEN || secrets.FERN_TOKEN }}\n PREVIEW_ID: ${{ inputs.preview-id }}\n\n - name: Report preview URL\n if: ${{ inputs.preview && inputs.preview-id != '' && inputs.fern-org != '' }}\n env:\n GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n FERN_ORG: ${{ inputs.fern-org }}\n PREVIEW_ID: ${{ inputs.preview-id }}\n BASE_PATH: ${{ inputs.base-path }}\n run: |\n PREVIEW_URL=\"https://${FERN_ORG}-preview-${PREVIEW_ID}.docs.buildwithfern.com${BASE_PATH:+/${BASE_PATH}}\"\n PAYLOAD=$(printf '{\"ref\":\"%s\",\"environment\":\"wiki (Preview)\",\"description\":\"Wiki\",\"production_environment\":false,\"auto_merge\":false,\"required_contexts\":[]}' \\\n \"$GITHUB_HEAD_REF\")\n DEPLOY_ID=$(echo \"$PAYLOAD\" | gh api \"repos/${GITHUB_REPOSITORY}/deployments\" \\\n --method POST --input - | jq -r '.id')\n gh api \"repos/${GITHUB_REPOSITORY}/deployments/${DEPLOY_ID}/statuses\" \\\n --method POST --field state=success --field environment_url=\"$PREVIEW_URL\"\n";
5556
+ var wiki_default = "name: Wiki\n\non: # yamllint disable-line rule:truthy\n workflow_call:\n inputs:\n fern-version:\n description: >\n Fern CLI version to install. Pin this to avoid breaking changes when\n Fern updates their config schema.\n type: string\n required: false\n default: \"5.114.1\"\n preview:\n description: >\n When true, publishes a preview instead of production.\n Requires preview-id to be set.\n type: boolean\n required: false\n default: false\n preview-id:\n description: >\n Stable ID for the preview URL. The preview is accessible at\n {fern-org}-preview-{id}.docs.buildwithfern.com. Use the PR number\n (e.g. \"pr-123\") so the same URL is reused on every push to\n the branch.\n type: string\n required: false\n default: \"\"\n fern-org:\n description: >\n Fern workspace org slug (e.g. \"holocron\"). When set, a GitHub\n deployment is created after a successful preview so the URL appears\n in the PR sidebar widget — the same pattern as Cloudflare Pages previews.\n type: string\n required: false\n default: \"\"\n base-path:\n description: >\n Basepath appended to the preview URL (e.g. \"holocron\" when using\n multi-source routing with wiki.theholocron.dev/holocron).\n Omit for single-instance Fern sites.\n type: string\n required: false\n default: \"\"\n secrets:\n HOLOCRON_FERN_TOKEN:\n required: false\n FERN_TOKEN:\n required: false\n\njobs:\n publish:\n name: ${{ inputs.preview && 'Preview' || 'Publish' }} to Wiki\n runs-on: ubuntu-latest\n timeout-minutes: 10\n permissions:\n contents: read\n deployments: write\n steps:\n - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0\n name: Checkout repository\n\n - name: Install Fern CLI\n run: npm install -g \"fern-api@$FERN_VERSION\"\n env:\n FERN_VERSION: ${{ inputs.fern-version }}\n\n - name: Publish docs\n if: ${{ !inputs.preview }}\n run: fern generate --docs\n env:\n FERN_TOKEN: ${{ secrets.HOLOCRON_FERN_TOKEN || secrets.FERN_TOKEN }}\n\n - name: Preview docs\n if: ${{ inputs.preview && inputs.preview-id != '' }}\n run: fern generate --docs --preview --id \"$PREVIEW_ID\"\n env:\n FERN_TOKEN: ${{ secrets.HOLOCRON_FERN_TOKEN || secrets.FERN_TOKEN }}\n PREVIEW_ID: ${{ inputs.preview-id }}\n\n - name: Report preview URL\n if: ${{ inputs.preview && inputs.preview-id != '' && inputs.fern-org != '' }}\n env:\n GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n FERN_ORG: ${{ inputs.fern-org }}\n PREVIEW_ID: ${{ inputs.preview-id }}\n BASE_PATH: ${{ inputs.base-path }}\n run: |\n PREVIEW_URL=\"https://${FERN_ORG}-preview-${PREVIEW_ID}.docs.buildwithfern.com${BASE_PATH:+/${BASE_PATH}}\"\n PAYLOAD=$(printf '{\"ref\":\"%s\",\"environment\":\"wiki (Preview)\",\"description\":\"Wiki\",\"production_environment\":false,\"auto_merge\":false,\"required_contexts\":[]}' \\\n \"$GITHUB_HEAD_REF\")\n DEPLOY_ID=$(echo \"$PAYLOAD\" | gh api \"repos/${GITHUB_REPOSITORY}/deployments\" \\\n --method POST --input - | jq -r '.id')\n gh api \"repos/${GITHUB_REPOSITORY}/deployments/${DEPLOY_ID}/statuses\" \\\n --method POST --field state=success --field environment_url=\"$PREVIEW_URL\"\n";
5523
5557
  //#endregion
5524
5558
  //#region src/templates/index.ts
5525
5559
  /**