@theholocron/cli 5.0.0-alpha.55 → 5.0.0-alpha.56

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
@@ -24,6 +24,8 @@ import { createGitHubClient } from "@theholocron/github-client";
24
24
  import { PostHogSink } from "@theholocron/observability/analytics";
25
25
  import { NoopAnalyticsSink, NoopErrorSink, redactObject } from "@theholocron/observability/core";
26
26
  import { SentrySink } from "@theholocron/observability/errors";
27
+ import load from "@commitlint/load";
28
+ import lint from "@commitlint/lint";
27
29
  import { promisify } from "node:util";
28
30
  import { ConfigFileError, loadConfigFile } from "@theholocron/datapad";
29
31
  import search from "@inquirer/search";
@@ -1219,6 +1221,7 @@ const COMMAND_CONTEXTS = {
1219
1221
  "skills install": "global",
1220
1222
  "skills remove": "global",
1221
1223
  "skills update": "global",
1224
+ "lint commit-msg": "global",
1222
1225
  run: "repo-aware",
1223
1226
  ci: "repo-aware",
1224
1227
  "config show": "repo-aware",
@@ -4171,12 +4174,21 @@ function createIgnoreConfig$1() {
4171
4174
  }
4172
4175
  //#endregion
4173
4176
  //#region src/templates/configs/commit-msg/commit-msg
4174
- var commit_msg_default = "# --config <resolved shared path> (config-resolution workstream, #676) — falls\n# back to commitlint's own auto-discovery of a local commitlint.config.ts when\n# the shared package's built dist/ isn't present (not installed, or a stale\n# install predating this file).\nCOMMITLINT_CONFIG=\"node_modules/@theholocron/commitlint-config/dist/index.js\"\nif [ -f \"$COMMITLINT_CONFIG\" ]; then\n npx --no -- commitlint --config \"$COMMITLINT_CONFIG\" --edit \"$1\"\nelse\n npx --no -- commitlint --edit \"$1\"\nfi\n";
4177
+ var commit_msg_default = "__HOLOCRON_SCRIPT__ lint commit-msg \"$1\"\n";
4175
4178
  //#endregion
4176
4179
  //#region src/templates/configs/commit-msg/create-config.ts
4177
4180
  const { workflowHeader: workflowHeader$8 } = createHeader({ source: "packages/cli/src/templates/configs/commit-msg/create-config.ts" });
4178
- function createConfig$7() {
4179
- return `${workflowHeader$8("shebang")}${commit_msg_default}`;
4181
+ /**
4182
+ * `.husky/commit-msg` — lints the commit message via `holocron lint
4183
+ * commit-msg` (holocron#789), the real programmatic replacement for
4184
+ * shelling out to `commitlint --edit`. Installed by `holocron setup` when
4185
+ * hooks are enabled. Same `__HOLOCRON_SCRIPT__` substitution as `pre-push`.
4186
+ *
4187
+ * @param holocronScript - the command that invokes the CLI. Defaults to
4188
+ * `pnpm exec holocron`; the source repo passes `node packages/cli/dist/cli.mjs`.
4189
+ */
4190
+ function createConfig$7(holocronScript = "pnpm exec holocron") {
4191
+ return `${workflowHeader$8("shebang")}${commit_msg_default.replace("__HOLOCRON_SCRIPT__", holocronScript)}`;
4180
4192
  }
4181
4193
  //#endregion
4182
4194
  //#region src/templates/configs/devmoji/create-config.ts
@@ -5279,7 +5291,7 @@ async function runSetup(input) {
5279
5291
  }));
5280
5292
  print(formatStep(steps[steps.length - 1]));
5281
5293
  steps.push(await runStep("source", "write .husky/commit-msg", dryRun, async () => {
5282
- await source.writeRepoFile(".husky/commit-msg", createConfig$7());
5294
+ await source.writeRepoFile(".husky/commit-msg", createConfig$7(config.holocronScript));
5283
5295
  }));
5284
5296
  print(formatStep(steps[steps.length - 1]));
5285
5297
  if (hooksEnabled) {
@@ -7651,6 +7663,63 @@ async function runUpgradeNode(input) {
7651
7663
  };
7652
7664
  }
7653
7665
  //#endregion
