@theholocron/cli 3.64.0 → 3.66.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
@@ -209,13 +209,15 @@ all three. See the [telemetry guide](https://docs.theholocron.dev/holocron/telem
209
209
  implement
210
210
  - `src/config.ts` — config schema, `defineConfig`, `resolveConfig`,
211
211
  `CapabilityConfigPackage`
212
- - `src/load-config.ts` — `loadConfig` — reads JSON/JS/TS config files
212
+ - `src/config/load-config.ts` — `loadConfig` — reads `holocron.config.*`
213
+ (file discovery via [`@theholocron/datapad`](../datapad))
213
214
  - `src/define-config.ts` — `defineConfig` typed pass-through
214
215
  - `src/logger.ts` — CLI-side `@theholocron/logger` wiring (`buildCliLogger`,
215
216
  `resolveLogLevel`)
216
217
  - `src/loader.ts` — `PluginLoader` — dynamic-imports plugins, resolves
217
218
  capability config packages, builds the capability registry
218
- - `src/cli.ts` — yargs entry, dispatches subcommands
219
+ - `src/cli.ts` — yargs entry, dispatches subcommands. `holocron run` is
220
+ delegated to [`@theholocron/astromech`](../astromech) (`createAstromech`).
219
221
  - `src/commands/` — `setup`, `sync`, `doctor`, `deploy`, `secret set`,
220
222
  `secrets sync`, `npm publish-initial`, `sync-github`, `upgrade node`,
221
223
  `plugin create`, `auth`
package/dist/cli.mjs CHANGED
@@ -3,6 +3,7 @@ import { createRequire } from "node:module";
3
3
  import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
4
4
  import path, { basename, dirname, join, relative, resolve } from "node:path";
5
5
  import { checkbox, input, select } from "@inquirer/prompts";
6
+ import { createAstromech } from "@theholocron/astromech";
6
7
  import yargs from "yargs";
7
8
  import { hideBin } from "yargs/helpers";
8
9
  import { AuthError, ProviderApiError, ProviderApiError as ProviderApiError$1, createRestClient } from "@theholocron/http-client";
@@ -22,6 +23,7 @@ import { createGitHubClient } from "@theholocron/github-client";
22
23
  import * as Sentry from "@sentry/node";
23
24
  import { PostHog } from "posthog-node";
24
25
  import { promisify } from "node:util";
26
+ import { ConfigFileError, loadConfigFile } from "@theholocron/datapad";
25
27
  //#region src/env.ts
26
28
  /** Singleton env for simple global lookups throughout the CLI. */
27
29
  const env = createEnvLookup();
@@ -6698,74 +6700,32 @@ async function runUpgradeNode(input) {
6698
6700
  //#endregion
6699
6701
  //#region src/config/load-config.ts
6700
6702
  /**
6701
- * `holocron.config.{json,js,ts}` file loader.
6703
+ * `holocron.config.{ts,js,mjs,cjs,json}` loader.
6702
6704
  *
6703
- * Search order: json → js → ts. JSON is parsed directly; JS is loaded
6704
- * via native dynamic import; TS is loaded via `tsImport` from tsx (a
6705
- * runtime dep) so operators can write typed configs with `defineConfig`
6706
- * without needing a separate build step.
6705
+ * File discovery + module loading (typed configs via `tsx`, no build
6706
+ * step) is delegated to `@theholocron/datapad`. This module keeps the
6707
+ * holocron-specific parts: filling `name` / `repo.name` defaults and
6708
+ * running the config through `resolveConfig`.
6707
6709
  *
6708
- * All three forms are validated through the same `resolveConfig` path.
6709
- * Implements the lookup-order contract from issue #75 / #81.
6710
+ * Probe order is TS-first (`.ts` → `.js` → `.mjs` → `.cjs` → `.json`) —
6711
+ * see ADR-0010 / issue #75 / #81.
6710
6712
  */
6711
6713
  const execFileAsync = promisify(execFile);
6712
- const CANDIDATE_FILENAMES = [
6713
- "holocron.config.json",
6714
- "holocron.config.js",
6715
- "holocron.config.ts"
6716
- ];
6717
- var ConfigFileError = class extends Error {
6718
- name = "ConfigFileError";
6719
- };
6720
6714
  /**
6721
6715
  * Read + parse + resolve `holocron.config.*` from the given directory.
6722
- * Search order: json → js → ts. Throws `ConfigFileError` if nothing
6723
- * found, or `ConfigError` if the config is malformed / invalid.
6716
+ * Throws {@link ConfigFileError} when nothing is found or a file cannot
6717
+ * be loaded, or `ConfigError` when the config is invalid.
6724
6718
  */
6725
6719
  async function loadConfig(cwd) {
6726
- for (const filename of CANDIDATE_FILENAMES) {
6727
- const fullPath = join(cwd, filename);
6728
- if (await fileExists(fullPath)) {
6729
- if (filename.endsWith(".json")) return {
6730
- resolved: await loadJson(fullPath),
6731
- filepath: fullPath
6732
- };
6733
- if (filename.endsWith(".ts")) return {
6734
- resolved: await loadTs(fullPath),
6735
- filepath: fullPath
6736
- };
6737
- return {
6738
- resolved: await loadJs(fullPath),
6739
- filepath: fullPath
6740
- };
6741
- }
6742
- }
6743
- throw new ConfigFileError(`no holocron.config.{json,js,ts} found in ${cwd}. Create one — see the README for the schema.`);
6744
- }
6745
- async function loadJson(filepath) {
6746
- const raw = await readFile(filepath, "utf8");
6747
- let parsed;
6748
- try {
6749
- parsed = JSON.parse(raw);
6750
- } catch (err) {
6751
- throw new ConfigError(`${filepath} is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
6752
- }
6753
- return resolveConfig(await deriveDefaults(dirname(filepath), parsed));
6754
- }
6755
- async function loadJs(filepath) {
6756
- const mod = await import(pathToFileURL(filepath).href);
6757
- return resolveConfig(await deriveDefaults(dirname(filepath), extractRaw(filepath, mod)));
6758
- }
6759
- async function loadTs(filepath) {
6760
- const { tsImport } = await import("tsx/esm/api");
6761
- const mod = await tsImport(pathToFileURL(filepath).href, import.meta.url);
6762
- return resolveConfig(await deriveDefaults(dirname(filepath), extractRaw(filepath, mod)));
6763
- }
6764
- function extractRaw(filepath, mod) {
6765
- const outer = mod.default;
6766
- const raw = outer?.__esModule === true ? outer.default : outer;
6767
- if (raw === void 0 || raw === null) throw new ConfigFileError(`${filepath} must have a default export (use \`export default defineConfig({…})\`)`);
6768
- return raw;
6720
+ const found = await loadConfigFile({
6721
+ cwd,
6722
+ name: "holocron"
6723
+ });
6724
+ if (!found) throw new ConfigFileError(`no holocron.config.{ts,js,mjs,cjs,json} found in ${cwd}. Create one — see the README for the schema.`);
6725
+ return {
6726
+ resolved: resolveConfig(await deriveDefaults(dirname(found.filepath), found.config)),
6727
+ filepath: found.filepath
6728
+ };
6769
6729
  }
6770
6730
  async function deriveDefaults(configDir, raw) {
6771
6731
  const result = { ...raw };
@@ -6806,13 +6766,6 @@ function parseGitRemoteUrl(url) {
6806
6766
  const sshMatch = url.match(/github\.com:([^/]+\/[^/]+?)(?:\.git)?$/);
6807
6767
  if (sshMatch) return sshMatch[1];
6808
6768
  }
6809
- async function fileExists(path) {
6810
- try {
6811
- return (await stat(path)).isFile();
6812
- } catch {
6813
- return false;
6814
- }
6815
- }
6816
6769
  //#endregion
6817
6770
  //#region src/update-notifier.ts
6818
6771
  const PACKAGE_NAME = "@theholocron/cli";
@@ -7276,6 +7229,29 @@ try {
7276
7229
  },
7277
7230
  ...argv.steps && argv.steps.length > 0 ? { steps: argv.steps } : {}
7278
7231
  })).summary.fail > 0) process.exitCode = 1;
7232
+ }).command("run <task> [passthrough..]", "Run a task locally (test, typecheck, lint, build) — figures out turbo / the tool / the package manager", (y) => y.positional("task", {
7233
+ type: "string",
7234
+ demandOption: true,
7235
+ describe: "Task name (test, typecheck, lint, build). See `holocron run --help`."
7236
+ }).positional("passthrough", {
7237
+ type: "string",
7238
+ array: true,
7239
+ describe: "Args forwarded to the tool — put them after `--`."
7240
+ }).option("required", {
7241
+ type: "boolean",
7242
+ default: false,
7243
+ describe: "Fail (exit 1) if this repo has no such task, instead of skipping."
7244
+ }), (argv) => {
7245
+ const { logger } = buildCliLogger(argv, { command: "run" });
7246
+ const report = createAstromech({
7247
+ cwd: argv.cwd,
7248
+ logger
7249
+ }).run(argv.task, {
7250
+ passthrough: argv.passthrough ?? [],
7251
+ dryRun: argv.dryRun,
7252
+ required: argv.required
7253
+ });
7254
+ if (report.status === "fail" || report.status === "unknown") process.exitCode = 1;
7279
7255
  }).command("sync-github", "Sync workflow templates and composite actions to theholocron/.github", (y) => y.option("repo", {
7280
7256
  type: "string",
7281
7257
  default: "theholocron/.github",