import { z } from 'zod'; /** * A lean view of the CLI's own `package.json`. Only the fields we actually * read elsewhere are typed — everything else would be unused churn. Extend * here (and in `readOwnPackage` below) when a new consumer needs more. */ interface CliPackageInfo { name: string; version: string; } interface CliContext { /** Directory the CLI was invoked from (user's project root in normal use). */ cwd: string; /** The monorepo root when the CLI runs inside a pnpm workspace; otherwise `cwd`. */ workspaceRoot: string; /** Absolute path of the CLI package (`@lhx-kit/cli`) on disk. */ packageRoot: string; /** Bundled template directory — `${packageRoot}/templates`. */ templatesDir: string; /** Parsed CLI `package.json` (name + version). */ cliPackage: CliPackageInfo; /** * Caret-pinned semver range derived from `cliPackage.version`. Used by * `lhx-cli create` to write the `@lhx-kit/*` dependency versions into * generated `package.json` files, so bumping the CLI automatically * updates what new projects install. * * 0.0.1 → ^0.0.1 (pre-release; patch bumps auto-pick up) * 0.3.5 → ^0.3.0 (stable minor; minor bumps are deliberate) * 1.2.3 → ^1.2.0 (same; majors never leak) * * Rewritten to `workspace:*` when `--link-workspace` is passed, for * in-monorepo scaffolding. */ lhxKitVersionRange: string; } /** * Walk up from `start` looking for a `pnpm-workspace.yaml`. Returns the * directory that contains it, or `start` itself if the CLI is run outside * a pnpm workspace (the common case for user projects). */ declare function findWorkspaceRoot(start: string): string; declare function createContext(cwd?: string): CliContext; type AddKind = 'page' | 'component' | 'api' | 'service' | 'store' | 'schema' | 'module' | 'package'; interface AddOptions { /** For `add page`: override the human-readable title. */ title?: string; /** For `add page`: also register the page under offline whitelist. */ offline?: boolean; /** For `add package`: description written into package.json. */ description?: string; /** For `add package`: force overwrite if target dir exists. */ force?: boolean; /** Non-interactive mode; all required args must come from the CLI. */ yes?: boolean; } declare function runAddCommand(context: CliContext, kindArg: AddKind | undefined, nameArg: string | undefined, options?: AddOptions): Promise; /** * Strategy for resolving `@lhx-kit/*` dependency versions written into the * generated `package.json`: * * - `'auto'` — run `npm view @lhx-kit/cli version` and use the returned * caret-minor range. The most accurate option for users * outside the monorepo, but requires network. Falls back to * `local` on failure (e.g. offline / private registry). * - `'local'` — use the CLI's own version (`@lhx-kit/cli/package.json`). * Stable, no network dependency, but may lag if the CLI * install is older than the latest published kit. * - explicit — any other string is treated as a literal version range * (e.g. `^1.2.0`, `workspace:*`, `1.2.3`). Used by users who * want to pin to a specific kit version intentionally. */ type VersionStrategy = 'auto' | 'local' | string; type TargetMode = 'pc' | 'mobile' | 'hybrid'; interface CreateOptions { template?: string; features?: string; title?: string; /** * Frontend deployment target. Maps to a feature in the `target` mutex group: * `pc` → `target-pc`, `mobile` → `target-mobile`, `hybrid` → `target-hybrid` (default). * Ignored for non-frontend templates. */ target?: TargetMode; /** CSS preprocessor: `less` (default) | `sass` | `none`. */ cssPreprocessor?: 'less' | 'sass' | 'none'; /** CSS atomic system: `unocss` (default) | `tailwind` | `none`. */ cssAtomic?: 'unocss' | 'tailwind' | 'none'; /** Component-level styling: `modules` (default) | `styled` | `vanilla-extract`. */ cssStyling?: 'modules' | 'styled' | 'vanilla-extract'; yes?: boolean; force?: boolean; /** * When true, rewrite every `@lhx-kit/*` dependency version in the generated * package.json to `workspace:*`. Used to scaffold demos/examples inside this * monorepo without publishing the kit packages. */ linkWorkspace?: boolean; /** * Skip all automatic post-create actions (install deps, run typegen, git * init). Useful for CI snapshot tests or when users want to manage these * steps themselves. */ skipInstall?: boolean; /** Package manager to use for install. Defaults to `pnpm`. */ packageManager?: 'pnpm' | 'npm' | 'yarn'; /** Skip `git init`. */ skipGit?: boolean; /** * Strategy for resolving `@lhx-kit/*` dependency versions in the generated * `package.json`. Defaults to `'auto'` (probe `npm view @lhx-kit/cli version` * and use the caret-minor of the result; fall back to `local` on failure). * Use `'local'` to skip the network probe or pass an explicit range like * `^1.2.0` to pin a specific kit version. * * Ignored when `--link-workspace` is set (the rewrite step takes precedence). */ lhxVersion?: VersionStrategy; } declare function runCreateCommand(context: CliContext, projectName: string | undefined, options: CreateOptions): Promise; interface DevBuildOptions { page?: string; pages?: string; mode?: string; /** Forward to the underlying vite dev/preview server. */ host?: boolean | string; /** Forward to the underlying vite dev/preview server. */ port?: number | string; /** Forward to the underlying vite dev/preview server. */ open?: boolean | string; /** Forward to the underlying vite dev/preview server. */ strictPort?: boolean; /** Forward to the underlying vite dev/preview server. */ base?: string; } declare function runDevCommand(context: CliContext, options: DevBuildOptions): Promise; declare function runBuildCommand(context: CliContext, options: DevBuildOptions): Promise; declare function runPreviewCommand(context: CliContext, options: DevBuildOptions): Promise; declare function runDoctorCommand(context: CliContext): Promise; declare function runInfoCommand(context: CliContext): Promise; type HybridType = 'prod' | 'test'; interface OfflineCommandOptions { buildDir?: string; outDir?: string; zip?: boolean; skipBuild?: boolean; hybridType?: HybridType; } declare function runOfflineBuild(context: CliContext, options: OfflineCommandOptions): Promise; declare function runOfflineManifest(context: CliContext, options: OfflineCommandOptions): Promise; declare function runOfflineInspect(context: CliContext, target?: string): Promise; declare function runOfflineDiff(): Promise; declare function runUpgradeCommand(): Promise; declare const PatchOpSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{ op: z.ZodLiteral<"insert-after">; file: z.ZodString; anchor: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"insert-before">; file: z.ZodString; anchor: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"replace">; file: z.ZodString; anchor: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"append">; file: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"prepend">; file: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"merge-imports">; file: z.ZodString; imports: z.ZodArray; }, z.core.$strip>], "op">; type PatchOp = z.infer; declare const FeatureManifestSchema: z.ZodObject<{ name: z.ZodString; title: z.ZodString; description: z.ZodString; appliesTo: z.ZodArray; mutexGroup: z.ZodNullable; isDefault: z.ZodDefault; priority: z.ZodDefault; conflictsWith: z.ZodOptional>; requires: z.ZodOptional>; requireAnchors: z.ZodOptional>>; files: z.ZodOptional>; patches: z.ZodOptional; file: z.ZodString; anchor: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"insert-before">; file: z.ZodString; anchor: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"replace">; file: z.ZodString; anchor: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"append">; file: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"prepend">; file: z.ZodString; content: z.ZodString; }, z.core.$strip>, z.ZodObject<{ op: z.ZodLiteral<"merge-imports">; file: z.ZodString; imports: z.ZodArray; }, z.core.$strip>], "op">>>; packageOverlay: z.ZodOptional>; workspaceOverlay: z.ZodOptional>; gitignoreAppend: z.ZodOptional>; monorepoExpand: z.ZodOptional>; }, z.core.$strip>; type FeatureManifestZ = z.infer; interface TemplateManifest { name: string; title: string; description: string; framework?: 'vue3' | 'react'; category?: 'frontend' | 'backend' | 'library' | 'business'; projectType?: 'spa' | 'h5' | 'admin' | 'service' | 'lib' | 'monorepo'; /** * If set to `"_shared"`, the template inherits the cross-cutting baseline * files from `templates/_shared/files/` before its own `files/` are copied. */ extends?: '_shared'; /** Feature names enabled by default when the user runs `-y` and supplies no `--features`. */ defaultFeatures?: string[]; tags?: string[]; /** * Extra npm-scope prefixes (e.g. `@my-org/`) to treat as "internal" alongside * the built-in `@lhx-kit/`, `@lhx-cli/`, `@lhx-business/` defaults. Internal * scopes get dynamic `npm view` version resolution and are eligible for the * `--link-workspace` rewrite. See `version-resolver.ts`. */ internalPackagePrefixes?: string[]; features?: TemplateFeatureManifest[]; postCreate?: string[]; } interface TemplateFeatureManifest { name: string; title: string; description: string; patchDir?: string; defaultEnabled?: boolean; } interface TemplateSource { type: 'builtin' | 'local' | 'remote'; name: string; directory: string; manifest: TemplateManifest; } interface TemplateVariables { projectName: string; packageName: string; appTitle: string; features: string[]; year: number; /** * Semver range to pin `@lhx-kit/*` dependencies to in the generated * project. Typically the caret-range of the CLI itself * (`^..0`) so that: * - bug-fix patches flow in automatically (`0.3.5 → 0.3.7`) * - minor bumps (`0.3 → 0.4`) require a deliberate upgrade * * Rewritten to `workspace:*` when `--link-workspace` is set (in-monorepo * scaffolding); otherwise this is the value that lands in the generated * `package.json`. */ lhxKitVersionRange: string; /** * Library scaffold variables (only meaningful when the template is a * library — `lib-single` / `lib-monorepo`). Empty / sensible defaults * for non-library templates so token substitution stays no-op. */ /** Selected bundler name: `tsup` | `rslib` | `rollup` (or `''` for non-lib). */ libBundlerName: string; /** Comma-separated formats list, e.g. `esm,cjs`. */ libFormats: string; /** Human-readable list, e.g. `esm + cjs + umd`. */ libFormatsHumanList: string; /** JS array literal of formats for tsup, e.g. `['esm', 'cjs']`. */ libFormatsTsupLiteral: string; /** JS array literal for rollup outputs, embedded as the `output` array. */ libFormatsRollupOutputsLiteral: string; /** JS array literal for rslib `lib` field. */ libFormatsRslibLiteral: string; /** UMD global name derived from `packageName` (PascalCase). */ libUmdGlobalName: string; /** * Index signature so this type is assignable to `Record` * without a cast, which is what `renderString` / `copyTemplateDir` / * `applyFeature` accept (the `add` command also feeds those helpers a * plain record of per-invocation vars). */ [key: string]: unknown; } /** * Minimal token engine: replaces `<%= name %>` with variables[name]. * Also supports `<%= appTitle %>`, `<%= projectName %>`, etc. * Chosen over handlebars to avoid conflicts with JSX `{{...}}`. * * Accepts any record shape so the same engine drives both `create`-time * project variables (`TemplateVariables`) and `add`-time per-invocation * variables (page name, component name, …). */ declare function renderString(input: string, variables: Record): string; declare function listBuiltinTemplates(templatesDir: string): Promise; declare function readTemplateSource(templatesDir: string, nameOrSource: string): Promise; interface CopyDirOptions { sourceDir: string; targetDir: string; variables: Record; } declare function copyTemplateDir(options: CopyDirOptions): Promise; /** * `_shared/` baseline directory under the templates root, copied before any * template-specific files when a template declares `extends: "_shared"`. * Returns `null` when the layer is absent (e.g. running against an old * template tarball that predates the shared layer). */ declare function resolveSharedDir(templatesDir: string): string | null; interface CopyStubsOptions { sourceDir: string; targetDir: string; variables: Record; } interface CopyStubsEntry { /** Destination path relative to `targetDir` (with `<%= … %>` substituted). */ rel: string; /** `false` when the file already existed and was left untouched. */ created: boolean; } /** * Copy a stub tree under `templates/_add/...` into `targetDir`, substituting * `<%= var %>` in both file paths and file contents. Files already present at * the destination are skipped — re-running an `lhx-cli add` command never * clobbers user edits. * * Differs from `copyTemplateDir`: * - Skip-if-exists semantics (no append/merge for `.gitignore` / `.env` / * `package.json`). Stubs are per-invocation and idempotency comes from * skip-if-exists rather than the merge magic that `create` needs. * - No special-case rendering paths — every file reads as UTF-8 and writes * the rendered body. Don't put binary assets under `_add/`. */ declare function copyStubs(options: CopyStubsOptions): Promise; /** * Read a single stub file from disk and return its rendered body. Caller * decides the destination path — used by `lhx-cli add` for kinds whose * destination depends on runtime checks (e.g. `add schema` writes under * `pages//render.json` or `schemas/.json` depending on whether * the page dir already exists). */ declare function renderStubFile(stubFile: string, variables: Record): Promise; interface ScaffoldFeature { name: string; /** Absolute directory containing files/, feature.json, patches.json (when present). */ directory: string; /** Parsed feature.json (zod-validated). */ manifest: FeatureManifestZ; } /** * Load and validate a feature manifest from `/feature.json`. * Returns `null` for legacy directories that have no manifest yet (e.g. the * original `offline` feature that pre-dates the schema). Callers fall back to * the legacy "copy patchDir verbatim" behavior in that case. */ declare function loadFeatureManifest(directory: string): Promise; /** * Discover features for a top-level template by scanning `