@lunora/cli 1.0.0-alpha.2 → 1.0.0-alpha.21

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.
Files changed (48) hide show
  1. package/dist/bin.mjs +1 -1
  2. package/dist/index.d.mts +217 -113
  3. package/dist/index.d.ts +217 -113
  4. package/dist/index.mjs +7 -7
  5. package/dist/packem_chunks/handler.mjs +80 -6
  6. package/dist/packem_chunks/handler10.mjs +1 -1
  7. package/dist/packem_chunks/handler11.mjs +1 -1
  8. package/dist/packem_chunks/handler12.mjs +1 -1
  9. package/dist/packem_chunks/handler13.mjs +1 -1
  10. package/dist/packem_chunks/handler14.mjs +2 -2
  11. package/dist/packem_chunks/handler15.mjs +1 -1
  12. package/dist/packem_chunks/handler16.mjs +5 -3
  13. package/dist/packem_chunks/handler17.mjs +1 -1
  14. package/dist/packem_chunks/handler18.mjs +4 -6
  15. package/dist/packem_chunks/handler19.mjs +3 -3
  16. package/dist/packem_chunks/handler2.mjs +2 -2
  17. package/dist/packem_chunks/handler20.mjs +1 -1
  18. package/dist/packem_chunks/handler21.mjs +2 -2
  19. package/dist/packem_chunks/handler3.mjs +1 -1
  20. package/dist/packem_chunks/handler4.mjs +1 -1
  21. package/dist/packem_chunks/handler5.mjs +2 -2
  22. package/dist/packem_chunks/handler6.mjs +2 -2
  23. package/dist/packem_chunks/handler7.mjs +1 -1
  24. package/dist/packem_chunks/handler8.mjs +1 -1
  25. package/dist/packem_chunks/handler9.mjs +1 -1
  26. package/dist/packem_chunks/planDevCommand.mjs +6 -49
  27. package/dist/packem_chunks/runCodegenCommand.mjs +2 -2
  28. package/dist/packem_chunks/runDeployCommand.mjs +3 -3
  29. package/dist/packem_chunks/runInitCommand.mjs +1948 -107
  30. package/dist/packem_chunks/runMigrateGenerateCommand.mjs +5 -5
  31. package/dist/packem_chunks/runResetCommand.mjs +4 -4
  32. package/dist/packem_chunks/runRpcCommand.mjs +1 -1
  33. package/dist/packem_shared/{COMMANDS-1V_KEx35.mjs → COMMANDS-D3h9Iwvl.mjs} +45 -6
  34. package/dist/packem_shared/{command-BDXcJCCJ.mjs → command-BC30oSBW.mjs} +1 -1
  35. package/dist/packem_shared/{runAddCommand-BZGkRnBs.mjs → commands-hl0mRqqg.mjs} +177 -25
  36. package/dist/packem_shared/{createLogger-CHPNjFw2.mjs → createLogger-B40gPzQo.mjs} +9 -4
  37. package/dist/packem_shared/detect-package-manager-DYp7n3mJ.mjs +61 -0
  38. package/dist/packem_shared/{diffSnapshots-RR2ZE8Ya.mjs → diffSnapshots-BeDvvNiF.mjs} +1 -1
  39. package/dist/packem_shared/{insertSchemaExtension-BuzF6-t2.mjs → insertSchemaExtension-DAqbfr9Z.mjs} +15 -10
  40. package/dist/packem_shared/{output-format-7gyGR3h8.mjs → output-format-wUvAN6AL.mjs} +1 -1
  41. package/dist/packem_shared/runAddCommand-vJdgiR5t.mjs +4 -0
  42. package/dist/packem_shared/{schemaIrToSnapshot-aBTo7TM5.mjs → schemaIrToSnapshot-DdsljJT-.mjs} +1 -1
  43. package/dist/packem_shared/storage-B7hHSTZP.mjs +84 -0
  44. package/dist/packem_shared/tui-prompts-M6OWsuyw.mjs +663 -0
  45. package/package.json +11 -10
  46. package/skills/lunora-setup-storage/SKILL.md +7 -3
  47. package/dist/packem_shared/features-ocSSpZtS.mjs +0 -24
  48. /package/dist/packem_shared/{defaultSpawner-DxI3mebw.mjs → createRecordingSpawner-DxI3mebw.mjs} +0 -0
package/dist/index.d.ts CHANGED
@@ -64,6 +64,12 @@ declare const createLogger: () => Logger;
64
64
  * the package barrel) stays side-effect-free.
65
65
  */
66
66
  declare const pail: PailLogger;
