@theholocron/cli 5.0.0-alpha.9 → 5.0.0-alpha.90

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/index.d.mts CHANGED
@@ -1,7 +1,9 @@
1
- import { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Errors, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, Logs, NormalizedAuthUser, Notifications, PagesConfig, ParseWebhookInput, ProviderApiError, ProviderIdentity, PullRequest, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, Wiki, WikiDnsRecord, WikiProvisionOpts, WikiProxyConfig, Workers, isMulti } from "./plugin/capabilities.mjs";
1
+ import { Analytics, Auth, AuthDescription, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, Ci, CiRun, CiRunFilter, CiRunStatus, ConnectionStringOptions, CreateAuthUserInput, DeployFunctionConfig, DeployFunctionResult, DeployScriptConfig, DeployScriptResult, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordRequest, DnsRecordType, EnsureResult, Environment, EnvironmentReviewer, Environments, Errors, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, Logs, NormalizedAuthUser, Notifications, PagesConfig, ParseWebhookInput, ProviderApiError, ProviderIdentity, PullRequest, REQUIRED_CAPABILITIES, RepoRef, RepoSettings, ResolvedCapability, Ruleset, SecretScope, Secrets, Source, StatusCategory, Storage, StorageBranch, TeamEntry, TeamPermission, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, Wiki, WikiProvisionOpts, WikiProxyConfig, Workers, isMulti } from "./plugin/capabilities.mjs";
2
2
  import { AuthError, RequestOptions, ResolveTokenConfig as ResolveTokenConfig$1, ResolveTokenInput, RestClient, RestClientConfig, createRestClient } from "@theholocron/http-client";
3
3
  import { ConfigFileError } from "@theholocron/datapad";
4
+ import { WorkspacePackage } from "@theholocron/astromech";
4
5
  import { LogLevel } from "@theholocron/observability/core";
6
+ import { QualifiedConfig } from "@commitlint/types";
5
7
  //#region src/auth/auth-resolver.d.ts
6
8
  type ResolveTokenConfig = Omit<ResolveTokenConfig$1, "getKeyringToken">;
7
9
  /** Wraps `createResolveToken` from `@theholocron/http` and injects the
@@ -161,6 +163,21 @@ interface RepoProperties {
161
163
  interface RepoConfig {
162
164
  /** "owner/name" — the GitHub repository coordinate. Derived from the git remote when absent. */
163
165
  name?: string;
166
+ /**
167
+ * GitHub's repo-level default branch (`HEAD`) — what a fresh clone checks
168
+ * out, what `gh pr create`/the compare UI target by default, and what
169
+ * Sentinel's `validateConfig()` reads (D6: the App only ever reads config
170
+ * from the repo's default branch, never a PR ref, as a security boundary
171
+ * — never widen that to read PR branches instead of fixing this field).
172
+ * Synced by `holocron setup`. Omit to leave GitHub's current setting
173
+ * untouched — most repos never need this. Exists for a repo using a
174
+ * `main`/`alpha` prerelease-channel split (CLAUDE.md's Releases section)
175
+ * where active development happens on a non-`main` branch: set this to
176
+ * that branch so Sentinel validates against what's actually being
177
+ * developed, then change it back once that branch merges to `main` for a
178
+ * stable cut and stops being the active branch.
179
+ */
180
+ defaultBranch?: string;
164
181
  /**
165
182
  * Branch protection preset applied by `holocron setup`. When omitted,
166
183
  * no protection is applied and no `branch_protection_level` property is set.
@@ -487,6 +504,29 @@ interface HolocronConfig {
487
504
  * { analytics: "none" }
488
505
  */
489
506
  telemetry?: TelemetryConfig;
507
+ /**
508
+ * ESLint bundle option overrides that Sentinel's centralized static-
509
+ * analysis check (holocron#849) can't otherwise see. Bucket 1 checks
510
+ * never read a PR's own committed files for config — this is the
511
+ * declared source of truth Sentinel reads instead, so its check stays
512
+ * in sync with what each package's own `eslint.config.ts` already
513
+ * enforces locally and in CI.
514
+ */
515
+ eslint?: EslintConfig;
516
+ }
517
+ /**
518
+ * Repo-root-relative directories treated as browser-targeted for eslint's
519
+ * node-builtins compatibility checks — mirrors
520
+ * `@theholocron/eslint-config`'s `library({ browserPackages })` option.
521
+ * Only needed for a package whose own `eslint.config.ts` already declares
522
+ * `browserPackages` (a deliberate outlier, not the norm — see that
523
+ * package's own config file for why).
524
+ *
525
+ * @example
526
+ * { browserPackages: ["packages/github-client/src/app-auth"] }
527
+ */
528
+ interface EslintConfig {
529
+ browserPackages?: string[];
490
530
  }