7666
+ //#region src/commit-lint/lint-message.ts
7667
+ /**
7668
+ * Lints one commit message string against an already-loaded commitlint
7669
+ * config. The genuinely environment-independent half of commit-message
7670
+ * linting (holocron#789) — *obtaining* a `QualifiedConfig` differs by
7671
+ * caller (Sentinel isolates itself from any ambient config since it runs
7672
+ * standalone, away from a real repo checkout; `lintCommitMsgFile` below
7673
+ * just lets cosmiconfig discover the invoking repo's own
7674
+ * `commitlint.config.*`, matching `commitlint --edit`'s own contract) —
7675
+ * but linting one message against a loaded config is identical either way.
7676
+ *
7677
+ * Real `@commitlint/lint` — the same programmatic API `@commitlint/cli`
7678
+ * itself uses internally — never a reimplementation of commitlint's rules.
7679
+ */
7680
+ /** `parserPreset.parserOpts`, if the loaded config sets one — same lookup `@commitlint/cli`'s own `selectParserOpts()` does. */
7681
+ function selectParserOpts(parserPreset) {
7682
+ return parserPreset?.parserOpts;
7683
+ }
7684
+ async function lintCommitMessage(message, loaded) {
7685
+ const opts = {
7686
+ /* istanbul ignore next -- see comment above */
7687
+ parserOpts: selectParserOpts(loaded.parserPreset) ?? {},
7688
+ plugins: loaded.plugins,
7689
+ /* istanbul ignore next -- see comment above */
7690
+ ignores: loaded.ignores ?? [],
7691
+ defaultIgnores: loaded.defaultIgnores !== false
7692
+ };
7693
+ const outcome = await lint(message, loaded.rules, opts);
7694
+ if (outcome.valid) return [];
7695
+ return outcome.errors.map((error) => ({
7696
+ rule: error.name,
7697
+ message: error.message
7698
+ }));
7699
+ }
7700
+ //#endregion
7701
+ //#region src/commit-lint/lint-commit-msg-file.ts
7702
+ /**
7703
+ * `holocron lint commit-msg <file>` — a real programmatic replacement for
7704
+ * shelling out to `commitlint --edit "$1"` from `.husky/commit-msg`
7705
+ * (holocron#789). Matches that CLI's own contract exactly: `file` is a
7706
+ * path to the commit message being written (the arg git's `commit-msg`
7707
+ * hook passes as `$1`), no `--config` override needed — `@commitlint/load`
7708
+ * runs from `cwd` with no seed, so cosmiconfig's own normal upward search
7709
+ * discovers the invoking repo's real `commitlint.config.*` file, exactly
7710
+ * as `commitlint --edit` (no `--config` flag) already does today. Unlike
7711
+ * Sentinel's `loadIsolatedConfig()`, no isolation is needed here — this
7712
+ * runs inside a real repo checkout with a real `node_modules`, not
7713
+ * standalone on a server.
7714
+ */
7715
+ async function lintCommitMsgFile(filePath, cwd = process.cwd()) {
7716
+ const violations = await lintCommitMessage(await readFile(filePath, "utf8"), await load({}, { cwd }));
7717
+ return {
7718
+ valid: violations.length === 0,
7719
+ violations
7720
+ };
7721
+ }
7722
+ //#endregion
7654
7723
  //#region src/config/load-config.ts
7655
7724
  /**
7656
7725
  * `holocron.config.{ts,js,mjs,cjs,json}` loader.
@@ -7761,7 +7830,9 @@ const numeric = (value) => /^\d+$/.test(value) ? true : "enter a number";
7761
7830
  * The full command surface, hand-maintained in parallel with `cli.ts`'s
7762
7831
  * yargs registrations (this module does not introspect yargs at runtime).
7763
7832
  * `run <task>` is deliberately excluded — it's CI/scripting-oriented, and an
7764
- * interactive prompt in front of it would work against that.
7833
+ * interactive prompt in front of it would work against that. `lint
7834
+ * commit-msg <file>` is excluded for the same reason: it's invoked
7835
+ * exclusively from `.husky/commit-msg "$1"`, never typed by a human.
7765
7836
  */
7766
7837
  const COMMAND_REGISTRY = [
7767
7838
  {
@@ -8670,7 +8741,17 @@ try {
8670
8741
  scope: argv.all ? "all" : "required",
8671
8742
  ...argv.filter ? { filter: argv.filter } : {}
8672
8743
  }).status === "fail") process.exitCode = 1;
8673
- }).command("sync-github", "Sync workflow templates and composite actions to theholocron/.github", (y) => y.option("repo", {
8744
+ }).command("lint", "Lint content locally against the org's shared rules", (y) => y.command("commit-msg <file>", "Lint a commit message file — matches `commitlint --edit`'s own contract, for use from .husky/commit-msg", (yy) => yy.positional("file", {
8745
+ type: "string",
8746
+ demandOption: true,
8747
+ describe: "Path to the commit message file (git's commit-msg hook passes this as $1)."
8748
+ }), async (argv) => {
8749
+ const result = await lintCommitMsgFile(argv.file, argv.cwd);
8750
+ if (!result.valid) {
8751
+ for (const violation of result.violations) console.error(`✖ ${violation.rule}: ${violation.message}`);
8752
+ process.exitCode = 1;
8753
+ }
8754
+ }), () => {}).command("sync-github", "Sync workflow templates and composite actions to theholocron/.github", (y) => y.option("repo", {
8674
8755
  type: "string",
8675
8756
  default: "theholocron/.github",
8676
8757
  describe: "Target org/repo (default: theholocron/.github)"