67
+ /**
68
+ * Emit a badged step line through the shared pail (the `init` flow's off-TTY
69
+ * fallback for the create-astro-style transcript). The `message` may contain
70
+ * newlines — `LunoraReporter` indents continuation lines under the badge so a
71
+ * dimmed answer sits below its question.
72
+ */
67
73
  interface CodegenCommandOptions {
68
74
  /** Which API spec(s) to emit. Defaults to codegen's `"openapi"` when omitted. */
69
75
  apiSpec?: ApiSpec;
@@ -496,15 +502,145 @@ declare const runDevCommand: (options: DevCommandOptions) => Promise<{
496
502
  /** `lunora dev` handler (lazy-loaded via the command's `loader`). */
497
503
  /** Supported CI providers. */
498
504
  type CiProvider = "github" | "gitlab";
505
+ type PackageManager = "pnpm" | "npm" | "yarn" | "bun";
506
+ /** True when `manager` is on PATH — probed by running `&lt;manager> --version`. Injectable for tests. */
507
+ type PackageManagerProbe = (manager: PackageManager) => boolean;
508
+ /**
509
+ * The package managers actually installed on this machine, in preference order
510
+ * ({@link INSTALL_PREFERENCE} — pnpm > bun > yarn > npm). The first entry is the
511
+ * recommended default for the install prompt; the whole list is what the user
512
+ * picks from. Empty when none are found.
513
+ */
499
514
  /** A registry item a feature can install. */
500
515
  type FeatureItem = "auth" | "auth-auth0" | "auth-clerk" | "mail";
501
516
  /** The auth-provider choices offered for `add auth` / the init auth prompt. Each value is a registry item name. */
502
-
503
- /** A feature offered in the post-scaffold multi-select. `value` is the stack-feature key, not (yet) a registry item. */
504
- type StackFeature = "auth" | "email";
517
+ /** A single file the item scaffolds into the project. */
518
+ interface RegistryFile {
519
+ /** Source path inside the item dir (e.g. `schema.ts`). */
520
+ from: string;
521
+ /** Merge strategy. `create-or-skip` writes whole files; `schema-extension` AST-merges schema.ts. */
522
+ merge: "create-or-skip" | "schema-extension";
523
+ /** Destination relative to the project root (e.g. `lunora/ratelimit/index.ts`). */
524
+ to: string;
525
+ }
526
+ /** A wrangler.jsonc binding addition. `path` is the jsonc key path; `value` the value to set. */
527
+ interface RegistryBinding {
528
+ path: ReadonlyArray<string>;
529
+ value: unknown;
530
+ }
531
+ /**
532
+ * An environment variable an item needs. Scaffolded into `.dev.vars` (Workers'
533
+ * local-secrets file) on add — non-secrets get their `value`; secrets get an
534
+ * empty placeholder and a reminder to run `wrangler secret put` for production.
535
+ */
536
+ interface RegistryEnvVariable {
537
+ /** Human note on what the variable is for. */
538
+ description?: string;
539
+ /** The variable name (e.g. `RESEND_API_KEY`). */
540
+ name: string;
541
+ /** Mark as a secret: never write a value, only a placeholder, and remind about prod. Defaults to `true` when no `value` is given. */
542
+ secret?: boolean;
543
+ /** A default/example value for non-secret vars. */
544
+ value?: string;
545
+ }
546
+ /** The `registry.json` manifest shape. */
547
+ interface RegistryManifest {
548
+ /** wrangler.jsonc additions (best-effort structural edits). */
549
+ bindings?: ReadonlyArray<RegistryBinding>;
550
+ /** npm deps to add to the project package.json (name → version range). */
551
+ deps?: Readonly<Record<string, string>>;
552
+ description?: string;
553
+ /** npm devDependencies to add to the project package.json. */
554
+ devDependencies?: Readonly<Record<string, string>>;
555
+ /** Post-install guidance printed after the item is added (per-item next steps). */
556
+ docs?: string;
557
+ /** Environment variables the item needs; scaffolded into `.dev.vars`. */
558
+ envVars?: ReadonlyArray<RegistryEnvVariable>;
559
+ files: ReadonlyArray<RegistryFile>;
560
+ name: string;
561
+ /** Other registry items this one depends on (resolved transitively, deps first). */
562
+ requires?: ReadonlyArray<string>;
563
+ /** Short human-readable label (distinct from the longer `description`). */
564
+ title?: string;
565
+ }
566
+ interface AddCommandOptions {
567
+ /** Bypass the `--source` safety gate (matches init). */
568
+ allowUnsafeSource?: boolean;
569
+ /** `registry build --check`: verify the index is current instead of rewriting it. */
570
+ check?: boolean;
571
+ /** Inject a confirmer for non-interactive callers / tests. */
572
+ confirm?: (prompt: string) => Promise<boolean>;
573
+ cwd?: string;
574
+ /** Preview the file-level changes (a content diff) and write nothing. */
575
+ diff?: boolean;
576
+ /** Print the plan and stop without writing anything. */
577
+ dryRun?: boolean;
578
+ /** Local registry root (offline / tests). Expects per-item subdirs, each with a `registry.json`. */
579
+ from?: string;
580
+ /** Emit a JSON snapshot of the plan/result. */
581
+ json?: boolean;
582
+ /** `--list`: enumerate available items instead of adding. */
583
+ list?: boolean;
584
+ logger: Logger;
585
+ /** Item names to add (positional args). */
586
+ names: ReadonlyArray<string>;
587
+ /** `registry build` output path for the generated catalog (defaults to the root's `index.json`). */
588
+ out?: string;
589
+ /** Force-overwrite existing files (take the incoming copy) instead of skipping/conflicting. */
590
+ overwrite?: boolean;
591
+ /** Override the git ref (branch, tag, or commit) items are fetched from (default: version-derived); appended to the `source` base when that is set. Ignored when `from` is set. */
592
+ ref?: string;
593
+ /** Override the remote registry source base (default gh:anolilab/lunora/registry). */
594
+ source?: string;
595
+ /**
596
+ * Customize each resolved manifest after it is loaded but before the plan is
597
+ * printed / reconciled — used to inject user-chosen values into otherwise
598
+ * static manifests (e.g. the R2 `bucket_name` the init storage prompt asks
599
+ * for). Applied to every item; return the manifest unchanged to leave it as-is.
600
+ */
601
+ transformManifest?: (manifest: RegistryManifest) => RegistryManifest;
602
+ /** Skip the package.json mutation confirmation prompt. */
603
+ yes?: boolean;
604
+ }
605
+ interface AddCommandResult {
606
+ /** Bindings written to wrangler.jsonc. */
607
+ bindings: ReadonlyArray<string>;
608
+ code: number;
609
+ /** Deps added to package.json. */
610
+ deps: ReadonlyArray<string>;
611
+ /** Files skipped because they already existed. */
612
+ skipped: ReadonlyArray<string>;
613
+ /** Files written (absolute paths). */
614
+ written: ReadonlyArray<string>;
615
+ }
616
+ /** One resolved item: its parsed manifest plus the (possibly staged) directory it lives in. */
617
+ /**
618
+ * A feature offered in the post-scaffold multi-select. `auth`/`email` carry a
619
+ * sub-prompt or alias; every other value IS the registry item name applied
620
+ * directly (`storage` → the `storage` registry item, etc.).
621
+ */
622
+ type StackFeature = "auth" | "backup" | "crons" | "email" | "presence" | "ratelimit" | "storage";
623
+ /** Customize a resolved manifest before it is written (e.g. inject the chosen R2 bucket name). */
624
+ type OfferTransformManifest = (manifest: RegistryManifest) => RegistryManifest;
625
+ /**
626
+ * One feature ready to apply: the registry item name(s), an optional manifest
627
+ * transform, and a short `label` (the feature value) shown on the combined
628
+ * progress line. Built up-front by the collectors so every prompt is answered
629
+ * before any apply runs.
630
+ */
631
+ interface FeatureApply {
632
+ label: string;
633
+ names: ReadonlyArray<string>;
634
+ transformManifest?: OfferTransformManifest;
635
+ }
505
636
  interface OfferDeps {
506
- /** Apply one or more registry items into the new project; resolves `true` on success. */
507
- apply: (names: ReadonlyArray<FeatureItem>) => Promise<boolean>;
637
+ /**
638
+ * Apply the collected features into the new project in one batch — resolves
639
+ * `true` when every item succeeds. The CLI renders this as a single progress
640
+ * line whose label changes per feature; each plan's `transformManifest`
641
+ * customizes that item's manifest before it is written.
642
+ */
643
+ applyAll: (plans: ReadonlyArray<FeatureApply>) => Promise<boolean>;
508
644
  /** When `false`, skip all prompts and print the later-setup hint. */
509
645
  interactive: boolean;
510
646
  logger: Logger;
@@ -516,6 +652,14 @@ interface OfferDeps {
516
652
  }>, settings?: {
517
653
  defaults?: ReadonlyArray<StackFeature>;
518
654
  }) => Promise<StackFeature[]>;
655
+ /**
656
+ * Features chosen non-interactively (the `--add` flag). When set, the
657
+ * multi-select and every sub-prompt are skipped — each feature is applied with
658
+ * its shipped defaults (base registry item, placeholder bindings).
659
+ */
660
+ preselected?: ReadonlyArray<StackFeature>;
661
+ /** The new project's name — seeds smart defaults like the `project-uploads` bucket name. */
662
+ projectName: string;
519
663
  /** Single-select among the auth providers (TTY-backed in production). */
520
664
  select: (message: string, options: ReadonlyArray<{
521
665
  description?: string;
@@ -524,16 +668,32 @@ interface OfferDeps {
524
668
  }>, settings?: {
525
669
  default?: FeatureItem;
526
670
  }) => Promise<FeatureItem | undefined>;
671
+ /** Single-line text input (TTY-backed in production) — used for the storage bucket-name prompt. */
672
+ text: (message: string, settings?: {
673
+ default?: string;
674
+ placeholder?: string;
675
+ }) => Promise<string>;
527
676
  }
528
677
  /**
529
- * Offer the stack features (authentication, transactional email) in ONE
530
- * multi-select after a successful scaffold. When auth is picked, a follow-up
531
- * single-select chooses the provider (email+password / Clerk / Auth0); email
532
- * maps to the `mail` item. Picked items are applied in selection order.
533
- * Non-interactive: prints how to add them later and changes nothing.
678
+ * Offer the stack features (auth, email, storage, rate limiting, crons,
679
+ * presence, backups) in ONE multi-select after a successful scaffold. Auth,
680
+ * email, and storage run a follow-up prompt (provider / destination / bucket
681
+ * name); every other feature value is applied as its registry item directly.
682
+ *
683
+ * Every question is asked FIRST (in selection order), then the picked features
684
+ * are applied together via {@link OfferDeps.applyAll} — the CLI renders that as a
685
+ * single progress line whose label changes per feature, instead of one spinner
686
+ * per item. Non-interactive: prints how to add them later and changes nothing.
534
687
  */
535
- type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite";
688
+ type Template = "analog" | "astro" | "next" | "nuxt" | "react-router" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid";
536
689
  interface InitCommandOptions {
690
+ /**
691
+ * Add features non-interactively after scaffolding (the `--add` flag): a
692
+ * comma-separated list of `auth | email | storage | ratelimit | crons |
693
+ * presence | backup`. Bypasses the interactive multi-select and sub-prompts —
694
+ * each named feature is applied with its shipped defaults.
695
+ */
696
+ add?: string;
537
697
  /**
538
698
  * When true, accept `--source` values that don't start with `gh:` /
539
699
  * `github:` / `https://` or that contain `..`. Defaults to false; the CLI
@@ -545,6 +705,12 @@ interface InitCommandOptions {
545
705
  ci?: CiProvider;
546
706
  cwd?: string;
547
707
  /**
708
+ * Walk the whole flow — prompts, task list, next-steps, mascot — but make no
709
+ * changes: skip the template fetch/copy, the feature applies, the dependency
710
+ * install, and `git init`. Each skipped action logs a `would …` line instead.
711
+ */
712
+ dryRun?: boolean;
713
+ /**
548
714
  * Local directory containing the template subdirs (e.g. `vite/`,
549
715
  * `standalone/`). When provided, skips the network fetch entirely.
550
716
  * Useful for offline runs, the clean-machine smoke test, and unit tests.
@@ -559,6 +725,15 @@ interface InitCommandOptions {
559
725
  */
560
726
  inPlace?: boolean;
561
727
  /**
728
+ * Inject the post-scaffold install offer's prompts (tests). When set, the
729
+ * offer runs regardless of TTY: `confirmInstall` drives the yes/no, and
730
+ * `selectManager` picks among the detected managers.
731
+ */
732
+ installPrompt?: {
733
+ confirmInstall: () => Promise<boolean>;
734
+ selectManager: (managers: ReadonlyArray<PackageManager>) => Promise<PackageManager>;
735
+ };
736
+ /**
562
737
  * Force the post-scaffold "add auth / email?" offer on (the `--interactive`
563
738
  * flag). When omitted, the offer runs only when stdin is a TTY. `--yes`
564
739
  * suppresses it regardless. Has no effect once {@link prompt} is injected.
@@ -567,22 +742,47 @@ interface InitCommandOptions {
567
742
  logger: Logger;
568
743
  name?: string;
569
744
  /**
745
+ * Local directory holding create-vite bases (one `template-&lt;id>/` subdir per
746
+ * framework). When set with `vite`, the overlay copies the base from disk
747
+ * instead of fetching `create-vite` over the network — offline mode + tests.
748
+ */
749
+ overlayBaseFrom?: string;
750
+ /** Probe for which package managers are installed (tests). Defaults to a real `&lt;pm> --version` check. */
751
+ packageManagerProbe?: PackageManagerProbe;
752
+ /**
570
753
  * Inject the offer's prompts (tests). When set, the offer is treated as
571
- * interactive regardless of TTY, and these drive the feature multi-select
572
- * and the auth-provider sub-select.
754
+ * interactive regardless of TTY, and these drive the feature multi-select,
755
+ * the auth-provider sub-select, and the storage bucket-name text input.
573
756
  */
574
- prompt?: Pick<OfferDeps, "multiSelect" | "select">;
757
+ prompt?: Pick<OfferDeps, "multiSelect" | "select" | "text">;
758
+ /**
759
+ * Override the git ref (branch, tag, or commit) the default template source
760
+ * is fetched from. Takes precedence over the version-derived ref. Ignored
761
+ * when `source` or `from` is set.
762
+ */
763
+ ref?: string;
575
764
  /** Local registry root for the offer's `runAddCommand` (offline / tests). Mirrors `from` but for registry items. */
576
765
  registryFrom?: string;
577
766
  /** Override the remote registry source base for the offer (default `gh:anolilab/lunora/registry`). */
578
767
  registrySource?: string;
579
768
  /**
580
769
  * Override the remote source giget downloads from. Default:
581
- * `gh:anolilab/lunora/templates/&lt;templateType>#v&lt;cliVersion>`. Tests
582
- * typically use `from` instead to skip the network.
770
+ * `gh:anolilab/lunora/templates/&lt;templateType>#&lt;ref>`, where `&lt;ref>` is
771
+ * the `ref` option when set, else derived from the CLI version (pre-release
772
+ * channels → their branch, stable → `main`). Tests typically use `from`
773
+ * instead to skip the network.
583
774
  */
584
775
  source?: string;
776
+ /** Spawner for the post-scaffold dependency install (tests inject a recording stub). Defaults to a real subprocess. */
777
+ spawner?: Spawner;
585
778
  templateType?: Template;
779
+ /**
780
+ * Scaffold via the **create-vite overlay** for this framework (`react`,
781
+ * `vue`, `solid`, `svelte`, `vanilla`) instead of a bespoke template: fetch
782
+ * the official create-vite base and apply the Lunora layer on top. Takes
783
+ * precedence over `templateType`.
784
+ */
785
+ vite?: string;
586
786
  /** Suppress the offer entirely (the `--yes` flag): scaffold only, print the later-setup hint. */
587
787
  yes?: boolean;
588
788
  }
@@ -591,13 +791,8 @@ interface InitCommandResult {
591
791
  files: ReadonlyArray<string>;
592
792
  target: string;
593
793
  }
594
- /**
595
- * `lunora init` entry: scaffold (in-place or a new directory), then — on success
596
- * — offer to add auth + email via the registry. The offer never affects the
597
- * scaffold's exit code.
598
- */
599
794
  declare const runInitCommand: (options: InitCommandOptions) => Promise<InitCommandResult>;
600
- /** Narrow a raw `--template` value to a known {@link Template} (defaults to vite). */
795
+ /** Narrow a raw `--template` value to a known {@link Template}. */
601
796
  interface MigrateGenerateCommandOptions {
602
797
  cwd?: string;
603
798
  logger: Logger;
@@ -633,97 +828,6 @@ interface IndexItem extends CatalogItem {
633
828
  declare const buildRegistryIndex: (root: string) => {
634
829
  items: IndexItem[];
635
830
  };
636
- /** A single file the item scaffolds into the project. */
637
- interface RegistryFile {
638
- /** Source path inside the item dir (e.g. `schema.ts`). */
639
- from: string;
640
- /** Merge strategy. `create-or-skip` writes whole files; `schema-extension` AST-merges schema.ts. */
641
- merge: "create-or-skip" | "schema-extension";
642
- /** Destination relative to the project root (e.g. `lunora/ratelimit/index.ts`). */
643
- to: string;
644
- }
645
- /** A wrangler.jsonc binding addition. `path` is the jsonc key path; `value` the value to set. */
646
- interface RegistryBinding {
647
- path: ReadonlyArray<string>;
648
- value: unknown;
649
- }
650
- /**
651
- * An environment variable an item needs. Scaffolded into `.dev.vars` (Workers'
652
- * local-secrets file) on add — non-secrets get their `value`; secrets get an
653
- * empty placeholder and a reminder to run `wrangler secret put` for production.
654
- */
655
- interface RegistryEnvVariable {
656
- /** Human note on what the variable is for. */
657
- description?: string;
658
- /** The variable name (e.g. `RESEND_API_KEY`). */
659
- name: string;
660
- /** Mark as a secret: never write a value, only a placeholder, and remind about prod. Defaults to `true` when no `value` is given. */
661
- secret?: boolean;
662
- /** A default/example value for non-secret vars. */
663
- value?: string;
664
- }
665
- /** The `registry.json` manifest shape. */
666
- interface RegistryManifest {
667
- /** wrangler.jsonc additions (best-effort structural edits). */
668
- bindings?: ReadonlyArray<RegistryBinding>;
669
- /** npm deps to add to the project package.json (name → version range). */
670
- deps?: Readonly<Record<string, string>>;
671
- description?: string;
672
- /** npm devDependencies to add to the project package.json. */
673
- devDependencies?: Readonly<Record<string, string>>;
674
- /** Post-install guidance printed after the item is added (per-item next steps). */
675
- docs?: string;
676
- /** Environment variables the item needs; scaffolded into `.dev.vars`. */
677
- envVars?: ReadonlyArray<RegistryEnvVariable>;
678
- files: ReadonlyArray<RegistryFile>;
679
- name: string;
680
- /** Other registry items this one depends on (resolved transitively, deps first). */
681
- requires?: ReadonlyArray<string>;
682
- /** Short human-readable label (distinct from the longer `description`). */
683
- title?: string;
684
- }
685
- interface AddCommandOptions {
686
- /** Bypass the `--source` safety gate (matches init). */
687
- allowUnsafeSource?: boolean;
688
- /** `registry build --check`: verify the index is current instead of rewriting it. */
689
- check?: boolean;
690
- /** Inject a confirmer for non-interactive callers / tests. */
691
- confirm?: (prompt: string) => Promise<boolean>;
692
- cwd?: string;
693
- /** Preview the file-level changes (a content diff) and write nothing. */
694
- diff?: boolean;
695
- /** Print the plan and stop without writing anything. */
696
- dryRun?: boolean;
697
- /** Local registry root (offline / tests). Expects per-item subdirs, each with a `registry.json`. */
698
- from?: string;
699
- /** Emit a JSON snapshot of the plan/result. */
700
- json?: boolean;
701
- /** `--list`: enumerate available items instead of adding. */
702
- list?: boolean;
703
- logger: Logger;
704
- /** Item names to add (positional args). */
705
- names: ReadonlyArray<string>;
706
- /** `registry build` output path for the generated catalog (defaults to the root's `index.json`). */
707
- out?: string;
708
- /** Force-overwrite existing files (take the incoming copy) instead of skipping/conflicting. */
709
- overwrite?: boolean;
710
- /** Override the remote registry source base (default gh:anolilab/lunora/registry). */
711
- source?: string;
712
- /** Skip the package.json mutation confirmation prompt. */
713
- yes?: boolean;
714
- }
715
- interface AddCommandResult {
716
- /** Bindings written to wrangler.jsonc. */
717
- bindings: ReadonlyArray<string>;
718
- code: number;
719
- /** Deps added to package.json. */
720
- deps: ReadonlyArray<string>;
721
- /** Files skipped because they already existed. */
722
- skipped: ReadonlyArray<string>;
723
- /** Files written (absolute paths). */
724
- written: ReadonlyArray<string>;
725
- }
726
- /** One resolved item: its parsed manifest plus the (possibly staged) directory it lives in. */
727
831
  /** `lunora registry add` (one or more item names): scaffold items into the project. */
728
832
  declare const runAddCommand: (options: AddCommandOptions) => Promise<AddCommandResult>;
729
833
  /**
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-1V_KEx35.mjs';
1
+ export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-D3h9Iwvl.mjs';
2
2
  export { runCodegenCommand } from './packem_chunks/runCodegenCommand.mjs';
3
3
  export { DEFAULT_IMPORT_BATCH_SIZE, runExportCommand, runImportCommand } from './packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
4
4
  export { runDeployCommand } from './packem_chunks/runDeployCommand.mjs';
@@ -8,12 +8,12 @@ export { runMigrateGenerateCommand } from './packem_chunks/runMigrateGenerateCom
8
8
  export { runResetCommand } from './packem_chunks/runResetCommand.mjs';
9
9
  export { runRpcCommand } from './packem_chunks/runRpcCommand.mjs';
10
10
  export { default as parseArgs } from './packem_shared/parseArgs-YXFuKdEk.mjs';
11
- export { insertSchemaExtension } from './packem_shared/insertSchemaExtension-BuzF6-t2.mjs';
12
- export { createLogger, pail } from './packem_shared/createLogger-CHPNjFw2.mjs';
13
- export { diffSnapshots, renderAddColumn, renderCreateIndex, renderCreateTable, renderDropIndex, renderDropTable, renderMigrationFile, validatorKindToSqlType } from './packem_shared/diffSnapshots-RR2ZE8Ya.mjs';
14
- export { default as schemaIrToSnapshot } from './packem_shared/schemaIrToSnapshot-aBTo7TM5.mjs';
15
- export { createRecordingSpawner, defaultSpawner } from './packem_shared/defaultSpawner-DxI3mebw.mjs';
11
+ export { insertSchemaExtension } from './packem_shared/insertSchemaExtension-DAqbfr9Z.mjs';
12
+ export { createLogger, pail } from './packem_shared/createLogger-B40gPzQo.mjs';
13
+ export { diffSnapshots, renderAddColumn, renderCreateIndex, renderCreateTable, renderDropIndex, renderDropTable, renderMigrationFile, validatorKindToSqlType } from './packem_shared/diffSnapshots-BeDvvNiF.mjs';
14
+ export { default as schemaIrToSnapshot } from './packem_shared/schemaIrToSnapshot-DdsljJT-.mjs';
15
+ export { createRecordingSpawner, defaultSpawner } from './packem_shared/createRecordingSpawner-DxI3mebw.mjs';
16
16
  export { default as parseManifest } from './packem_shared/parseManifest--vZf2FY1.mjs';
17
17
  export { REQUIRED_COMPATIBILITY_DATE, REQUIRED_FLAG, validateWranglerProject as validateWrangler, validateWranglerConfig } from '@lunora/config';
18
18
  export { buildRegistryIndex } from './packem_shared/buildRegistryIndex-BcYe607_.mjs';
19
- export { runAddCommand, runBuildIndexCommand, runRegistryViewCommand } from './packem_shared/runAddCommand-BZGkRnBs.mjs';
19
+ export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-hl0mRqqg.mjs';
@@ -1,9 +1,10 @@
1
1
  import { existsSync } from 'node:fs';
2
- import { findWranglerFile, promptSelect } from '@lunora/config';
3
- import { join } from '@visulima/path';
4
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
5
- import { n as normalizeFeature, E as EMAIL_ITEM, D as DEFAULT_AUTH_ITEM, p as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS } from '../packem_shared/features-ocSSpZtS.mjs';
6
- import { runAddCommand } from '../packem_shared/runAddCommand-BZGkRnBs.mjs';
2
+ import { findWranglerFile } from '@lunora/config';
3
+ import { join, basename } from '@visulima/path';
4
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
5
+ import { t as tuiText, a as tuiSelect } from '../packem_shared/tui-prompts-M6OWsuyw.mjs';
6
+ import { n as normalizeFeature, E as EMAIL_ITEM, s as sanitizeBucketName, d as deriveBucketName, p as promptBucketName, r as resolveTypedDestination, M as MAIL_DESTINATION_PROMPT, a as sanitizeDatabaseName, b as deriveDatabaseName, c as promptDatabaseName, D as DEFAULT_AUTH_ITEM, e as promptAuthProvider, A as AUTH_PROVIDER_OPTIONS, w as withStorageBucketName, f as withMailDestination, g as withAuthDatabaseName } from '../packem_shared/storage-B7hHSTZP.mjs';
7
+ import { r as runAddCommand } from '../packem_shared/commands-hl0mRqqg.mjs';
7
8
 
8
9
  const providerToItem = (provider) => {
9
10
  const value = provider.trim().toLowerCase();
@@ -24,9 +25,54 @@ const resolveAuthItem = async (options) => {
24
25
  if (options.yes === true) {
25
26
  return DEFAULT_AUTH_ITEM;
26
27
  }
27
- const select = options.promptSelect ?? ((message, choices, settings) => promptSelect(message, choices, settings));
28
+ const select = options.promptSelect ?? ((message, choices, settings) => tuiSelect(message, choices, settings));
28
29
  return promptAuthProvider(select);
29
30
  };
31
+ const textPrompt = (options) => options.promptText ?? ((message, settings) => tuiText(message, settings));
32
+ const resolveStorageBucketName = async (options) => {
33
+ const projectName = basename(options.cwd ?? process.cwd());
34
+ if (options.bucket !== void 0 && options.bucket !== "") {
35
+ const sanitized = sanitizeBucketName(options.bucket);
36
+ if (sanitized !== void 0) {
37
+ return sanitized;
38
+ }
39
+ const fallback = deriveBucketName(projectName);
40
+ options.logger.warn(`add: "${options.bucket}" isn't a valid R2 bucket name (lowercase alphanumeric + hyphens, 3–63 chars) — using "${fallback}".`);
41
+ return fallback;
42
+ }
43
+ if (options.yes === true) {
44
+ return deriveBucketName(projectName);
45
+ }
46
+ return promptBucketName(textPrompt(options), projectName);
47
+ };
48
+ const resolveMailDestination = async (options) => {
49
+ const warn = (message) => {
50
+ options.logger.warn(`add: ${message}`);
51
+ };
52
+ if (options.mailTo !== void 0 && options.mailTo !== "") {
53
+ return resolveTypedDestination(options.mailTo, warn);
54
+ }
55
+ if (options.yes === true) {
56
+ return void 0;
57
+ }
58
+ return resolveTypedDestination(await textPrompt(options)(MAIL_DESTINATION_PROMPT, { placeholder: "you@yourdomain.com" }), warn);
59
+ };
60
+ const resolveAuthDatabaseName = async (options) => {
61
+ const projectName = basename(options.cwd ?? process.cwd());
62
+ if (options.db !== void 0 && options.db !== "") {
63
+ const sanitized = sanitizeDatabaseName(options.db);
64
+ if (sanitized !== void 0) {
65
+ return sanitized;
66
+ }
67
+ const fallback = deriveDatabaseName(projectName);
68
+ options.logger.warn(`add: "${options.db}" isn't a usable D1 database name — using "${fallback}".`);
69
+ return fallback;
70
+ }
71
+ if (options.yes === true) {
72
+ return deriveDatabaseName(projectName);
73
+ }
74
+ return promptDatabaseName(textPrompt(options), projectName);
75
+ };
30
76
  const resolveFeatureItems = async (feature, options) => {
31
77
  if (feature.kind === "auth") {
32
78
  return [await resolveAuthItem(options)];
@@ -48,13 +94,37 @@ const runAddFeature = async (options) => {
48
94
  return { code: 1, items: [] };
49
95
  }
50
96
  const items = await resolveFeatureItems(feature, options);
97
+ const transforms = [];
98
+ if (items.includes("storage")) {
99
+ const bucketName = await resolveStorageBucketName(options);
100
+ transforms.push((manifest) => withStorageBucketName(manifest, bucketName));
101
+ }
102
+ if (items.includes("mail")) {
103
+ const destination = await resolveMailDestination(options);
104
+ if (destination !== void 0) {
105
+ transforms.push((manifest) => withMailDestination(manifest, destination));
106
+ }
107
+ }
108
+ if (items.some((name) => name === "auth" || name.startsWith("auth-"))) {
109
+ const databaseName = await resolveAuthDatabaseName(options);
110
+ transforms.push((manifest) => withAuthDatabaseName(manifest, databaseName));
111
+ }
112
+ const transformManifest = transforms.length > 0 ? (manifest) => {
113
+ let result2 = manifest;
114
+ for (const transform of transforms) {
115
+ result2 = transform(result2);
116
+ }
117
+ return result2;
118
+ } : void 0;
51
119
  const result = await runAddCommand({
52
120
  allowUnsafeSource: options.allowUnsafeSource,
53
121
  cwd,
54
122
  from: options.from,
55
123
  logger: options.logger,
56
124
  names: [...items],
125
+ ref: options.ref,
57
126
  source: options.source,
127
+ transformManifest,
58
128
  yes: true
59
129
  });
60
130
  return { code: result.code, items };
@@ -62,11 +132,15 @@ const runAddFeature = async (options) => {
62
132
  const execute = defineHandler(async ({ argument, cwd, logger, options }) => {
63
133
  const result = await runAddFeature({
64
134
  allowUnsafeSource: options.allowUnsafeSource === true,
135
+ bucket: options.bucket,
65
136
  cwd,
137
+ db: options.db,
66
138
  feature: argument[0],
67
139
  from: options.from,
68
140
  logger,
141
+ mailTo: options.mailTo,
69
142
  provider: options.provider,
143
+ ref: options.ref,
70
144
  source: options.source,
71
145
  yes: options.yes === true
72
146
  });
@@ -1,4 +1,4 @@
1
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
1
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
2
2
  import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
3
3
  import { runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
4
4
 
@@ -4,7 +4,7 @@ import { discoverSchema } from '@lunora/codegen';
4
4
  import { readLinkedProject } from '@lunora/config';
5
5
  import { parse } from 'jsonc-parser';
6
6
  import { Project } from 'ts-morph';
7
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
7
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
8
8
 
9
9
  const findWranglerFile = (projectRoot) => {
10
10
  for (const candidate of ["wrangler.jsonc", "wrangler.json"]) {
@@ -1,5 +1,5 @@
1
1
  import { r as resolveAdminBaseUrl } from '../packem_shared/admin-url-4UzT-CI4.mjs';
2
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
2
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
3
3
  import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
4
4
 
5
5
  const GET_FUNCTION_STATS_OP = "__lunora_admin__:getFunctionStats";
@@ -1,7 +1,7 @@
1
1
  import { existsSync, rmSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { writeLinkedProject, LINKED_PROJECT_FILE } from '@lunora/config';
4
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
4
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
5
5
  import { r as readWranglerName } from '../packem_shared/wrangler-name-cy4yhm9j.mjs';
6
6
 
7
7
  const isValidWorkerUrl = (value) => {
@@ -1,6 +1,6 @@
1
1
  import { readLinkedProject } from '@lunora/config';
2
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
3
- import { defaultSpawner } from '../packem_shared/defaultSpawner-DxI3mebw.mjs';
2
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
3
+ import { defaultSpawner } from '../packem_shared/createRecordingSpawner-DxI3mebw.mjs';
4
4
 
5
5
  const LOG_FORMATS = /* @__PURE__ */ new Set(["json", "pretty"]);
6
6
  const runLogsCommand = async (options) => {
@@ -1,7 +1,7 @@
1
1
  import { runCodegen } from '@lunora/codegen';
2
2
  import { validateWranglerProject, inferLunoraBindings, reconcileWranglerBindings } from '@lunora/config';
3
3
  import { p as parseApiSpec } from '../packem_shared/api-spec-CtA6ilu4.mjs';
4
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
4
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
5
5
  import { r as runSchemaDriftGate } from '../packem_shared/schema-drift-gate-BtBt0as0.mjs';
6
6
 
7
7
  const provisionBindings = async (cwd, logger) => {
@@ -1,5 +1,5 @@
1
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
2
- import { runAddCommand, runRegistryViewCommand, runBuildIndexCommand } from '../packem_shared/runAddCommand-BZGkRnBs.mjs';
1
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
2
+ import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-hl0mRqqg.mjs';
3
3
 
4
4
  const execute = defineHandler(({ argument, cwd, logger, options }) => {
5
5
  const subcommand = argument[0];
@@ -15,12 +15,13 @@ const execute = defineHandler(({ argument, cwd, logger, options }) => {
15
15
  logger,
16
16
  names,
17
17
  overwrite: options.overwrite === true,
18
+ ref: options.ref,
18
19
  source: options.source,
19
20
  yes: options.yes === true
20
21
  });
21
22
  }
22
23
  if (subcommand === "list") {
23
- return runAddCommand({ cwd, from: options.from, json: options.json === true, list: true, logger, names: [], source: options.source });
24
+ return runAddCommand({ cwd, from: options.from, json: options.json === true, list: true, logger, names: [], ref: options.ref, source: options.source });
24
25
  }
25
26
  if (subcommand === "view") {
26
27
  return runRegistryViewCommand({
@@ -28,6 +29,7 @@ const execute = defineHandler(({ argument, cwd, logger, options }) => {
28
29
  from: options.from,
29
30
  logger,
30
31
  names,
32
+ ref: options.ref,
31
33
  source: options.source
32
34
  });
33
35
  }
@@ -2,7 +2,7 @@ import { existsSync, readFileSync, readdirSync, statSync, mkdirSync, writeFileSy
2
2
  import { fileURLToPath } from 'node:url';
3
3
  import { AGENT_RULES_DIR, detectAgentRules, LUNORA_SKILL_NAMES } from '@lunora/config';
4
4
  import { join, relative, dirname } from '@visulima/path';
5
- import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
5
+ import { d as defineHandler } from '../packem_shared/command-BC30oSBW.mjs';
6
6
 
7
7
  const resolveBundledSkillsDirectory = (startDirectory = dirname(fileURLToPath(import.meta.url))) => {
8
8
  let directory = startDirectory;