491
531
  interface ResolvedTuple {
492
532
  provider: string;
@@ -526,6 +566,7 @@ interface ResolvedHolocronConfig {
526
566
  env?: EnvConfig;
527
567
  log?: LogConfig;
528
568
  telemetry?: TelemetryConfig;
569
+ eslint?: EslintConfig;
529
570
  }
530
571
  declare class ConfigError extends Error {
531
572
  name: string;
@@ -539,6 +580,98 @@ declare function resolvePluginPackage(provider: string): string;
539
580
  declare function resolveEntry(key: CapabilityKey, raw: RawProviderEntry): ResolvedProviderEntry;
540
581
  declare function resolveConfig(raw: HolocronConfig): ResolvedHolocronConfig;
541
582
  //#endregion
583
+ //#region src/commands/setup/derived-properties.d.ts
584
+ /**
585
+ * Derived custom-properties (#677). Unlike the existing 6 GitHub
586
+ * custom-properties fields (`lifecycle`, `open_source`, `runtime_environment`,
587
+ * `uses_external_packages`, `monorepo`, `branch_protection_level`) — which
588
+ * mirror what `holocron.config.ts` already states explicitly — these four are
589
+ * *derived*: signal that isn't an explicit config field today (framework
590
+ * choice, repo archetype, capability drift). See
591
+ * `.notes/tech-holocron-platform.spec.md` → "Custom-properties sync — field
592
+ * definitions (#677)" for the full design.
593
+ */
594
+ type HolocronProfile = "library" | "cli" | "plugin" | "template" | "app" | "docs" | "platform";
595
+ interface PackageJsonLike {
596
+ name?: string;
597
+ private?: boolean;
598
+ bin?: unknown;
599
+ dependencies?: Record<string, string>;
600
+ devDependencies?: Record<string, string>;
601
+ }
602
+ interface DeriveProfileInput {
603
+ rootPackageJson: PackageJsonLike | null;
604
+ repoName: string;
605
+ isMonorepo: boolean;
606
+ /** `packages/*` package.json contents, monorepo repos only — used to find the primary published artifact. */
607
+ workspacePackageJsons: PackageJsonLike[];
608
+ }
609
+ /**
610
+ * Repo archetype, in priority order:
611
+ * 1. `*-template` repo name → `template` (independent of package shape).
612
+ * 2. Monorepo whose primary published artifact is a CLI (some workspace
613
+ * package has `bin`) → `platform` — matches `holocron` itself: the CLI is
614
+ * the deliverable, the plugin packages are supporting infrastructure for
615
+ * it, not separate products.
616
+ * 3. Monorepo with no CLI but at least one non-private workspace package →
617
+ * `library` — matches `clients`/`configs`/`utils`/`themes`/
618
+ * `observability`: a family of published packages, none of them a CLI.
619
+ * Checked *before* falling into the root-package branch below, because
620
+ * these repos' own root `package.json` is private and often carries
621
+ * `@theholocron/astro-config` for a docs site built from the monorepo
622
+ * root — without this check they'd wrongly resolve to `docs` off that
623
+ * root-level signal alone, when the docs site is a secondary concern of
624
+ * a library collection, not what the repo fundamentally is.
625
+ * 4. Single-package repo with `package.json#bin` → `cli`.
626
+ * 5. Docs-only site (astro/starlight present, root package private, no
627
+ * publishable exports) → `docs`.
628
+ * 6. Private, non-publishable, non-docs → `app`.
629
+ * 7. Otherwise, a single publishable package → `library`.
630
+ */
631
+ declare function deriveProfile(input: DeriveProfileInput): HolocronProfile;
632
+ /**
633
+ * Detected build/framework tooling — the literal "does this repo need
634
+ * next/vite/tsdown" signal `holocron.config.ts` has no field for today
635
+ * (framework choice isn't a config concept, unlike `providers`/`tasks`).
636
+ * Curated table, not exhaustive by design — add to `KNOWN_STACK_DEPS` as new
637
+ * frameworks show up rather than trying to detect anything on npm.
638
+ */
639
+ declare function deriveStack(pkg: PackageJsonLike | null): string[];
640
+ /** The provider capability keys actually wired in `providers: {}` — zero heuristics. */
641
+ declare function deriveCapabilities(providers: ResolvedProvidersConfig | undefined): string[];
642
+ declare function deriveCompliance(capabilities: readonly string[]): "compliant" | "non-compliant";
643
+ /**
644
+ * The Phase B refinement `deriveCompliance()`'s own doc comment names —
645
+ * *why* a repo is non-compliant, not just that it is. `[]` when compliant.
646
+ * Same `REQUIRED_BASELINE` `deriveCompliance()` checks against — one
647
+ * table, not two independently-maintained baselines.
648
+ */
649
+ declare function missingCapabilities(capabilities: readonly string[]): string[];
650
+ /**
651
+ * Reads each workspace package's actual `package.json` content (not just
652
+ * `readWorkspacePackages()`'s `{ slug, name, dir }` — `deriveProfile()`'s
653
+ * `platform` detection needs `bin`, which that helper doesn't carry).
654
+ * Missing/invalid files are skipped, matching `readWorkspacePackages()`'s
655
+ * own soft-fail behavior.
656
+ */
657
+ declare function readWorkspacePackageJsons(repoRoot: string, workspacePackages: readonly WorkspacePackage[]): Promise<PackageJsonLike[]>;
658
+ //#endregion
659
+ //#region src/commit-lint/lint-message.d.ts
660
+ interface CommitMessageViolation {
661
+ /** Rule name, e.g. `"subject-empty"`. */
662
+ rule: string;
663
+ /** commitlint's own message for the failure, e.g. `"subject may not be empty"`. */
664
+ message: string;
665
+ }
666
+ declare function lintCommitMessage(message: string, loaded: QualifiedConfig): Promise<CommitMessageViolation[]>;
667
+ //#endregion
668
+ //#region src/commit-lint/lint-commit-msg-file.d.ts
669
+ interface LintCommitMsgFileResult {
670
+ valid: boolean;
671
+ violations: CommitMessageViolation[];
672
+ }
673
+ declare function lintCommitMsgFile(filePath: string, cwd?: string): Promise<LintCommitMsgFileResult>;
674
+ //#endregion
542
675
  //#region src/config/compose.d.ts
543
676
  type TaskEntry = NonNullable<HolocronConfig["tasks"]>[number];
544
677
  /** A single composable capability fragment. */
@@ -617,4 +750,56 @@ interface LoadedConfig {
617
750
  */
618
751
  declare function loadConfig(cwd: string): Promise<LoadedConfig>;
619
752
  //#endregion
620
- export { Analytics, AppConfig, Auth, AuthDescription, AuthError, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, Capability, CapabilityConfigPackage, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, ChromaticProjectConfig, Ci, CiRun, CiRunFilter, CiRunStatus, ComposedPreset, ConfigError, ConfigFileError, ConnectionStringOptions, CreateAuthUserInput, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, Dns, DnsRecord, DnsRecordType, DocsConfig, DoctorConfig, EnsureResult, EnvConfig, Environment, EnvironmentReviewer, Environments, Errors, FeatureResolverConfig, HolocronConfig, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, LoadedConfig, LogConfig, Logs, MultiEntry, NormalizedAuthUser, Notifications, PagesConfig, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, PullRequest, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoConfig, RepoProperties, RepoProtection, RepoRef, RepoSettings, type RequestOptions, ResolveTokenConfig, type ResolveTokenInput, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, type RestClient, type RestClientConfig, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, StorybookDeployProject, TaskEntryConfig, TeamEntry, TeamPermission, TelemetryConfig, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, Wiki, WikiDnsRecord, WikiProvisionOpts, WikiProxyConfig, Workers, WorkflowWithConfig, compose, createFeatureResolver, createResolveToken, createRestClient, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
753
+ //#region src/formatting/config.d.ts
754
+ /**
755
+ * The org's canonical prettier ignore-pattern list for a central Sentinel
756
+ * check (holocron#769/#819) — the `PRETTIER_CONFIG` object itself doesn't
757
+ * need a home here, since `@theholocron/prettier-config`'s default export
758
+ * already *is* one canonical, importable config (the same "one config,
759
+ * not N copies" reasoning `@theholocron/commitlint-config` established for
760
+ * commit-message linting, and `ALEX_CONFIG` established for inclusive
761
+ * language) — only the ignore list is a Sentinel-specific decision that
762
+ * belongs alongside `ALEX_IGNORE_PATTERNS`, not inside the config package.
763
+ *
764
+ * Reuses `ALEX_IGNORE_PATTERNS`' own entries (`.github/*`, `CHANGELOG.md`,
765
+ * `LICENSE` all independently confirmed to fail a real `prettier.check()`
766
+ * today — machine-generated or unparseable content, same reasoning that
767
+ * excluded them from alex), plus `pnpm-lock.yaml` — prettier-specific
768
+ * (alex never reads a lockfile, but prettier would try to reformat it):
769
+ * machine-written by pnpm's own writer, and not gitignored (lockfiles are
770
+ * tracked), so prettier picks it up by default. Reformatting it just
771
+ * fights every subsequent `pnpm install` (this repo's own `.prettierignore`
772
+ * already excludes it for that exact reason).
773
+ */
774
+ declare const PRETTIER_IGNORE_PATTERNS: string[];
775
+ //#endregion
776
+ //#region src/inclusive-language/config.d.ts
777
+ /**
778
+ * The org's canonical `alex` (inclusive-language) config, as raw parsed
779
+ * data rather than the pre-rendered file strings `createRcConfig()`/
780
+ * `createIgnoreConfig()` produce for `holocron setup` to write into a
781
+ * repo's own `.alexrc.json`/`.alexignore`.
782
+ *
783
+ * `.alexrc.json` is explicitly "generated by `holocron setup` — do not
784
+ * edit manually" (this repo's own `AGENTS.md`) — the real source of truth
785
+ * is this JSON file, one canonical copy, not each repo's synced one. A
786
+ * central Sentinel check (holocron#769/#793) reads it directly rather
787
+ * than fetching each repo's own copy via the Contents API — the same "one
788
+ * config, not N copies" reasoning `@theholocron/commitlint-config` already
789
+ * established for commit-message linting.
790
+ */
791
+ declare const ALEX_CONFIG: {
792
+ allow: string[];
793
+ };
794
+ /**
795
+ * Glob-ish path patterns `alex` skips. Kept as a literal here rather than
796
+ * importing `../templates/configs/alexjs/alexignore` directly — that file
797
+ * (like `commit-msg`/`pre-push`) is deliberately extensionless, matching
798
+ * the real filename `holocron setup` writes, which ECMAScript's own
799
+ * module resolution (`moduleResolution: "nodenext"`) can't import across
800
+ * a package boundary without an extension. Keep in sync with that file by
801
+ * hand; it changes rarely.
802
+ */
803
+ declare const ALEX_IGNORE_PATTERNS: string[];
804
+ //#endregion
805
+ export { ALEX_CONFIG, ALEX_IGNORE_PATTERNS, Analytics, AppConfig, Auth, AuthDescription, AuthError, AuthEvent, AuthEventType, AuthIdentity, AuthUser, CARDINALITY, Capability, CapabilityConfigPackage, CapabilityImpls, CapabilityKey, Cardinality, CardinalityFor, ChromaticProjectConfig, Ci, CiRun, CiRunFilter, CiRunStatus, CommitMessageViolation, ComposedPreset, ConfigError, ConfigFileError, ConnectionStringOptions, CreateAuthUserInput, DeployFunctionConfig, DeployFunctionResult, DeployScriptConfig, DeployScriptResult, Deployment, DeploymentProject, DeploymentProjectSettings, DeploymentRecord, DeploymentTarget, DeploymentTrigger, DeriveProfileInput, Dns, DnsRecord, DnsRecordRequest, DnsRecordType, DocsConfig, DoctorConfig, EnsureResult, EnvConfig, Environment, EnvironmentReviewer, Environments, Errors, EslintConfig, FeatureResolverConfig, HolocronConfig, HolocronProfile, Issue, IssueSearchFilter, Issues, LabelDef, LifecycleResult, LifecycleSlot, LintCommitMsgFileResult, LoadedConfig, LogConfig, Logs, MultiEntry, NormalizedAuthUser, Notifications, PRETTIER_IGNORE_PATTERNS, PackageJsonLike, PagesConfig, ParseWebhookInput, ProviderApiError, ProviderIdentity, ProviderOptions, PullRequest, REQUIRED_CAPABILITIES, RawProviderEntry, RawProvidersConfig, RepoConfig, RepoProperties, RepoProtection, RepoRef, RepoSettings, type RequestOptions, ResolveTokenConfig, type ResolveTokenInput, ResolvedCapability, ResolvedHolocronConfig, ResolvedProviderEntry, ResolvedProvidersConfig, ResolvedTuple, type RestClient, type RestClientConfig, Ruleset, SecretScope, Secrets, SingleEntry, Source, StatusCategory, Storage, StorageBranch, StorybookDeployProject, TaskEntryConfig, TeamEntry, TeamPermission, TelemetryConfig, Tooling, ToolingDoctorReport, TrackerDoctorReport, TrackerUser, Vault, WebhookDashboardInfo, WebhookVerificationError, Wiki, WikiProvisionOpts, WikiProxyConfig, Workers, WorkflowWithConfig, compose, createFeatureResolver, createResolveToken, createRestClient, defineConfig, deleteToken, deriveCapabilities, deriveCompliance, deriveProfile, deriveStack, getToken, isMulti, lintCommitMessage, lintCommitMsgFile, listStoredProviders, loadConfig, missingCapabilities, readWorkspacePackageJsons, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
package/dist/index.mjs CHANGED
@@ -2,9 +2,11 @@ import { CARDINALITY, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificati
2
2
  import { AuthError, createResolveToken as createResolveToken$1, createRestClient } from "@theholocron/http-client";
3
3
  import { createEnvLookup } from "@theholocron/env-utils";
4
4
  import { Entry, findCredentials } from "@napi-rs/keyring";
5
- import { execFile } from "node:child_process";
6
5
  import { readFile } from "node:fs/promises";
7
6
  import { basename, dirname, join } from "node:path";
7
+ import load from "@commitlint/load";
8
+ import lint from "@commitlint/lint";
9
+ import { execFile } from "node:child_process";
8
10
  import { promisify } from "node:util";
9
11
  import { loadTasksConfig } from "@theholocron/astromech/config";
10
12
  import { ConfigFileError, ConfigFileError as ConfigFileError$1, loadConfigFile } from "@theholocron/datapad";
@@ -113,6 +115,170 @@ function createFeatureResolver(config) {
113
115
  };
114
116
  }
115
117
  //#endregion
118
+ //#region src/commands/setup/derived-properties.ts
119
+ /**
120
+ * Repo archetype, in priority order:
121
+ * 1. `*-template` repo name → `template` (independent of package shape).
122
+ * 2. Monorepo whose primary published artifact is a CLI (some workspace
123
+ * package has `bin`) → `platform` — matches `holocron` itself: the CLI is
124
+ * the deliverable, the plugin packages are supporting infrastructure for
125
+ * it, not separate products.
126
+ * 3. Monorepo with no CLI but at least one non-private workspace package →
127
+ * `library` — matches `clients`/`configs`/`utils`/`themes`/
128
+ * `observability`: a family of published packages, none of them a CLI.
129
+ * Checked *before* falling into the root-package branch below, because
130
+ * these repos' own root `package.json` is private and often carries
131
+ * `@theholocron/astro-config` for a docs site built from the monorepo
132
+ * root — without this check they'd wrongly resolve to `docs` off that
133
+ * root-level signal alone, when the docs site is a secondary concern of
134
+ * a library collection, not what the repo fundamentally is.
135
+ * 4. Single-package repo with `package.json#bin` → `cli`.
136
+ * 5. Docs-only site (astro/starlight present, root package private, no
137
+ * publishable exports) → `docs`.
138
+ * 6. Private, non-publishable, non-docs → `app`.
139
+ * 7. Otherwise, a single publishable package → `library`.
140
+ */
141
+ function deriveProfile(input) {
142
+ if (/-template$/.test(input.repoName)) return "template";
143
+ if (input.isMonorepo) {
144
+ if (input.workspacePackageJsons.some((pkg) => Boolean(pkg.bin))) return "platform";
145
+ if (input.workspacePackageJsons.some((pkg) => pkg.private !== true)) return "library";
146
+ }
147
+ const pkg = input.rootPackageJson;
148
+ if (pkg?.bin) return "cli";
149
+ const deps = {
150
+ ...pkg?.dependencies,
151
+ ...pkg?.devDependencies
152
+ };
153
+ const isDocsSite = Boolean(deps["@theholocron/astro-config"] || deps["astro"]);
154
+ if (pkg?.private && isDocsSite) return "docs";
155
+ if (pkg?.private) return "app";
156
+ return "library";
157
+ }
158
+ const KNOWN_STACK_DEPS = [
159
+ "next",
160
+ "vite",
161
+ "astro",
162
+ "tsdown",
163
+ "rollup",
164
+ "webpack",
165
+ "storybook",
166
+ "vitest",
167
+ "playwright",
168
+ "turbo"
169
+ ];
170
+ /**
171
+ * Detected build/framework tooling — the literal "does this repo need
172
+ * next/vite/tsdown" signal `holocron.config.ts` has no field for today
173
+ * (framework choice isn't a config concept, unlike `providers`/`tasks`).
174
+ * Curated table, not exhaustive by design — add to `KNOWN_STACK_DEPS` as new
175
+ * frameworks show up rather than trying to detect anything on npm.
176
+ */
177
+ function deriveStack(pkg) {
178
+ const deps = {
179
+ ...pkg?.dependencies,
180
+ ...pkg?.devDependencies
181
+ };
182
+ return KNOWN_STACK_DEPS.filter((name) => name in deps);
183
+ }
184
+ /** The provider capability keys actually wired in `providers: {}` — zero heuristics. */
185
+ function deriveCapabilities(providers) {
186
+ return Object.keys(providers ?? {}).sort();
187
+ }
188
+ /**
189
+ * `source` + `ci` are the minimal baseline every repo `holocron setup`
190
+ * touches wires — every `holocron.config.ts` in this org already satisfies
191
+ * this, so `non-compliant` here means drift (a provider manually removed
192
+ * after setup), not an unmet aspirational policy. Deliberately not a richer
193
+ * per-profile policy yet (e.g. "a `library` must have `deployment`") — that's
194
+ * a Phase B (GitHub App) refinement once it can post *why* on a check run.
195
+ */
196
+ const REQUIRED_BASELINE = ["source", "ci"];
197
+ function deriveCompliance(capabilities) {
198
+ return REQUIRED_BASELINE.every((key) => capabilities.includes(key)) ? "compliant" : "non-compliant";
199
+ }
200
+ /**
201
+ * The Phase B refinement `deriveCompliance()`'s own doc comment names —
202
+ * *why* a repo is non-compliant, not just that it is. `[]` when compliant.
203
+ * Same `REQUIRED_BASELINE` `deriveCompliance()` checks against — one
204
+ * table, not two independently-maintained baselines.
205
+ */
206
+ function missingCapabilities(capabilities) {
207
+ return REQUIRED_BASELINE.filter((key) => !capabilities.includes(key));
208
+ }
209
+ /**
210
+ * Reads each workspace package's actual `package.json` content (not just
211
+ * `readWorkspacePackages()`'s `{ slug, name, dir }` — `deriveProfile()`'s
212
+ * `platform` detection needs `bin`, which that helper doesn't carry).
213
+ * Missing/invalid files are skipped, matching `readWorkspacePackages()`'s
214
+ * own soft-fail behavior.
215
+ */
216
+ async function readWorkspacePackageJsons(repoRoot, workspacePackages) {
217
+ const results = [];
218
+ for (const { slug, dir = "packages" } of workspacePackages) try {
219
+ const raw = await readFile(join(repoRoot, dir, slug, "package.json"), "utf8");
220
+ results.push(JSON.parse(raw));
221
+ } catch {}
222
+ return results;
223
+ }
224
+ //#endregion
225
+ //#region src/commit-lint/lint-message.ts
226
+ /**
227
+ * Lints one commit message string against an already-loaded commitlint
228
+ * config. The genuinely environment-independent half of commit-message
229
+ * linting (holocron#789) — *obtaining* a `QualifiedConfig` differs by
230
+ * caller (Sentinel isolates itself from any ambient config since it runs
231
+ * standalone, away from a real repo checkout; `lintCommitMsgFile` below
232
+ * just lets cosmiconfig discover the invoking repo's own
233
+ * `commitlint.config.*`, matching `commitlint --edit`'s own contract) —
234
+ * but linting one message against a loaded config is identical either way.
235
+ *
236
+ * Real `@commitlint/lint` — the same programmatic API `@commitlint/cli`
237
+ * itself uses internally — never a reimplementation of commitlint's rules.
238
+ */
239
+ /** `parserPreset.parserOpts`, if the loaded config sets one — same lookup `@commitlint/cli`'s own `selectParserOpts()` does. */
240
+ function selectParserOpts(parserPreset) {
241
+ return parserPreset?.parserOpts;
242
+ }
243
+ async function lintCommitMessage(message, loaded) {
244
+ const opts = {
245
+ /* istanbul ignore next -- see comment above */
246
+ parserOpts: selectParserOpts(loaded.parserPreset) ?? {},
247
+ plugins: loaded.plugins,
248
+ /* istanbul ignore next -- see comment above */
249
+ ignores: loaded.ignores ?? [],
250
+ defaultIgnores: loaded.defaultIgnores !== false
251
+ };
252
+ const outcome = await lint(message, loaded.rules, opts);
253
+ if (outcome.valid) return [];
254
+ return outcome.errors.map((error) => ({
255
+ rule: error.name,
256
+ message: error.message
257
+ }));
258
+ }
259
+ //#endregion
260
+ //#region src/commit-lint/lint-commit-msg-file.ts
261
+ /**
262
+ * `holocron lint commit-msg <file>` — a real programmatic replacement for
263
+ * shelling out to `commitlint --edit "$1"` from `.husky/commit-msg`
264
+ * (holocron#789). Matches that CLI's own contract exactly: `file` is a
265
+ * path to the commit message being written (the arg git's `commit-msg`
266
+ * hook passes as `$1`), no `--config` override needed — `@commitlint/load`
267
+ * runs from `cwd` with no seed, so cosmiconfig's own normal upward search
268
+ * discovers the invoking repo's real `commitlint.config.*` file, exactly
269
+ * as `commitlint --edit` (no `--config` flag) already does today. Unlike
270
+ * Sentinel's `loadIsolatedConfig()`, no isolation is needed here — this
271
+ * runs inside a real repo checkout with a real `node_modules`, not
272
+ * standalone on a server.
273
+ */
274
+ async function lintCommitMsgFile(filePath, cwd = process.cwd()) {
275
+ const violations = await lintCommitMessage(await readFile(filePath, "utf8"), await load({}, { cwd }));
276
+ return {
277
+ valid: violations.length === 0,
278
+ violations
279
+ };
280
+ }
281
+ //#endregion
116
282
  //#region src/config/config.ts
117
283
  var ConfigError = class extends Error {
118
284
  name = "ConfigError";
@@ -217,7 +383,8 @@ function resolveConfig(raw) {
217
383
  docs,
218
384
  env: raw.env,
219
385
  log: raw.log,
220
- telemetry: raw.telemetry
386
+ telemetry: raw.telemetry,
387
+ eslint: raw.eslint
221
388
  };
222
389
  }
223
390
  //#endregion
@@ -389,4 +556,69 @@ function parseGitRemoteUrl(url) {
389
556
  if (sshMatch) return sshMatch[1];
390
557
  }
391
558
  //#endregion
392
- export { AuthError, CARDINALITY, ConfigError, ConfigFileError, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, compose, createFeatureResolver, createResolveToken, createRestClient, defineConfig, deleteToken, getToken, isMulti, listStoredProviders, loadConfig, resolveConfig, resolveEntry, resolvePluginPackage, setToken };
559
+ //#region src/inclusive-language/config.ts
560
+ /**
561
+ * The org's canonical `alex` (inclusive-language) config, as raw parsed
562
+ * data rather than the pre-rendered file strings `createRcConfig()`/
563
+ * `createIgnoreConfig()` produce for `holocron setup` to write into a
564
+ * repo's own `.alexrc.json`/`.alexignore`.
565
+ *
566
+ * `.alexrc.json` is explicitly "generated by `holocron setup` — do not
567
+ * edit manually" (this repo's own `AGENTS.md`) — the real source of truth
568
+ * is this JSON file, one canonical copy, not each repo's synced one. A
569
+ * central Sentinel check (holocron#769/#793) reads it directly rather
570
+ * than fetching each repo's own copy via the Contents API — the same "one
571
+ * config, not N copies" reasoning `@theholocron/commitlint-config` already
572
+ * established for commit-message linting.
573
+ */
574
+ const ALEX_CONFIG = { allow: [
575
+ "dead",
576
+ "execute",
577
+ "execution",
578
+ "executes",
579
+ "failure",
580
+ "failures",
581
+ "hook",
582
+ "hooks",
583
+ "husky",
584
+ "period"
585
+ ] };
586
+ /**
587
+ * Glob-ish path patterns `alex` skips. Kept as a literal here rather than
588
+ * importing `../templates/configs/alexjs/alexignore` directly — that file
589
+ * (like `commit-msg`/`pre-push`) is deliberately extensionless, matching
590
+ * the real filename `holocron setup` writes, which ECMAScript's own
591
+ * module resolution (`moduleResolution: "nodenext"`) can't import across
592
+ * a package boundary without an extension. Keep in sync with that file by
593
+ * hand; it changes rarely.
594
+ */
595
+ const ALEX_IGNORE_PATTERNS = [
596
+ ".github/*",
597
+ "CHANGELOG.md",
598
+ "LICENSE"
599
+ ];
600
+ //#endregion
601
+ //#region src/formatting/config.ts
602
+ /**
603
+ * The org's canonical prettier ignore-pattern list for a central Sentinel
604
+ * check (holocron#769/#819) — the `PRETTIER_CONFIG` object itself doesn't
605
+ * need a home here, since `@theholocron/prettier-config`'s default export
606
+ * already *is* one canonical, importable config (the same "one config,
607
+ * not N copies" reasoning `@theholocron/commitlint-config` established for
608
+ * commit-message linting, and `ALEX_CONFIG` established for inclusive
609
+ * language) — only the ignore list is a Sentinel-specific decision that
610
+ * belongs alongside `ALEX_IGNORE_PATTERNS`, not inside the config package.
611
+ *
612
+ * Reuses `ALEX_IGNORE_PATTERNS`' own entries (`.github/*`, `CHANGELOG.md`,
613
+ * `LICENSE` all independently confirmed to fail a real `prettier.check()`
614
+ * today — machine-generated or unparseable content, same reasoning that
615
+ * excluded them from alex), plus `pnpm-lock.yaml` — prettier-specific
616
+ * (alex never reads a lockfile, but prettier would try to reformat it):
617
+ * machine-written by pnpm's own writer, and not gitignored (lockfiles are
618
+ * tracked), so prettier picks it up by default. Reformatting it just
619
+ * fights every subsequent `pnpm install` (this repo's own `.prettierignore`
620
+ * already excludes it for that exact reason).
621
+ */
622
+ const PRETTIER_IGNORE_PATTERNS = [...ALEX_IGNORE_PATTERNS, "pnpm-lock.yaml"];
623
+ //#endregion
624
+ export { ALEX_CONFIG, ALEX_IGNORE_PATTERNS, AuthError, CARDINALITY, ConfigError, ConfigFileError, PRETTIER_IGNORE_PATTERNS, ProviderApiError, REQUIRED_CAPABILITIES, WebhookVerificationError, compose, createFeatureResolver, createResolveToken, createRestClient, defineConfig, deleteToken, deriveCapabilities, deriveCompliance, deriveProfile, deriveStack, getToken, isMulti, lintCommitMessage, lintCommitMsgFile, listStoredProviders, loadConfig, missingCapabilities, readWorkspacePackageJsons, resolveConfig, resolveEntry, resolvePluginPackage, setToken };