@lunora/cli 1.0.0-alpha.4 → 1.0.0-alpha.5
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/bin.mjs +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +2 -2
- package/dist/packem_chunks/handler.mjs +4 -3
- package/dist/packem_chunks/handler16.mjs +1 -1
- package/dist/packem_chunks/handler18.mjs +3 -5
- package/dist/packem_chunks/planDevCommand.mjs +3 -2
- package/dist/packem_chunks/runInitCommand.mjs +37 -19
- package/dist/packem_chunks/runResetCommand.mjs +3 -3
- package/dist/packem_shared/{COMMANDS-CHw4zOZ9.mjs → COMMANDS-DXaq12xm.mjs} +2 -2
- package/dist/packem_shared/{commands-SUPdjsu5.mjs → commands-B9nASOYd.mjs} +4 -3
- package/dist/packem_shared/{runAddCommand-Txwh1Xw1.mjs → runAddCommand-BF7XreDW.mjs} +1 -1
- package/dist/packem_shared/tui-prompts-Bm15GPJA.mjs +229 -0
- package/package.json +4 -1
package/dist/bin.mjs
CHANGED
package/dist/index.d.mts
CHANGED
|
@@ -532,7 +532,7 @@ interface OfferDeps {
|
|
|
532
532
|
* maps to the `mail` item. Picked items are applied in selection order.
|
|
533
533
|
* Non-interactive: prints how to add them later and changes nothing.
|
|
534
534
|
*/
|
|
535
|
-
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite";
|
|
535
|
+
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite-react";
|
|
536
536
|
interface InitCommandOptions {
|
|
537
537
|
/**
|
|
538
538
|
* When true, accept `--source` values that don't start with `gh:` /
|
|
@@ -605,7 +605,7 @@ interface InitCommandResult {
|
|
|
605
605
|
* scaffold's exit code.
|
|
606
606
|
*/
|
|
607
607
|
declare const runInitCommand: (options: InitCommandOptions) => Promise<InitCommandResult>;
|
|
608
|
-
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite). */
|
|
608
|
+
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite-react). */
|
|
609
609
|
interface MigrateGenerateCommandOptions {
|
|
610
610
|
cwd?: string;
|
|
611
611
|
logger: Logger;
|
package/dist/index.d.ts
CHANGED
|
@@ -532,7 +532,7 @@ interface OfferDeps {
|
|
|
532
532
|
* maps to the `mail` item. Picked items are applied in selection order.
|
|
533
533
|
* Non-interactive: prints how to add them later and changes nothing.
|
|
534
534
|
*/
|
|
535
|
-
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite";
|
|
535
|
+
type Template = "astro" | "next" | "nuxt" | "standalone" | "sveltekit" | "tanstack-start-react" | "tanstack-start-solid" | "vite-react";
|
|
536
536
|
interface InitCommandOptions {
|
|
537
537
|
/**
|
|
538
538
|
* When true, accept `--source` values that don't start with `gh:` /
|
|
@@ -605,7 +605,7 @@ interface InitCommandResult {
|
|
|
605
605
|
* scaffold's exit code.
|
|
606
606
|
*/
|
|
607
607
|
declare const runInitCommand: (options: InitCommandOptions) => Promise<InitCommandResult>;
|
|
608
|
-
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite). */
|
|
608
|
+
/** Narrow a raw `--template` value to a known {@link Template} (defaults to vite-react). */
|
|
609
609
|
interface MigrateGenerateCommandOptions {
|
|
610
610
|
cwd?: string;
|
|
611
611
|
logger: Logger;
|
package/dist/index.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-
|
|
1
|
+
export { COMMANDS, VERSION, runCli } from './packem_shared/COMMANDS-DXaq12xm.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';
|
|
@@ -16,4 +16,4 @@ export { createRecordingSpawner, defaultSpawner } from './packem_shared/defaultS
|
|
|
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 { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-
|
|
19
|
+
export { r as runAddCommand, a as runBuildIndexCommand, b as runRegistryViewCommand } from './packem_shared/commands-B9nASOYd.mjs';
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { findWranglerFile
|
|
2
|
+
import { findWranglerFile } from '@lunora/config';
|
|
3
3
|
import { join } from '@visulima/path';
|
|
4
4
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
5
|
+
import { t as tuiSelect } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
5
6
|
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 { r as runAddCommand } from '../packem_shared/commands-
|
|
7
|
+
import { r as runAddCommand } from '../packem_shared/commands-B9nASOYd.mjs';
|
|
7
8
|
|
|
8
9
|
const providerToItem = (provider) => {
|
|
9
10
|
const value = provider.trim().toLowerCase();
|
|
@@ -24,7 +25,7 @@ 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) =>
|
|
28
|
+
const select = options.promptSelect ?? ((message, choices, settings) => tuiSelect(message, choices, settings));
|
|
28
29
|
return promptAuthProvider(select);
|
|
29
30
|
};
|
|
30
31
|
const resolveFeatureItems = async (feature, options) => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
2
|
-
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-
|
|
2
|
+
import { r as runAddCommand, b as runRegistryViewCommand, a as runBuildIndexCommand } from '../packem_shared/commands-B9nASOYd.mjs';
|
|
3
3
|
|
|
4
4
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
5
5
|
const subcommand = argument[0];
|
|
@@ -2,12 +2,12 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { discoverSchema, schemaFromIr } from '@lunora/codegen';
|
|
5
|
-
import { promptYesNo } from '@lunora/config';
|
|
6
5
|
import { seedPlan } from '@lunora/seed';
|
|
7
6
|
import { join } from '@visulima/path';
|
|
8
7
|
import { Project } from 'ts-morph';
|
|
9
8
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
10
9
|
import { a as resolveProductionWorkerUrl } from '../packem_shared/resolve-target-qbsJ_5sF.mjs';
|
|
10
|
+
import { a as tuiConfirm } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
11
11
|
import { runImportCommand } from '../packem_shared/DEFAULT_IMPORT_BATCH_SIZE-Ck-2bU08.mjs';
|
|
12
12
|
import { runResetCommand } from './runResetCommand.mjs';
|
|
13
13
|
|
|
@@ -88,10 +88,8 @@ const confirmRemoteSeedTarget = async (options, generated) => {
|
|
|
88
88
|
options.logger.error("seed: refusing to insert into a non-local target without confirmation — re-run with --yes");
|
|
89
89
|
return seedFailure(1);
|
|
90
90
|
}
|
|
91
|
-
const confirmer = options.confirm ??
|
|
92
|
-
const confirmed = await confirmer(
|
|
93
|
-
`This will insert ${String(generated)} generated row(s) into ${options.url ?? "the production worker"}. Continue? [y/N] `
|
|
94
|
-
);
|
|
91
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
92
|
+
const confirmed = await confirmer(`This will insert ${String(generated)} generated row(s) into ${options.url ?? "the production worker"}. Continue?`);
|
|
95
93
|
if (!confirmed) {
|
|
96
94
|
options.logger.info("seed: aborted");
|
|
97
95
|
return seedFailure(1);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import { detectAgentRules, inferLunoraBindings, packageNamesFromBindings, ensureDevVarsExample, ensureDevVariables,
|
|
2
|
+
import { detectAgentRules, inferLunoraBindings, packageNamesFromBindings, ensureDevVarsExample, ensureDevVariables, isInteractive, DEV_VARS_FILE, DEV_VARS_EXAMPLE_FILE, claimAgentRulesHint, AGENT_RULES_HINT, materializeRemoteWranglerConfig, formatLunoraEvent, resolveRemoteEnabled, readProjectRemotePreference } from '@lunora/config';
|
|
3
3
|
import { p as parseApiSpec } from '../packem_shared/api-spec-CtA6ilu4.mjs';
|
|
4
4
|
import { existsSync, watch, readFileSync } from 'node:fs';
|
|
5
5
|
import { join } from 'node:path';
|
|
@@ -9,6 +9,7 @@ import { dirname, join as join$1 } from '@visulima/path';
|
|
|
9
9
|
import { createServer, request } from 'node:http';
|
|
10
10
|
import { connect } from 'node:net';
|
|
11
11
|
import { loadStudioAssets, studioAssetsStamp, renderStudioHtml, resolveAdminToken, SCHEMA_EDIT_ENDPOINT, POLICY_SCAFFOLD_ENDPOINT, SEED_ENDPOINT, serveJsonHandler, handleSchemaEditRequest, handlePolicyScaffoldRequest, handleSeedRequest } from '@lunora/config/studio-host';
|
|
12
|
+
import { c as createTuiConfirm } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
12
13
|
|
|
13
14
|
const DEFAULT_DEBOUNCE_MS = 100;
|
|
14
15
|
const PATH_SEGMENT_SEPARATOR = /[/\\]/u;
|
|
@@ -441,7 +442,7 @@ const offerDevVariablesScaffold = async (options, cwd) => {
|
|
|
441
442
|
} catch {
|
|
442
443
|
}
|
|
443
444
|
const result = await (options.ensureEnv ?? ensureDevVariables)({
|
|
444
|
-
confirm:
|
|
445
|
+
confirm: createTuiConfirm(),
|
|
445
446
|
cwd,
|
|
446
447
|
info: (message) => {
|
|
447
448
|
options.logger.info(message);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, mkdirSync, writeFileSync, readdirSync, mkdtempSync, rmSync, readFileSync } from 'node:fs';
|
|
2
2
|
import { tmpdir } from 'node:os';
|
|
3
|
-
import { detectFramework as detectFramework$1, isInteractive
|
|
3
|
+
import { detectFramework as detectFramework$1, isInteractive } from '@lunora/config';
|
|
4
4
|
import { walkSync } from '@visulima/fs';
|
|
5
5
|
import { resolve, join as join$1, relative, dirname as dirname$1, basename } from '@visulima/path';
|
|
6
6
|
import { downloadTemplate } from 'giget';
|
|
@@ -9,7 +9,8 @@ import { join, dirname } from 'node:path';
|
|
|
9
9
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
10
10
|
import MagicString from 'magic-string';
|
|
11
11
|
import { Project, SyntaxKind } from 'ts-morph';
|
|
12
|
-
import { c as resolveSourceRef, d as resolveDistTag, r as runAddCommand } from '../packem_shared/commands-
|
|
12
|
+
import { c as resolveSourceRef, d as resolveDistTag, r as runAddCommand } from '../packem_shared/commands-B9nASOYd.mjs';
|
|
13
|
+
import { b as tuiIntro, d as tuiOutro, t as tuiSelect, e as tuiMultiSelect, w as withTuiSpinner } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
13
14
|
import { p as promptAuthProvider, E as EMAIL_ITEM } from '../packem_shared/features-ocSSpZtS.mjs';
|
|
14
15
|
|
|
15
16
|
const GITHUB_CONTENT = `name: Deploy
|
|
@@ -570,31 +571,48 @@ const offerIsInteractive = (options) => options.yes !== true && (options.prompt
|
|
|
570
571
|
const maybeOfferExtras = async (options, projectDirectory) => {
|
|
571
572
|
const interactive = offerIsInteractive(options);
|
|
572
573
|
const apply = async (names) => {
|
|
573
|
-
const
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
574
|
+
const applyLogger = isInteractive() ? {
|
|
575
|
+
error: (message) => {
|
|
576
|
+
options.logger.error(message);
|
|
577
|
+
},
|
|
578
|
+
info: () => {
|
|
579
|
+
},
|
|
580
|
+
success: () => {
|
|
581
|
+
},
|
|
582
|
+
warn: (message) => {
|
|
583
|
+
options.logger.warn(message);
|
|
584
|
+
}
|
|
585
|
+
} : options.logger;
|
|
586
|
+
const result = await withTuiSpinner(
|
|
587
|
+
`adding ${names.join(", ")}…`,
|
|
588
|
+
() => runAddCommand({
|
|
589
|
+
allowUnsafeSource: options.allowUnsafeSource,
|
|
590
|
+
cwd: projectDirectory,
|
|
591
|
+
from: options.registryFrom,
|
|
592
|
+
logger: applyLogger,
|
|
593
|
+
names: [...names],
|
|
594
|
+
ref: options.ref,
|
|
595
|
+
source: options.registrySource,
|
|
596
|
+
yes: true
|
|
597
|
+
})
|
|
598
|
+
);
|
|
583
599
|
return result.code === 0;
|
|
584
600
|
};
|
|
601
|
+
await tuiIntro("let's finish setting up your app");
|
|
585
602
|
await offerRegistryExtras({
|
|
586
603
|
apply,
|
|
587
604
|
interactive,
|
|
588
605
|
logger: options.logger,
|
|
589
|
-
multiSelect: options.prompt?.multiSelect ?? ((message, choices, settings) =>
|
|
590
|
-
select: options.prompt?.select ?? ((message, choices, settings) =>
|
|
606
|
+
multiSelect: options.prompt?.multiSelect ?? ((message, choices, settings) => tuiMultiSelect(message, choices, settings)),
|
|
607
|
+
select: options.prompt?.select ?? ((message, choices, settings) => tuiSelect(message, choices, settings))
|
|
591
608
|
});
|
|
609
|
+
await tuiOutro("you're all set — run `pnpm dev` to start.");
|
|
592
610
|
};
|
|
593
611
|
const scaffoldNewProject = async (options, cwd) => {
|
|
594
612
|
const name = options.name ?? "lunora-app";
|
|
595
|
-
const templateType = options.templateType ?? "vite";
|
|
613
|
+
const templateType = options.templateType ?? "vite-react";
|
|
596
614
|
if (templateType === "next") {
|
|
597
|
-
options.logger.warn('template "next" is not yet available — re-run with `-t vite` or `-t standalone`.');
|
|
615
|
+
options.logger.warn('template "next" is not yet available — re-run with `-t vite-react` or `-t standalone`.');
|
|
598
616
|
return { code: 1, files: [], target: "" };
|
|
599
617
|
}
|
|
600
618
|
if (name.includes("/") || name.includes("\\") || name === ".." || name === ".") {
|
|
@@ -631,7 +649,7 @@ const runInitCommand = async (options) => {
|
|
|
631
649
|
}
|
|
632
650
|
return result;
|
|
633
651
|
};
|
|
634
|
-
const isTemplate = (value) => value === "astro" || value === "next" || value === "nuxt" || value === "standalone" || value === "sveltekit" || value === "tanstack-start-react" || value === "tanstack-start-solid" || value === "vite";
|
|
652
|
+
const isTemplate = (value) => value === "astro" || value === "next" || value === "nuxt" || value === "standalone" || value === "sveltekit" || value === "tanstack-start-react" || value === "tanstack-start-solid" || value === "vite-react";
|
|
635
653
|
const resolveCiProvider = (raw, logger) => {
|
|
636
654
|
if (raw === void 0) {
|
|
637
655
|
return void 0;
|
|
@@ -643,8 +661,8 @@ const resolveCiProvider = (raw, logger) => {
|
|
|
643
661
|
return void 0;
|
|
644
662
|
};
|
|
645
663
|
const execute = defineHandler(({ argument, cwd, logger, options }) => {
|
|
646
|
-
const templateRaw = options.template ?? "vite";
|
|
647
|
-
const template = isTemplate(templateRaw) ? templateRaw : "vite";
|
|
664
|
+
const templateRaw = options.template ?? "vite-react";
|
|
665
|
+
const template = isTemplate(templateRaw) ? templateRaw : "vite-react";
|
|
648
666
|
return runInitCommand({
|
|
649
667
|
allowUnsafeSource: options.allowUnsafeSource === true,
|
|
650
668
|
cwd,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { existsSync, rmSync } from 'node:fs';
|
|
2
|
-
import { promptYesNo } from '@lunora/config';
|
|
3
2
|
import { join } from '@visulima/path';
|
|
4
3
|
import { d as defineHandler } from '../packem_shared/command-BDXcJCCJ.mjs';
|
|
4
|
+
import { a as tuiConfirm } from '../packem_shared/tui-prompts-Bm15GPJA.mjs';
|
|
5
5
|
|
|
6
6
|
const runResetCommand = async (options) => {
|
|
7
7
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -15,8 +15,8 @@ const runResetCommand = async (options) => {
|
|
|
15
15
|
options.logger.error("reset: stdin is not a TTY — re-run with --yes to confirm deleting .wrangler/state");
|
|
16
16
|
return { code: 1, removed: [] };
|
|
17
17
|
}
|
|
18
|
-
const confirmer = options.confirm ??
|
|
19
|
-
const confirmed = await confirmer("This will delete .wrangler/state. Continue?
|
|
18
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
19
|
+
const confirmed = await confirmer("This will delete .wrangler/state. Continue?");
|
|
20
20
|
if (!confirmed) {
|
|
21
21
|
options.logger.info("reset: aborted");
|
|
22
22
|
return { code: 1, removed: [] };
|
|
@@ -361,8 +361,8 @@ const initCommand = {
|
|
|
361
361
|
options: [
|
|
362
362
|
{
|
|
363
363
|
alias: "t",
|
|
364
|
-
defaultValue: "vite",
|
|
365
|
-
description: "Template to scaffold (vite | standalone | astro | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid)",
|
|
364
|
+
defaultValue: "vite-react",
|
|
365
|
+
description: "Template to scaffold (vite-react | standalone | astro | nuxt | sveltekit | tanstack-start-react | tanstack-start-solid)",
|
|
366
366
|
name: "template",
|
|
367
367
|
type: String
|
|
368
368
|
},
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, mkdtempSync, rmSync } from 'node:fs';
|
|
2
2
|
import { dirname, join } from '@visulima/path';
|
|
3
|
-
import { DEV_VARS_FILE, parseDevVariableEntries
|
|
3
|
+
import { DEV_VARS_FILE, parseDevVariableEntries } from '@lunora/config';
|
|
4
4
|
import { modify, applyEdits, parse } from 'jsonc-parser';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { a as tuiConfirm } from './tui-prompts-Bm15GPJA.mjs';
|
|
6
7
|
import { collectCatalog, buildRegistryIndex } from './buildRegistryIndex-BcYe607_.mjs';
|
|
7
8
|
import { insertSchemaExtension } from './insertSchemaExtension-BuzF6-t2.mjs';
|
|
8
9
|
import { createHash } from 'node:crypto';
|
|
@@ -272,8 +273,8 @@ const confirmDepMutation = async (items, options) => {
|
|
|
272
273
|
options.logger.error(`add: stdin is not a TTY and the requested items ${reasonText} — re-run with --yes to confirm`);
|
|
273
274
|
return false;
|
|
274
275
|
}
|
|
275
|
-
const confirmer = options.confirm ??
|
|
276
|
-
const confirmed = await confirmer(`The requested items ${reasonText}. Continue
|
|
276
|
+
const confirmer = options.confirm ?? tuiConfirm;
|
|
277
|
+
const confirmed = await confirmer(`The requested items ${reasonText}. Continue?`);
|
|
277
278
|
if (!confirmed) {
|
|
278
279
|
options.logger.info("add: aborted");
|
|
279
280
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
import 'node:fs';
|
|
2
2
|
import '@visulima/path';
|
|
3
|
-
export { r as runAddCommand, a as runBuildIndexCommand, e as runListCommand, b as runRegistryViewCommand } from './commands-
|
|
3
|
+
export { r as runAddCommand, a as runBuildIndexCommand, e as runListCommand, b as runRegistryViewCommand } from './commands-B9nASOYd.mjs';
|
|
4
4
|
import './buildRegistryIndex-BcYe607_.mjs';
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
2
|
+
import { isInteractive } from '@lunora/config';
|
|
3
|
+
import { render } from '@visulima/tui';
|
|
4
|
+
import { Box } from '@visulima/tui/components/box';
|
|
5
|
+
import { CommandPalette } from '@visulima/tui/components/command-palette';
|
|
6
|
+
import { ConfirmInput } from '@visulima/tui/components/confirm-input';
|
|
7
|
+
import { MultiSelect } from '@visulima/tui/components/multi-select';
|
|
8
|
+
import { SelectInput } from '@visulima/tui/components/select-input';
|
|
9
|
+
import { Spinner } from '@visulima/tui/components/spinner';
|
|
10
|
+
import { Text } from '@visulima/tui/components/text';
|
|
11
|
+
import { useApp } from '@visulima/tui/hooks/use-app';
|
|
12
|
+
import { useInput } from '@visulima/tui/hooks/use-input';
|
|
13
|
+
import { useEffect } from 'react';
|
|
14
|
+
|
|
15
|
+
const ACCENT = "#a855f7";
|
|
16
|
+
const SCROLL_LIMIT = 10;
|
|
17
|
+
const FILTER_THRESHOLD = 8;
|
|
18
|
+
const PromptFrame = ({ children }) => jsx(Box, { borderColor: ACCENT, borderStyle: "round", flexDirection: "column", paddingX: 1, children });
|
|
19
|
+
const runInkPrompt = async (build, fallback) => {
|
|
20
|
+
let result = fallback;
|
|
21
|
+
const instance = render(
|
|
22
|
+
build((value) => {
|
|
23
|
+
result = value;
|
|
24
|
+
}),
|
|
25
|
+
{ exitOnCtrlC: true }
|
|
26
|
+
);
|
|
27
|
+
try {
|
|
28
|
+
await instance.waitUntilExit();
|
|
29
|
+
} catch {
|
|
30
|
+
} finally {
|
|
31
|
+
instance.unmount();
|
|
32
|
+
}
|
|
33
|
+
return result;
|
|
34
|
+
};
|
|
35
|
+
const itemLabel = (option) => option.description === void 0 ? option.label : `${option.label} — ${option.description}`;
|
|
36
|
+
const useCommit = (finish) => {
|
|
37
|
+
const { exit } = useApp();
|
|
38
|
+
return (result) => {
|
|
39
|
+
finish(result);
|
|
40
|
+
exit();
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
const useEscapeToExit = () => {
|
|
44
|
+
const { exit } = useApp();
|
|
45
|
+
useInput((_input, key) => {
|
|
46
|
+
if (key.escape) {
|
|
47
|
+
exit();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const SelectView = ({ finish, initialIndex, message, options }) => {
|
|
52
|
+
const commit = useCommit(finish);
|
|
53
|
+
useEscapeToExit();
|
|
54
|
+
return jsxs(PromptFrame, { children: [
|
|
55
|
+
jsx(Text, { bold: true, children: message }),
|
|
56
|
+
jsx(
|
|
57
|
+
SelectInput,
|
|
58
|
+
{
|
|
59
|
+
accentColor: ACCENT,
|
|
60
|
+
initialIndex,
|
|
61
|
+
items: options.map((option) => {
|
|
62
|
+
return { key: option.value, label: itemLabel(option), value: option.value };
|
|
63
|
+
}),
|
|
64
|
+
limit: SCROLL_LIMIT,
|
|
65
|
+
onSelect: (item) => {
|
|
66
|
+
commit(item.value);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
] });
|
|
71
|
+
};
|
|
72
|
+
const PaletteView = ({ finish, message, options }) => {
|
|
73
|
+
const { exit } = useApp();
|
|
74
|
+
const commit = useCommit(finish);
|
|
75
|
+
return jsxs(Box, { flexDirection: "column", children: [
|
|
76
|
+
jsx(Text, { bold: true, children: message }),
|
|
77
|
+
jsx(
|
|
78
|
+
CommandPalette,
|
|
79
|
+
{
|
|
80
|
+
accentColor: ACCENT,
|
|
81
|
+
autoFocus: true,
|
|
82
|
+
commands: options.map((option) => {
|
|
83
|
+
return { description: option.description, id: option.value, label: option.label };
|
|
84
|
+
}),
|
|
85
|
+
limit: SCROLL_LIMIT,
|
|
86
|
+
onCancel: () => {
|
|
87
|
+
exit();
|
|
88
|
+
},
|
|
89
|
+
onSelect: (id) => {
|
|
90
|
+
commit(id);
|
|
91
|
+
},
|
|
92
|
+
placeholder: "Type to filter…"
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
] });
|
|
96
|
+
};
|
|
97
|
+
const tuiSelect = async (message, options, settings) => {
|
|
98
|
+
if (!isInteractive() || options.length === 0) {
|
|
99
|
+
return settings?.default;
|
|
100
|
+
}
|
|
101
|
+
if (options.length > FILTER_THRESHOLD) {
|
|
102
|
+
return runInkPrompt((finish) => jsx(PaletteView, { finish, message, options }), settings?.default);
|
|
103
|
+
}
|
|
104
|
+
const defaultIndex = settings?.default === void 0 ? -1 : options.findIndex((option) => option.value === settings.default);
|
|
105
|
+
return runInkPrompt(
|
|
106
|
+
(finish) => jsx(SelectView, { finish, initialIndex: defaultIndex >= 0 ? defaultIndex : void 0, message, options }),
|
|
107
|
+
settings?.default
|
|
108
|
+
);
|
|
109
|
+
};
|
|
110
|
+
const MultiSelectView = ({ defaults, finish, message, options }) => {
|
|
111
|
+
const commit = useCommit(finish);
|
|
112
|
+
useEscapeToExit();
|
|
113
|
+
return jsxs(PromptFrame, { children: [
|
|
114
|
+
jsx(Text, { bold: true, children: message }),
|
|
115
|
+
jsx(Text, { dimColor: true, children: "space toggles · enter confirms · esc cancels" }),
|
|
116
|
+
jsx(
|
|
117
|
+
MultiSelect,
|
|
118
|
+
{
|
|
119
|
+
accentColor: ACCENT,
|
|
120
|
+
defaultValue: [...defaults],
|
|
121
|
+
limit: SCROLL_LIMIT,
|
|
122
|
+
onSubmit: (values) => {
|
|
123
|
+
const chosen = new Set(values);
|
|
124
|
+
commit(options.filter((option) => chosen.has(option.value)).map((option) => option.value));
|
|
125
|
+
},
|
|
126
|
+
options: options.map((option) => {
|
|
127
|
+
return { key: option.value, label: itemLabel(option), value: option.value };
|
|
128
|
+
})
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
] });
|
|
132
|
+
};
|
|
133
|
+
const tuiMultiSelect = async (message, options, settings) => {
|
|
134
|
+
const defaults = settings?.defaults ?? [];
|
|
135
|
+
if (!isInteractive() || options.length === 0) {
|
|
136
|
+
return [...defaults];
|
|
137
|
+
}
|
|
138
|
+
return runInkPrompt((finish) => jsx(MultiSelectView, { defaults, finish, message, options }), [...defaults]);
|
|
139
|
+
};
|
|
140
|
+
const ConfirmView = ({ defaultYes, finish, message }) => {
|
|
141
|
+
const commit = useCommit(finish);
|
|
142
|
+
return jsx(PromptFrame, { children: jsxs(Box, { children: [
|
|
143
|
+
jsxs(Text, { bold: true, children: [
|
|
144
|
+
message,
|
|
145
|
+
" "
|
|
146
|
+
] }),
|
|
147
|
+
jsx(
|
|
148
|
+
ConfirmInput,
|
|
149
|
+
{
|
|
150
|
+
defaultChoice: defaultYes ? "confirm" : "cancel",
|
|
151
|
+
onCancel: () => {
|
|
152
|
+
commit(false);
|
|
153
|
+
},
|
|
154
|
+
onConfirm: () => {
|
|
155
|
+
commit(true);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
)
|
|
159
|
+
] }) });
|
|
160
|
+
};
|
|
161
|
+
const tuiConfirm = async (message, options) => {
|
|
162
|
+
const defaultYes = options?.defaultYes === true;
|
|
163
|
+
if (!isInteractive()) {
|
|
164
|
+
return defaultYes;
|
|
165
|
+
}
|
|
166
|
+
return runInkPrompt((finish) => jsx(ConfirmView, { defaultYes, finish, message }), false);
|
|
167
|
+
};
|
|
168
|
+
const createTuiConfirm = () => isInteractive() ? (message) => tuiConfirm(message, { defaultYes: true }) : () => Promise.resolve(false);
|
|
169
|
+
const SelfExit = ({ children }) => {
|
|
170
|
+
const { exit } = useApp();
|
|
171
|
+
useEffect(() => {
|
|
172
|
+
exit();
|
|
173
|
+
}, [exit]);
|
|
174
|
+
return children;
|
|
175
|
+
};
|
|
176
|
+
const printFrame = async (element) => {
|
|
177
|
+
const instance = render(jsx(SelfExit, { children: element }));
|
|
178
|
+
try {
|
|
179
|
+
await instance.waitUntilExit();
|
|
180
|
+
} catch {
|
|
181
|
+
} finally {
|
|
182
|
+
instance.unmount();
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const tuiIntro = async (message) => {
|
|
186
|
+
if (!isInteractive()) {
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
await printFrame(
|
|
190
|
+
jsxs(Box, { borderColor: ACCENT, borderStyle: "round", paddingX: 1, children: [
|
|
191
|
+
jsxs(Text, { bold: true, color: ACCENT, children: [
|
|
192
|
+
"◆ Lunora",
|
|
193
|
+
" "
|
|
194
|
+
] }),
|
|
195
|
+
jsx(Text, { children: message })
|
|
196
|
+
] })
|
|
197
|
+
);
|
|
198
|
+
};
|
|
199
|
+
const tuiOutro = async (message) => {
|
|
200
|
+
if (!isInteractive()) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
await printFrame(
|
|
204
|
+
jsxs(Box, { paddingX: 1, children: [
|
|
205
|
+
jsx(Text, { color: "green", children: "✔ " }),
|
|
206
|
+
jsx(Text, { children: message })
|
|
207
|
+
] })
|
|
208
|
+
);
|
|
209
|
+
};
|
|
210
|
+
const SpinnerView = ({ label }) => jsxs(Box, { children: [
|
|
211
|
+
jsx(Text, { color: ACCENT, children: jsx(Spinner, { type: "dots" }) }),
|
|
212
|
+
jsxs(Text, { children: [
|
|
213
|
+
" ",
|
|
214
|
+
label
|
|
215
|
+
] })
|
|
216
|
+
] });
|
|
217
|
+
const withTuiSpinner = async (label, task) => {
|
|
218
|
+
if (!isInteractive()) {
|
|
219
|
+
return task();
|
|
220
|
+
}
|
|
221
|
+
const instance = render(jsx(SpinnerView, { label }));
|
|
222
|
+
try {
|
|
223
|
+
return await task();
|
|
224
|
+
} finally {
|
|
225
|
+
instance.unmount();
|
|
226
|
+
}
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
export { tuiConfirm as a, tuiIntro as b, createTuiConfirm as c, tuiOutro as d, tuiMultiSelect as e, tuiSelect as t, withTuiSpinner as w };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/cli",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.5",
|
|
4
4
|
"description": "The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-skills",
|
|
@@ -65,9 +65,12 @@
|
|
|
65
65
|
"@visulima/pail": "4.0.0-alpha.22",
|
|
66
66
|
"@visulima/path": "3.0.0-alpha.13",
|
|
67
67
|
"@visulima/spinner": "1.0.0-alpha.4",
|
|
68
|
+
"@visulima/tui": "1.0.0-alpha.26",
|
|
68
69
|
"giget": "3.3.0",
|
|
69
70
|
"jsonc-parser": "^3.3.1",
|
|
70
71
|
"magic-string": "^0.30.21",
|
|
72
|
+
"react": "^19.2.7",
|
|
73
|
+
"react-reconciler": "^0.33.0",
|
|
71
74
|
"ts-morph": "^28.0.0"
|
|
72
75
|
},
|
|
73
76
|
"engines": {
|