@theholocron/cli 4.18.3 → 5.0.0-alpha.3

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
@@ -2154,12 +2154,11 @@ function render$10(inputs) {
2154
2154
  main: "./src/index.ts",
2155
2155
  exports: { ".": "./src/index.ts" },
2156
2156
  scripts: {
2157
- build: "tsdown",
2158
- lint: "eslint .",
2159
- typecheck: "tsc --noEmit",
2160
- test: "vitest run",
2157
+ "delivery.build": "tsdown",
2158
+ "sourceQuality.staticAnalysis": "holocron run sourceQuality.staticAnalysis --",
2159
+ "verification.typeSafety": "holocron run verification.typeSafety --",
2160
+ "verification.unitTests": "holocron run verification.unitTests --",
2161
2161
  "test:watch": "vitest",
2162
- "test:coverage": "vitest run --coverage",
2163
2162
  validate: "tsx scripts/validate.mjs"
2164
2163
  },
2165
2164
  peerDependencies: { "@theholocron/cli": "workspace:*" },
@@ -2879,7 +2878,7 @@ function runPluginCreate(input) {
2879
2878
  execFn("pnpm", [
2880
2879
  "--filter",
2881
2880
  pkg,
2882
- "typecheck"
2881
+ "verification.typeSafety"
2883
2882
  ], {
2884
2883
  cwd,
2885
2884
  stdio: "inherit"
@@ -2887,7 +2886,7 @@ function runPluginCreate(input) {
2887
2886
  execFn("pnpm", [
2888
2887
  "--filter",
2889
2888
  pkg,
2890
- "lint"
2889
+ "sourceQuality.staticAnalysis"
2891
2890
  ], {
2892
2891
  cwd,
2893
2892
  stdio: "inherit"
@@ -2895,7 +2894,7 @@ function runPluginCreate(input) {
2895
2894
  execFn("pnpm", [
2896
2895
  "--filter",
2897
2896
  pkg,
2898
- "test"
2897
+ "verification.unitTests"
2899
2898
  ], {
2900
2899
  cwd,
2901
2900
  stdio: "inherit"
@@ -3018,6 +3017,193 @@ function printNextSteps$1(print, inputs) {
3018
3017
  * "revoke this token at <url>" reminder — same pattern as `rando vc
3019
3018
  * setup` for the ephemeral GH admin PAT.
3020
3019
  */
3020
+ /**
3021
+ * `holocron publish --sync-trust <old-file> <new-file>` — bulk-migrates npm
3022
+ * Trusted Publisher (OIDC) config to a new workflow filename across every
3023
+ * public package. See issue #689 / `scripts/npm-trust-migrate.sh` (the
3024
+ * shell-script prototype this supersedes) for the full "why": npm ties
3025
+ * Trusted Publisher to a workflow FILENAME per package, so renaming the
3026
+ * release workflow breaks OIDC publish (`ENEEDAUTH`) until every package's
3027
+ * config is updated to match.
3028
+ *
3029
+ * Requires npm@11.15.0+ (`npm trust`) — always shells out through
3030
+ * `npx -y npm@latest` regardless of the locally-installed npm version.
3031
+ *
3032
+ * npm's `trust` mutations — and reads, once a short-lived OTP grant lapses —
3033
+ * require a fresh interactive browser 2FA approval (an `EOTP` error with an
3034
+ * `authUrl`). No token bypasses this (npm's own docs: Granular Access Tokens
3035
+ * with the bypass-2FA option are explicitly unsupported for trust commands).
3036
+ * This command can't drive that browser step itself, so on the first `EOTP`
3037
+ * it stops immediately, prints the `authUrl`, and returns — the operator
3038
+ * approves it and re-runs; already-migrated packages are skipped on the next
3039
+ * pass, so it's safe to resume from wherever it stopped.
3040
+ */
3041
+ async function runSyncTrust(input) {
3042
+ const print = input.print ?? ((line) => console.log(line));
3043
+ const logger = input.logger ?? getLogger();
3044
+ const cwd = input.cwd ?? process.cwd();
3045
+ const repo = input.repo ?? "theholocron/holocron";
3046
+ const dryRun = input.dryRun ?? false;
3047
+ const exec = input.exec ?? defaultExec$1;
3048
+ const packageNames = input.packages ?? discoverPublicPackages(cwd);
3049
+ const npm = "npx";
3050
+ const npmArgs = ["-y", "npm@latest"];
3051
+ print(`Holocron publish --sync-trust${dryRun ? " (dry-run)" : ""}`);
3052
+ print(` ${input.oldFile} -> ${input.newFile} (repo: ${repo})`);
3053
+ print(` ${packageNames.length} public packages`);
3054
+ print("");
3055
+ logger.info({
3056
+ oldFile: input.oldFile,
3057
+ newFile: input.newFile,
3058
+ packages: packageNames.length
3059
+ }, "sync-trust: start");
3060
+ if (packageNames.length === 0) {
3061
+ const message = "nothing to sync (no packages/* and root package.json is private or unnamed)";
3062
+ print(` ✗ ${message}`);
3063
+ return {
3064
+ status: "fail",
3065
+ message,
3066
+ packages: []
3067
+ };
3068
+ }
3069
+ if (dryRun) {
3070
+ for (const name of packageNames) print(` … would sync ${name}`);
3071
+ return {
3072
+ status: "dry-run",
3073
+ message: "dry-run — no npm calls made",
3074
+ packages: []
3075
+ };
3076
+ }
3077
+ const results = [];
3078
+ for (const name of packageNames) {
3079
+ print(`=== ${name} ===`);
3080
+ const parsed = parseJson((await exec(npm, [
3081
+ ...npmArgs,
3082
+ "trust",
3083
+ "list",
3084
+ name,
3085
+ "--json"
3086
+ ], { cwd })).stdout);
3087
+ if (isEotp(parsed)) {
3088
+ const authUrl = parsed.error?.authUrl ?? "";
3089
+ print(` needs fresh 2FA — open and approve: ${authUrl}`);
3090
+ print(" then re-run; already-migrated packages are skipped.");
3091
+ results.push({
3092
+ name,
3093
+ status: "needs-auth",
3094
+ message: authUrl
3095
+ });
3096
+ logger.warn({
3097
+ package: name,
3098
+ authUrl
3099
+ }, "sync-trust: needs auth, stopping");
3100
+ return {
3101
+ status: "fail",
3102
+ message: "npm 2FA grant needed — see printed authUrl",
3103
+ packages: results
3104
+ };
3105
+ }
3106
+ const entry = parsed;
3107
+ if (entry.file === input.newFile) {
3108
+ print(` already on ${input.newFile}, skipping`);
3109
+ results.push({
3110
+ name,
3111
+ status: "skipped"
3112
+ });
3113
+ continue;
3114
+ }
3115
+ if (entry.id) {
3116
+ print(` revoking existing trust (file=${entry.file ?? "?"}, id=${entry.id})`);
3117
+ const revoke = await exec(npm, [
3118
+ ...npmArgs,
3119
+ "trust",
3120
+ "revoke",
3121
+ name,
3122
+ "--id",
3123
+ entry.id,
3124
+ "-y"
3125
+ ], { cwd });
3126
+ if (revoke.exitCode !== 0 && isEotp(parseJson(revoke.stdout))) {
3127
+ const authUrl = parseJson(revoke.stdout).error?.authUrl ?? "";
3128
+ print(` needs fresh 2FA — open and approve: ${authUrl}`);
3129
+ results.push({
3130
+ name,
3131
+ status: "needs-auth",
3132
+ message: authUrl
3133
+ });
3134
+ return {
3135
+ status: "fail",
3136
+ message: "npm 2FA grant needed — see printed authUrl",
3137
+ packages: results
3138
+ };
3139
+ }
3140
+ } else print(" no existing trust config found");
3141
+ print(` creating trust: ${input.newFile}`);
3142
+ const create = await exec(npm, [
3143
+ ...npmArgs,
3144
+ "trust",
3145
+ "github",
3146
+ name,
3147
+ "--repo",
3148
+ repo,
3149
+ "--file",
3150
+ input.newFile,
3151
+ "--allow-publish",
3152
+ "--allow-stage-publish",
3153
+ "-y"
3154
+ ], { cwd });
3155
+ if (create.exitCode === 0) {
3156
+ print(" ✓ ok");
3157
+ results.push({
3158
+ name,
3159
+ status: "ok"
3160
+ });
3161
+ } else {
3162
+ const createParsed = parseJson(create.stdout);
3163
+ if (isEotp(createParsed)) {
3164
+ const authUrl = createParsed.error?.authUrl ?? "";
3165
+ print(` needs fresh 2FA — open and approve: ${authUrl}`);
3166
+ results.push({
3167
+ name,
3168
+ status: "needs-auth",
3169
+ message: authUrl
3170
+ });
3171
+ return {
3172
+ status: "fail",
3173
+ message: "npm 2FA grant needed — see printed authUrl",
3174
+ packages: results
3175
+ };
3176
+ }
3177
+ const message = create.stderr.trim() || create.stdout.trim() || `exit ${create.exitCode}`;
3178
+ print(` ✗ FAILED: ${message}`);
3179
+ results.push({
3180
+ name,
3181
+ status: "fail",
3182
+ message
3183
+ });
3184
+ }
3185
+ }
3186
+ const failed = results.filter((r) => r.status === "fail");
3187
+ logger.info({
3188
+ ok: results.filter((r) => r.status === "ok").length,
3189
+ failed: failed.length
3190
+ }, "sync-trust: done");
3191
+ return {
3192
+ status: failed.length > 0 ? "fail" : "ok",
3193
+ ...failed.length > 0 ? { message: `${failed.length} package(s) failed` } : {},
3194
+ packages: results
3195
+ };
3196
+ }
3197
+ function parseJson(raw) {
3198
+ try {
3199
+ return JSON.parse(raw);
3200
+ } catch {
3201
+ return;
3202
+ }
3203
+ }
3204
+ function isEotp(parsed) {
3205
+ return typeof parsed === "object" && parsed !== null && "error" in parsed && parsed.error?.code === "EOTP";
3206
+ }
3021
3207
  async function runPublish(input = {}) {
3022
3208
  const print = input.print ?? ((line) => console.log(line));
3023
3209
  const logger = input.logger ?? getLogger();
@@ -3180,7 +3366,7 @@ function printNextSteps(print, env, packageNames, repoName) {
3180
3366
  print("");
3181
3367
  print(" → next: configure Trusted Publisher for each package on npm:");
3182
3368
  for (const name of packageNames) print(` https://www.npmjs.com/package/${name}/access`);
3183
- print(` Publisher: GitHub Actions Org: theholocron Repo: ${repoName} Workflow: release.yml`);
3369
+ print(` Publisher: GitHub Actions Org: theholocron Repo: ${repoName} Workflow: delivery.publish.yml`);
3184
3370
  if (env.get("NPM_TOKEN")) {
3185
3371
  print("");
3186
3372
  print(" → cleanup: $NPM_TOKEN was used. Revoke it now (no API for self-revoke; UI-only):");
@@ -3563,19 +3749,19 @@ var alexrc_default = { allow: [
3563
3749
  ] };
3564
3750
  //#endregion
3565
3751
  //#region src/templates/configs/alexjs/create-config.ts
3566
- const { workflowHeader: workflowHeader$7 } = createHeader({ source: "packages/cli/src/templates/configs/alexjs/create-config.ts" });
3752
+ const { workflowHeader: workflowHeader$8 } = createHeader({ source: "packages/cli/src/templates/configs/alexjs/create-config.ts" });
3567
3753
  function createRcConfig() {
3568
3754
  return JSON.stringify(alexrc_default, null, 2) + "\n";
3569
3755
  }
3570
- function createIgnoreConfig() {
3571
- return `${workflowHeader$7()}${alexignore_default}`;
3756
+ function createIgnoreConfig$1() {
3757
+ return `${workflowHeader$8()}${alexignore_default}`;
3572
3758
  }
3573
3759
  //#endregion
3574
3760
  //#region src/templates/configs/devmoji/create-config.ts
3575
- const { workflowHeader: workflowHeader$6 } = createHeader({ source: "packages/cli/src/templates/configs/devmoji/create-config.ts" });
3576
- function createConfig$5() {
3761
+ const { workflowHeader: workflowHeader$7 } = createHeader({ source: "packages/cli/src/templates/configs/devmoji/create-config.ts" });
3762
+ function createConfig$6() {
3577
3763
  return [
3578
- workflowHeader$6("cjs"),
3764
+ workflowHeader$7("cjs"),
3579
3765
  `/* eslint-disable */`,
3580
3766
  `const { defineConfig } = require("@theholocron/devmoji-config");`,
3581
3767
  `module.exports = defineConfig();`,
@@ -3587,9 +3773,9 @@ function createConfig$5() {
3587
3773
  var editorconfig_default = "root = true\n\n[*]\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newline = true\nindent_style = tab\nindent_size = 4\n\n[.gitattributes]\nindent_style = space\nindent_size = 2\n\n[*.{json,yml,yaml}]\nindent_style = space\nindent_size = 2\n\n[*.{md,mdx}]\ntrim_trailing_whitespace = false\n\n[.*{rc,ignore}]\nindent_style = space\nindent_size = 2\n";
3588
3774
  //#endregion
3589
3775
  //#region src/templates/configs/editorconfig/create-config.ts
3590
- const { workflowHeader: workflowHeader$5 } = createHeader({ source: "packages/cli/src/templates/configs/editorconfig/create-config.ts" });
3591
- function createConfig$4() {
3592
- return `${workflowHeader$5()}${editorconfig_default}`;
3776
+ const { workflowHeader: workflowHeader$6 } = createHeader({ source: "packages/cli/src/templates/configs/editorconfig/create-config.ts" });
3777
+ function createConfig$5() {
3778
+ return `${workflowHeader$6()}${editorconfig_default}`;
3593
3779
  }
3594
3780
  //#endregion
3595
3781
  //#region src/templates/configs/editorconfig-checker/editorconfig-checker.json
@@ -3630,7 +3816,7 @@ var editorconfig_checker_default = {
3630
3816
  * options here (`Exclude`, `Disable`) are stable across releases, so pinning
3631
3817
  * buys nothing.
3632
3818
  */
3633
- function createConfig$3() {
3819
+ function createConfig$4() {
3634
3820
  return JSON.stringify(editorconfig_checker_default, null, 2) + "\n";
3635
3821
  }
3636
3822
  //#endregion
@@ -3638,7 +3824,7 @@ function createConfig$3() {
3638
3824
  var pre_commit_default = "# Run GitLeaks against staged changes only — a full `gitleaks git` history\n# scan re-flags every pre-existing (already-merged) finding on every commit,\n# permanently blocking the repo once any false positive lands in history.\n# CI's super-linter GITLEAKS validator is likewise scoped to changed files.\ngitleaks protect --staged --no-banner\n\n# Exit with non-zero if secrets are detected by GitLeaks\nif [ $? -ne 0 ]; then\n echo \"Secrets detected by GitLeaks, aborting commit!\"\n exit 1\nfi\n\n# Run lint-staged with a custom config\nnpx lint-staged --config @theholocron/lint-staged-config\n";
3639
3825
  //#endregion
3640
3826
  //#region src/templates/configs/pre-commit/create-config.ts
3641
- const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cli/src/templates/configs/pre-commit/create-config.ts" });
3827
+ const { workflowHeader: workflowHeader$5 } = createHeader({ source: "packages/cli/src/templates/configs/pre-commit/create-config.ts" });
3642
3828
  /**
3643
3829
  * `.husky/pre-commit` — runs GitLeaks (scoped to staged changes) and
3644
3830
  * lint-staged before every commit. Installed by `holocron setup` when hooks
@@ -3651,15 +3837,15 @@ const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cl
3651
3837
  * doc — every future commit is permanently blocked, regardless of what's
3652
3838
  * actually staged.
3653
3839
  */
3654
- function createConfig$2() {
3655
- return `${workflowHeader$4("shebang")}${pre_commit_default}`;
3840
+ function createConfig$3() {
3841
+ return `${workflowHeader$5("shebang")}${pre_commit_default}`;
3656
3842
  }
3657
3843
  //#endregion
3658
3844
  //#region src/templates/configs/pre-push/pre-push
3659
3845
  var pre_push_default = "# Run the merge-gating checks locally before pushing — the same set CI runs.\n# Bypass once with: git push --no-verify\n\n# CI runners (semantic-release, super-linter auto-commit, bots) push without a\n# built dev environment — the hook has nothing to add there.\n[ -n \"$CI\" ] && exit 0\n\n__HOLOCRON_SCRIPT__ ci\n";
3660
3846
  //#endregion
3661
3847
  //#region src/templates/configs/pre-push/create-config.ts
3662
- const { workflowHeader: workflowHeader$3 } = createHeader({ source: "packages/cli/src/templates/configs/pre-push/create-config.ts" });
3848
+ const { workflowHeader: workflowHeader$4 } = createHeader({ source: "packages/cli/src/templates/configs/pre-push/create-config.ts" });
3663
3849
  /**
3664
3850
  * `.husky/pre-push` — runs `holocron ci` (the merge-gating checks, in CI order)
3665
3851
  * before every push. Installed by `holocron setup` when hooks are enabled
@@ -3668,17 +3854,32 @@ const { workflowHeader: workflowHeader$3 } = createHeader({ source: "packages/cl
3668
3854
  * @param holocronScript - the command that invokes the CLI. Defaults to
3669
3855
  * `pnpm exec holocron`; the source repo passes `node packages/cli/dist/cli.mjs`.
3670
3856
  */
3671
- function createConfig$1(holocronScript = "pnpm exec holocron") {
3672
- return `${workflowHeader$3("shebang")}${pre_push_default.replace("__HOLOCRON_SCRIPT__", holocronScript)}`;
3857
+ function createConfig$2(holocronScript = "pnpm exec holocron") {
3858
+ return `${workflowHeader$4("shebang")}${pre_push_default.replace("__HOLOCRON_SCRIPT__", holocronScript)}`;
3673
3859
  }
3674
3860
  //#endregion
3675
3861
  //#region src/templates/configs/prepare-commit-msg/prepare-commit-msg
3676
3862
  var prepare_commit_msg_default = "NAME=$(git config user.name)\nEMAIL=$(git config user.email)\n\nif [ -z \"$NAME\" ]; then\n echo \"empty git config user.name\"\n exit 1\nfi\n\nif [ -z \"$EMAIL\" ]; then\n echo \"empty git config user.email\"\n exit 1\nfi\n\ngit interpret-trailers --if-exists doNothing --trailer \\\n \"Signed-off-by: $NAME <$EMAIL>\" \\\n --in-place \"$1\"\n\nnpx devmoji -e\n";
3677
3863
  //#endregion
3678
3864
  //#region src/templates/configs/prepare-commit-msg/create-config.ts
3679
- const { workflowHeader: workflowHeader$2 } = createHeader({ source: "packages/cli/src/templates/configs/prepare-commit-msg/create-config.ts" });
3865
+ const { workflowHeader: workflowHeader$3 } = createHeader({ source: "packages/cli/src/templates/configs/prepare-commit-msg/create-config.ts" });
3866
+ function createConfig$1() {
3867
+ return `${workflowHeader$3("shebang")}${prepare_commit_msg_default}`;
3868
+ }
3869
+ //#endregion
3870
+ //#region src/templates/configs/yamllint/.yamlignore
3871
+ var _yamlignore_default = "pnpm-lock.yaml\n";
3872
+ //#endregion
3873
+ //#region src/templates/configs/yamllint/.yamllint.yml
3874
+ var _yamllint_default = "---\nextends: default\n\nignore-from-file:\n - .gitignore\n - .yamlignore\n\nrules:\n comments:\n min-spaces-from-content: 1\n require-starting-space: true\n\n comments-indentation: {}\n\n document-start:\n ignore: |\n .github/*.yml\n\n indentation:\n spaces: consistent\n indent-sequences: consistent\n\n line-length: disable\n";
3875
+ //#endregion
3876
+ //#region src/templates/configs/yamllint/create-config.ts
3877
+ const { workflowHeader: workflowHeader$2 } = createHeader({ source: "packages/cli/src/templates/configs/yamllint/create-config.ts" });
3680
3878
  function createConfig() {
3681
- return `${workflowHeader$2("shebang")}${prepare_commit_msg_default}`;
3879
+ return `${workflowHeader$2()}${_yamllint_default}`;
3880
+ }
3881
+ function createIgnoreConfig() {
3882
+ return `${workflowHeader$2()}${_yamlignore_default}`;
3682
3883
  }
3683
3884
  //#endregion
3684
3885
  //#region src/templates/dco.yml
@@ -3688,7 +3889,7 @@ var dco_default = "allowRemediationCommits:\n individual: true\n";
3688
3889
  var dependabot_default = "version: 2\nupdates:\n - package-ecosystem: npm\n directory: /\n schedule:\n interval: weekly\n commit-message:\n prefix: \"chore(deps)\"\n prefix-development: \"chore(deps-dev)\"\n groups:\n security-patches:\n applies-to: security-updates\n patterns:\n - \"*\"\n all-dependencies:\n update-types:\n - minor\n - patch\n\n - package-ecosystem: github-actions\n directory: /\n schedule:\n interval: weekly\n commit-message:\n prefix: \"chore(deps)\"\n groups:\n all-actions:\n patterns:\n - \"*\"\n";
3689
3890
  //#endregion
3690
3891
  //#region src/templates/labeler.yml
3691
- var labeler_default = "bug:\n - '^fix'\n\nchore:\n - '^chore(?!\\(deps)'\n\nci:\n - '^ci'\n\ndependencies:\n - '^chore\\(deps'\n\ndocumentation:\n - '^docs'\n\nenhancement:\n - '^feat'\n\nperformance:\n - '^perf'\n\nrefactor:\n - '^refactor'\n\ntest:\n - '^test'\n";
3892
+ var labeler_default = "bug:\n - \"^fix\"\n\nchore:\n - '^chore(?!\\(deps)'\n\nci:\n - \"^ci\"\n\ndependencies:\n - '^chore\\(deps'\n\ndocumentation:\n - \"^docs\"\n\nenhancement:\n - \"^feat\"\n\nperformance:\n - \"^perf\"\n\nrefactor:\n - \"^refactor\"\n\ntest:\n - \"^test\"\n";
3692
3893
  //#endregion
3693
3894
  //#region src/commands/setup/agent-prompts-data.ts
3694
3895
  /**
@@ -4503,6 +4704,10 @@ const { workflowHeader: workflowHeader$1 } = createHeader({
4503
4704
  source: "packages/cli/src/commands/setup/run-setup.ts",
4504
4705
  tool: "holocron setup"
4505
4706
  });
4707
+ const { workflowHeader: thinCallerHeader } = createHeader({
4708
+ source: "packages/astromech/src/thin-callers.ts",
4709
+ tool: "holocron setup"
4710
+ });
4506
4711
  async function runSetup(input) {
4507
4712
  const print = input.print ?? ((line) => console.log(line));
4508
4713
  const logger = getLogger();
@@ -4537,7 +4742,7 @@ async function runSetup(input) {
4537
4742
  }, { skipCodes: SECURITY_SKIP_CODES[method] }));
4538
4743
  print(formatStep(steps[steps.length - 1]));
4539
4744
  }
4540
- const usesAdvancedCodeQL = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("codeql");
4745
+ const usesAdvancedCodeQL = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("security.codeScanning");
4541
4746
  steps.push(await runStep("source", usesAdvancedCodeQL ? "disableDefaultCodeScanning" : "enableCodeScanning", dryRun, async () => {
4542
4747
  if (usesAdvancedCodeQL) await source.disableDefaultCodeScanning();
4543
4748
  else return await source.enableCodeScanning();
@@ -4574,7 +4779,8 @@ async function runSetup(input) {
4574
4779
  }).thinCallers();
4575
4780
  for (const entry of tasks) {
4576
4781
  const name = typeof entry === "string" ? entry : entry.name;
4577
- if (!KNOWN_WORKFLOWS.has(name)) {
4782
+ const isCombinedContentOnly = name === "knowledge.docs" || name === "knowledge.components";
4783
+ if (!KNOWN_WORKFLOWS.has(name) && !isCombinedContentOnly) {
4578
4784
  steps.push({
4579
4785
  capability: "source",
4580
4786
  step: `write workflow ${name}`,
@@ -4584,12 +4790,12 @@ async function runSetup(input) {
4584
4790
  print(formatStep(steps[steps.length - 1]));
4585
4791
  continue;
4586
4792
  }
4587
- const filename = name === "deploy" ? "deploy.yml" : `${name}.yml`;
4793
+ const filename = `${name}.yml`;
4588
4794
  const content = files.get(filename);
4589
4795
  if (content === void 0) continue;
4590
- const step = name === "deploy" && content.includes("workflows/preview.yml@main") ? "write workflow deploy (with preview)" : `write workflow ${name}`;
4796
+ const step = content.includes("workflows/preview.yml@main") ? `write workflow ${name} (with preview)` : `write workflow ${name}`;
4591
4797
  steps.push(await runStep("source", step, dryRun, async () => {
4592
- await source.writeWorkflowFile(filename, `${workflowHeader$1()}${content}`);
4798
+ await source.writeWorkflowFile(filename, `${thinCallerHeader()}${content}`);
4593
4799
  }));
4594
4800
  print(formatStep(steps[steps.length - 1]));
4595
4801
  }
@@ -4623,33 +4829,41 @@ async function runSetup(input) {
4623
4829
  }));
4624
4830
  print(formatStep(steps[steps.length - 1]));
4625
4831
  steps.push(await runStep("source", "write .alexignore", dryRun, async () => {
4626
- await source.writeRepoFile(".alexignore", createIgnoreConfig());
4832
+ await source.writeRepoFile(".alexignore", createIgnoreConfig$1());
4833
+ }));
4834
+ print(formatStep(steps[steps.length - 1]));
4835
+ steps.push(await runStep("source", "write .yamllint.yml", dryRun, async () => {
4836
+ await source.writeRepoFile(".yamllint.yml", createConfig());
4837
+ }));
4838
+ print(formatStep(steps[steps.length - 1]));
4839
+ steps.push(await runStep("source", "write .yamlignore", dryRun, async () => {
4840
+ await source.writeRepoFile(".yamlignore", createIgnoreConfig());
4627
4841
  }));
4628
4842
  print(formatStep(steps[steps.length - 1]));
4629
4843
  steps.push(await runStep("source", "write .editorconfig", dryRun, async () => {
4630
- await source.writeRepoFile(".editorconfig", createConfig$4());
4844
+ await source.writeRepoFile(".editorconfig", createConfig$5());
4631
4845
  }));
4632
4846
  print(formatStep(steps[steps.length - 1]));
4633
4847
  steps.push(await runStep("source", "write .editorconfig-checker.json", dryRun, async () => {
4634
- await source.writeRepoFile(".editorconfig-checker.json", createConfig$3());
4848
+ await source.writeRepoFile(".editorconfig-checker.json", createConfig$4());
4635
4849
  }));
4636
4850
  print(formatStep(steps[steps.length - 1]));
4637
4851
  steps.push(await runStep("source", "write devmoji.config.cjs", dryRun, async () => {
4638
- await source.writeRepoFile("devmoji.config.cjs", createConfig$5());
4852
+ await source.writeRepoFile("devmoji.config.cjs", createConfig$6());
4639
4853
  }));
4640
4854
  print(formatStep(steps[steps.length - 1]));
4641
4855
  steps.push(await runStep("source", "write .husky/prepare-commit-msg", dryRun, async () => {
4642
- await source.writeRepoFile(".husky/prepare-commit-msg", createConfig());
4856
+ await source.writeRepoFile(".husky/prepare-commit-msg", createConfig$1());
4643
4857
  }));
4644
4858
  print(formatStep(steps[steps.length - 1]));
4645
4859
  if (hooksEnabled) {
4646
4860
  steps.push(await runStep("source", "write .husky/pre-push", dryRun, async () => {
4647
- await source.writeRepoFile(".husky/pre-push", createConfig$1(config.holocronScript));
4861
+ await source.writeRepoFile(".husky/pre-push", createConfig$2(config.holocronScript));
4648
4862
  return "runs `holocron ci` before push — bypass with `git push --no-verify`";
4649
4863
  }));
4650
4864
  print(formatStep(steps[steps.length - 1]));
4651
4865
  steps.push(await runStep("source", "write .husky/pre-commit", dryRun, async () => {
4652
- await source.writeRepoFile(".husky/pre-commit", createConfig$2());
4866
+ await source.writeRepoFile(".husky/pre-commit", createConfig$3());
4653
4867
  return "runs GitLeaks (staged-only) + lint-staged before commit";
4654
4868
  }));
4655
4869
  print(formatStep(steps[steps.length - 1]));
@@ -4676,7 +4890,7 @@ async function runSetup(input) {
4676
4890
  print(formatStep(steps[steps.length - 1]));
4677
4891
  }
4678
4892
  {
4679
- const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("test");
4893
+ const hasTestWorkflow = (config.tasks ?? []).map((e) => typeof e === "string" ? e : e.name).includes("verification.unitTests");
4680
4894
  const existing = await readFile(join(input.context.repoRoot, "codecov.yml"), "utf8").catch(() => null);
4681
4895
  if (!hasTestWorkflow && existing == null) steps.push({
4682
4896
  capability: "source",
@@ -4695,6 +4909,23 @@ async function runSetup(input) {
4695
4909
  }));
4696
4910
  print(formatStep(steps[steps.length - 1]));
4697
4911
  }
4912
+ {
4913
+ const content = createAstromech({
4914
+ cwd: input.context.repoRoot,
4915
+ config: { tasks: config.tasks },
4916
+ logger
4917
+ }).turboConfig();
4918
+ if (content === null) steps.push({
4919
+ capability: "source",
4920
+ step: "write turbo.json",
4921
+ status: "skip",
4922
+ message: "no fan-out-eligible tasks configured"
4923
+ });
4924
+ else steps.push(await runStep("source", "write turbo.json", dryRun, async () => {
4925
+ await source.writeRepoFile("turbo.json", content);
4926
+ }));
4927
+ print(formatStep(steps[steps.length - 1]));
4928
+ }
4698
4929
  if (source.syncLabels) {
4699
4930
  steps.push(await runStep("source", "sync labels", dryRun, async () => {
4700
4931
  return source.syncLabels(CANONICAL_LABELS, STALE_LABELS);
@@ -4833,7 +5064,7 @@ async function runSetup(input) {
4833
5064
  if (loader.has("deployment")) {
4834
5065
  const deploy = loader.get("deployment");
4835
5066
  print(style.step("deployment"));
4836
- const deployEntry = (config.tasks ?? []).map((e) => typeof e === "string" ? { name: e } : e).find((e) => e.name === "deploy");
5067
+ const deployEntry = (config.tasks ?? []).map((e) => typeof e === "string" ? { name: e } : e).find((e) => e.name === "delivery.deploy" || e.name === "knowledge.docs" || e.name === "knowledge.components");
4837
5068
  const previewCfg = deployEntry?.with ? extractPreviewConfig(deployEntry.with, {
4838
5069
  org: config.org,
4839
5070
  domain: config.domain
@@ -5557,7 +5788,7 @@ async function runSyncWiki(input) {
5557
5788
  //#endregion
5558
5789
  //#region src/commands/sync.ts
5559
5790
  const { workflowHeader } = createHeader({
5560
- source: "packages/cli/src/commands/sync.ts",
5791
+ source: "packages/astromech/src/thin-callers.ts",
5561
5792
  tool: "holocron sync"
5562
5793
  });
5563
5794
  const SYNC_STEPS = [
@@ -5571,6 +5802,7 @@ const SYNC_STEPS = [
5571
5802
  "readme",
5572
5803
  "workflows",
5573
5804
  "scripts",
5805
+ "turbo",
5574
5806
  "wiki"
5575
5807
  ];
5576
5808
  const LOCAL_STEPS = /* @__PURE__ */ new Set([
@@ -5580,6 +5812,7 @@ const LOCAL_STEPS = /* @__PURE__ */ new Set([
5580
5812
  "readme",
5581
5813
  "workflows",
5582
5814
  "scripts",
5815
+ "turbo",
5583
5816
  "wiki"
5584
5817
  ]);
5585
5818
  async function runSync(input) {
@@ -5728,6 +5961,7 @@ async function runSync(input) {
5728
5961
  "readme",
5729
5962
  "workflows",
5730
5963
  "scripts",
5964
+ "turbo",
5731
5965
  "wiki"
5732
5966
  ]) {
5733
5967
  if (requestedSteps !== void 0 && !requestedSteps.includes(stepName)) continue;
@@ -5832,7 +6066,8 @@ async function runSync(input) {
5832
6066
  }).thinCallers();
5833
6067
  for (const entry of taskEntries) {
5834
6068
  const name = typeof entry === "string" ? entry : entry.name;
5835
- if (!KNOWN_WORKFLOWS.has(name)) {
6069
+ const isCombinedContentOnly = name === "knowledge.docs" || name === "knowledge.components";
6070
+ if (!KNOWN_WORKFLOWS.has(name) && !isCombinedContentOnly) {
5836
6071
  steps.push({
5837
6072
  capability: "local",
5838
6073
  step: `sync workflow ${name}`,
@@ -5842,10 +6077,10 @@ async function runSync(input) {
5842
6077
  print(formatSyncStep(steps[steps.length - 1]));
5843
6078
  continue;
5844
6079
  }
5845
- const filename = name === "deploy" ? "deploy.yml" : `${name}.yml`;
6080
+ const filename = `${name}.yml`;
5846
6081
  const content = files.get(filename);
5847
6082
  if (content === void 0) continue;
5848
- const step = `sync workflow ${name}${name === "deploy" && content.includes("workflows/preview.yml@main") ? " (with preview)" : ""}`;
6083
+ const step = `sync workflow ${name}${content.includes("workflows/preview.yml@main") ? " (with preview)" : ""}`;
5849
6084
  steps.push(await runSyncStep("local", step, dryRun, async () => {
5850
6085
  await writeWorkflowFile(input.context.repoRoot, filename, `${workflowHeader()}${content}`);
5851
6086
  }));
@@ -5877,6 +6112,27 @@ async function runSync(input) {
5877
6112
  }));
5878
6113
  print(formatSyncStep(steps[steps.length - 1]));
5879
6114
  }
6115
+ if (stepName === "turbo") {
6116
+ const content = createAstromech({
6117
+ cwd: input.context.repoRoot,
6118
+ config: { tasks: config.tasks },
6119
+ logger
6120
+ }).turboConfig();
6121
+ if (content === null) {
6122
+ steps.push({
6123
+ capability: "local",
6124
+ step: "sync turbo.json",
6125
+ status: "skip",
6126
+ message: "no fan-out-eligible tasks configured"
6127
+ });
6128
+ print(formatSyncStep(steps[steps.length - 1]));
6129
+ } else {
6130
+ steps.push(await runSyncStep("local", "sync turbo.json", dryRun, async () => {
6131
+ await writeFile(join(input.context.repoRoot, "turbo.json"), content, "utf8");
6132
+ }));
6133
+ print(formatSyncStep(steps[steps.length - 1]));
6134
+ }
6135
+ }
5880
6136
  if (stepName === "wiki") {
5881
6137
  const result = await runSyncWiki({
5882
6138
  loaded: input.loaded,
@@ -7728,9 +7984,31 @@ try {
7728
7984
  }).option("otp", {
7729
7985
  type: "string",
7730
7986
  describe: "One-time password from your authenticator (required if npm needs 2FA for writes)"
7987
+ }).option("sync-trust", {
7988
+ type: "array",
7989
+ describe: "Migrate npm Trusted Publisher config to a new workflow filename across every public package: --sync-trust <old-file> <new-file>. See issue #689 — npm requires fresh interactive 2FA per package; re-run after approving the printed authUrl if it stops early."
7990
+ }).option("repo", {
7991
+ type: "string",
7992
+ describe: "owner/repo for Trusted Publisher config (--sync-trust). Defaults to theholocron/holocron."
7731
7993
  }), async (argv) => {
7994
+ if (argv.syncTrust) {
7995
+ const [oldFile, newFile] = argv.syncTrust;
7996
+ if (!oldFile || !newFile) {
7997
+ getLogger().error("publish --sync-trust requires exactly two values: <old-file> <new-file>");
7998
+ process.exitCode = 1;
7999
+ return;
8000
+ }
8001
+ if ((await runSyncTrust({
8002
+ cwd: argv.cwd,
8003
+ oldFile,
8004
+ newFile,
8005
+ dryRun: argv.dryRun,
8006
+ ...argv.repo ? { repo: argv.repo } : {}
8007
+ })).status === "fail") process.exitCode = 1;
8008
+ return;
8009
+ }
7732
8010
  if (!argv.initial) {
7733
- getLogger().error("publish: only `--initial` is supported today. Run `holocron publish --initial`.");
8011
+ getLogger().error("publish: only `--initial` and `--sync-trust` are supported today. Run `holocron publish --initial`.");
7734
8012
  process.exitCode = 1;
7735
8013
  return;
7736
8014
  }