@bluealba/platform-cli 0.3.1-feature-platform-cli-prefix-221 → 0.3.1-feature-platform-cli-224

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 (2) hide show
  1. package/dist/index.js +262 -66
  2. package/package.json +3 -2
package/dist/index.js CHANGED
@@ -5,8 +5,8 @@ import { render } from "ink";
5
5
 
6
6
  // src/app.tsx
7
7
  import { createRequire } from "module";
8
- import { useState as useState3, useCallback as useCallback2 } from "react";
9
- import { Box as Box4, Text as Text7, useApp, useInput } from "ink";
8
+ import { useState as useState3, useCallback as useCallback2, useEffect as useEffect2 } from "react";
9
+ import { Box as Box6, Text as Text9, useApp, useInput } from "ink";
10
10
 
11
11
  // src/components/prompt.tsx
12
12
  import { Box, Text } from "ink";
@@ -143,6 +143,31 @@ function Spinner({ label }) {
143
143
  ] });
144
144
  }
145
145
 
146
+ // src/components/select-list.tsx
147
+ import React4 from "react";
148
+ import { Box as Box4, Text as Text7 } from "ink";
149
+ import { jsx as jsx7 } from "react/jsx-runtime";
150
+ var SelectList = React4.memo(function SelectList2({ options, selectedIndex }) {
151
+ if (options.length === 0) {
152
+ return /* @__PURE__ */ jsx7(Box4, { paddingLeft: 2, children: /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "No options available" }) });
153
+ }
154
+ return /* @__PURE__ */ jsx7(Box4, { flexDirection: "column", paddingLeft: 2, children: options.map((opt, i) => {
155
+ const isSelected = i === selectedIndex;
156
+ return /* @__PURE__ */ jsx7(Box4, { children: /* @__PURE__ */ jsx7(Text7, { color: "cyan", bold: isSelected, inverse: isSelected, children: opt.label }) }, opt.value);
157
+ }) });
158
+ });
159
+
160
+ // src/components/confirm-prompt.tsx
161
+ import { Box as Box5, Text as Text8 } from "ink";
162
+ import { jsx as jsx8, jsxs as jsxs6 } from "react/jsx-runtime";
163
+ function ConfirmPrompt({ value }) {
164
+ return /* @__PURE__ */ jsxs6(Box5, { gap: 2, paddingLeft: 2, children: [
165
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: value, inverse: value, children: "Yes" }),
166
+ /* @__PURE__ */ jsx8(Text8, { color: "cyan", bold: !value, inverse: !value, children: "No" }),
167
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: "(\u2190/\u2192 to toggle, Enter to confirm)" })
168
+ ] });
169
+ }
170
+
146
171
  // src/commands/registry.ts
147
172
  import { Fzf } from "fzf";
148
173
 
@@ -358,8 +383,8 @@ function buildServiceModuleEntry(organizationName, platformName, applicationName
358
383
  dependsOn: []
359
384
  };
360
385
  }
361
- function buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName) {
362
- const uiName = `${platformName}-${applicationName}-ui`;
386
+ function buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName, uiNameOverride) {
387
+ const uiName = uiNameOverride ?? `${platformName}-${applicationName}-ui`;
363
388
  return {
364
389
  name: `@${organizationName}/${uiName}`,
365
390
  displayName: applicationDisplayName,
@@ -791,7 +816,8 @@ async function generateLocalEnv(outputDir, logger, coreDirName = "core") {
791
816
  var INIT_COMMAND_NAME = "init";
792
817
  var initCommand = {
793
818
  name: INIT_COMMAND_NAME,
794
- description: "Initialize a new platform"
819
+ description: "Initialize a new platform",
820
+ hidden: ({ platformInitialized }) => platformInitialized
795
821
  };
796
822
  async function init(params, logger) {
797
823
  if (await isPlatformInitialized()) {
@@ -803,8 +829,10 @@ async function init(params, logger) {
803
829
  const outputDir = cwd4();
804
830
  logger.log(`Initializing ${platformTitle} platform for @${organizationName}...`);
805
831
  const coreDirName = `${platformName}-core`;
806
- const bootstrapServiceName = `${platformName}-bootstrap-service`;
807
- const customizationUiName = `${platformName}-customization-ui`;
832
+ const bootstrapSuffix = params.bootstrapServiceSuffix ?? "bootstrap-service";
833
+ const customizationUiSuffix = params.customizationUiSuffix ?? "customization-ui";
834
+ const bootstrapServiceName = `${platformName}-${bootstrapSuffix}`;
835
+ const customizationUiName = `${platformName}-${customizationUiSuffix}`;
808
836
  const variables = {
809
837
  organizationName,
810
838
  platformName,
@@ -1077,7 +1105,7 @@ var createServiceModuleCommand = {
1077
1105
  description: "Add a new service module to an existing application"
1078
1106
  };
1079
1107
  async function createServiceModule(params, logger) {
1080
- const { applicationName, serviceName, serviceDisplayName } = params;
1108
+ const { applicationName, serviceName, serviceDisplayName, serviceNameSuffix } = params;
1081
1109
  const layout = await findPlatformLayout();
1082
1110
  if (!layout) {
1083
1111
  logger.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
@@ -1104,7 +1132,8 @@ async function createServiceModule(params, logger) {
1104
1132
  logger.log(`Error: The specified application "${applicationName}" does not exist in the platform.`);
1105
1133
  return;
1106
1134
  }
1107
- const fullServiceName = `${platformName}-${serviceName}-service`;
1135
+ const suffix = serviceNameSuffix === void 0 ? "service" : serviceNameSuffix;
1136
+ const fullServiceName = suffix ? `${platformName}-${serviceName}-${suffix}` : `${platformName}-${serviceName}`;
1108
1137
  const serviceDir = join20(applicationDir, "services", fullServiceName);
1109
1138
  try {
1110
1139
  await access3(serviceDir);
@@ -1141,8 +1170,8 @@ import { access as access4, readdir as readdir4 } from "fs/promises";
1141
1170
 
1142
1171
  // src/commands/create-ui-module/append-ui-docker-compose.ts
1143
1172
  import { readFile as readFile9, writeFile as writeFile8 } from "fs/promises";
1144
- async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger) {
1145
- const uiName = `${platformName}-${applicationName}-ui`;
1173
+ async function appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger, uiNameOverride) {
1174
+ const uiName = uiNameOverride ?? `${platformName}-${applicationName}-ui`;
1146
1175
  const block = `
1147
1176
  ${uiName}:
1148
1177
  build:
@@ -1170,7 +1199,7 @@ var createUiModuleCommand = {
1170
1199
  description: "Add a UI module to an existing application"
1171
1200
  };
1172
1201
  async function createUiModule(params, logger) {
1173
- const { applicationName, applicationDisplayName } = params;
1202
+ const { applicationName, applicationDisplayName, uiModuleSuffix } = params;
1174
1203
  const layout = await findPlatformLayout();
1175
1204
  if (!layout) {
1176
1205
  logger.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
@@ -1207,7 +1236,8 @@ async function createUiModule(params, logger) {
1207
1236
  }
1208
1237
  } catch {
1209
1238
  }
1210
- const uiName = `${platformName}-${applicationName}-ui`;
1239
+ const uiSuffix = uiModuleSuffix === void 0 ? "ui" : uiModuleSuffix;
1240
+ const uiName = uiSuffix ? `${platformName}-${applicationName}-${uiSuffix}` : `${platformName}-${applicationName}`;
1211
1241
  const uiOutputDir = join21(uiDir, uiName);
1212
1242
  const uiBaseDir = `${platformName}-${applicationName}/ui`;
1213
1243
  try {
@@ -1217,11 +1247,11 @@ async function createUiModule(params, logger) {
1217
1247
  return;
1218
1248
  }
1219
1249
  const bootstrapServiceDir = join21(applicationDir, "services", `${platformName}-${applicationName}-bootstrap-service`);
1220
- const moduleEntry = buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName);
1250
+ const moduleEntry = buildUiModuleEntry(organizationName, platformName, applicationName, applicationDisplayName, uiName);
1221
1251
  await addModuleEntry(bootstrapServiceDir, applicationName, moduleEntry, logger);
1222
1252
  const dockerComposePath = join21(localDir, `${platformName}-${applicationName}-docker-compose.yml`);
1223
1253
  const port = await getNextAvailablePort(localDir);
1224
- await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger);
1254
+ await appendUiToDockerCompose(dockerComposePath, platformName, applicationName, port, logger, uiName);
1225
1255
  logger.log(`Done! UI module "${uiName}" added to application "${applicationName}".`);
1226
1256
  }
1227
1257
 
@@ -1245,6 +1275,14 @@ var CommandRegistry = class {
1245
1275
  });
1246
1276
  return fzf.find(query).map((result) => result.item);
1247
1277
  }
1278
+ searchVisible(query, visibilityCtx) {
1279
+ const visible = this.getAll().filter((cmd) => !cmd.hidden?.(visibilityCtx));
1280
+ if (!query) return visible;
1281
+ const fzf = new Fzf(visible, {
1282
+ selector: (cmd) => `${cmd.name} ${cmd.description}`
1283
+ });
1284
+ return fzf.find(query).map((result) => result.item);
1285
+ }
1248
1286
  };
1249
1287
  var registry = new CommandRegistry();
1250
1288
  registry.register(createApplicationCommand);
@@ -1282,23 +1320,15 @@ async function createApplicationUiController(ctx) {
1282
1320
  }
1283
1321
  const applicationDisplayName = await ctx.prompt("Application display name:");
1284
1322
  const applicationDescription = await ctx.prompt("Application description:");
1285
- const hasUserInterfaceAnswer = await ctx.prompt("Does this application have a user interface? (yes/no):");
1286
- if (hasUserInterfaceAnswer !== "yes" && hasUserInterfaceAnswer !== "no") {
1287
- ctx.log(`Error: Please answer "yes" or "no".`);
1288
- return;
1289
- }
1290
- const hasBackendServiceAnswer = await ctx.prompt("Does this application have a backend service? (yes/no):");
1291
- if (hasBackendServiceAnswer !== "yes" && hasBackendServiceAnswer !== "no") {
1292
- ctx.log(`Error: Please answer "yes" or "no".`);
1293
- return;
1294
- }
1323
+ const hasUserInterface = await ctx.confirm("Does this application have a user interface?");
1324
+ const hasBackendService = await ctx.confirm("Does this application have a backend service?");
1295
1325
  await createApplicationService(
1296
1326
  {
1297
1327
  applicationName,
1298
1328
  applicationDisplayName,
1299
1329
  applicationDescription,
1300
- hasUserInterface: hasUserInterfaceAnswer === "yes",
1301
- hasBackendService: hasBackendServiceAnswer === "yes"
1330
+ hasUserInterface,
1331
+ hasBackendService
1302
1332
  },
1303
1333
  ctx
1304
1334
  );
@@ -1318,7 +1348,32 @@ async function initUiController(ctx) {
1318
1348
  const organizationName = await ctx.prompt("Organization name:");
1319
1349
  const platformName = await ctx.prompt("Platform name:");
1320
1350
  const platformDisplayName = await ctx.prompt("Platform display name:");
1321
- await initService({ organizationName, platformName, platformDisplayName }, ctx);
1351
+ const defaultBootstrapSuffix = "bootstrap-service";
1352
+ const defaultCustomizationUiSuffix = "customization-ui";
1353
+ ctx.log(`Default artifact names:`);
1354
+ ctx.log(` Bootstrap service: ${platformName}-${defaultBootstrapSuffix}`);
1355
+ ctx.log(` Customization UI: ${platformName}-${defaultCustomizationUiSuffix}`);
1356
+ const customize = await ctx.confirm("Customize artifact names?", false);
1357
+ let bootstrapServiceSuffix = defaultBootstrapSuffix;
1358
+ let customizationUiSuffix = defaultCustomizationUiSuffix;
1359
+ if (customize) {
1360
+ const bsSuffix = await ctx.prompt(`Bootstrap service suffix (${platformName}-...):`, defaultBootstrapSuffix);
1361
+ if (!/^[a-z0-9-]+$/.test(bsSuffix)) {
1362
+ ctx.log(`Error: Suffix "${bsSuffix}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
1363
+ return;
1364
+ }
1365
+ bootstrapServiceSuffix = bsSuffix;
1366
+ const cuiSuffix = await ctx.prompt(`Customization UI suffix (${platformName}-...):`, defaultCustomizationUiSuffix);
1367
+ if (!/^[a-z0-9-]+$/.test(cuiSuffix)) {
1368
+ ctx.log(`Error: Suffix "${cuiSuffix}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
1369
+ return;
1370
+ }
1371
+ customizationUiSuffix = cuiSuffix;
1372
+ }
1373
+ await initService(
1374
+ { organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix },
1375
+ ctx
1376
+ );
1322
1377
  }
1323
1378
 
1324
1379
  // src/services/configure-idp.service.ts
@@ -1333,14 +1388,11 @@ async function configureIdpUiController(ctx) {
1333
1388
  return;
1334
1389
  }
1335
1390
  const providers = getAllProviders();
1336
- const options = providers.map((p, i) => `${i + 1}: ${p.displayName}`).join(", ");
1337
- const selectionInput = await ctx.prompt(`Select IDP type (${options}):`);
1338
- const index = parseInt(selectionInput.trim(), 10) - 1;
1339
- if (isNaN(index) || index < 0 || index >= providers.length) {
1340
- ctx.log(`Error: Invalid selection "${selectionInput}"`);
1341
- return;
1342
- }
1343
- const provider = providers[index];
1391
+ const providerType = await ctx.select(
1392
+ "Select IDP type:",
1393
+ providers.map((p) => ({ label: p.displayName, value: p.type }))
1394
+ );
1395
+ const provider = providers.find((p) => p.type === providerType);
1344
1396
  const name = await ctx.prompt("Provider name:");
1345
1397
  const issuer = await ctx.prompt("Issuer URL:");
1346
1398
  const clientId = await ctx.prompt("Client ID:");
@@ -1366,19 +1418,38 @@ async function createServiceModuleUiController(ctx) {
1366
1418
  ctx.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
1367
1419
  return;
1368
1420
  }
1369
- const applicationName = await ctx.prompt("Application name:");
1370
- if (!/^[a-z0-9-]+$/.test(applicationName)) {
1371
- ctx.log(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
1421
+ const layout = await findPlatformLayout();
1422
+ if (!layout) {
1423
+ ctx.log("Error: Cannot create a service module \u2014 no platform initialized in this directory.");
1372
1424
  return;
1373
1425
  }
1426
+ let manifest = await readManifest(layout.rootDir, layout.coreDirName);
1427
+ if (manifest.applications.length === 0) {
1428
+ ctx.log("No applications found in this platform.");
1429
+ const createFirst = await ctx.confirm("Would you like to create an application first?");
1430
+ if (!createFirst) return;
1431
+ await createApplicationUiController(ctx);
1432
+ manifest = await readManifest(layout.rootDir, layout.coreDirName);
1433
+ if (manifest.applications.length === 0) {
1434
+ ctx.log("Error: No applications available after creation attempt.");
1435
+ return;
1436
+ }
1437
+ }
1438
+ const applicationName = await ctx.select(
1439
+ "Select application:",
1440
+ manifest.applications.map((a) => ({ label: `${a.name} \u2014 ${a.displayName}`, value: a.name }))
1441
+ );
1374
1442
  const serviceName = await ctx.prompt("Service name:");
1375
1443
  if (!/^[a-z0-9-]+$/.test(serviceName)) {
1376
1444
  ctx.log(`Error: Service name "${serviceName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
1377
1445
  return;
1378
1446
  }
1447
+ const platformName = manifest.product.name;
1448
+ const includeSuffix = await ctx.confirm(`Include "-service" suffix? (${platformName}-${serviceName}-service)`, true);
1449
+ const serviceNameSuffix = includeSuffix ? "service" : null;
1379
1450
  const serviceDisplayName = await ctx.prompt("Service display name:");
1380
1451
  await createServiceModuleService(
1381
- { applicationName, serviceName, serviceDisplayName },
1452
+ { applicationName, serviceName, serviceDisplayName, serviceNameSuffix },
1382
1453
  ctx
1383
1454
  );
1384
1455
  }
@@ -1394,14 +1465,33 @@ async function createUiModuleUiController(ctx) {
1394
1465
  ctx.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
1395
1466
  return;
1396
1467
  }
1397
- const applicationName = await ctx.prompt("Application name:");
1398
- if (!/^[a-z0-9-]+$/.test(applicationName)) {
1399
- ctx.log(`Error: Application name "${applicationName}" is invalid. Use only lowercase letters, numbers, and hyphens.`);
1468
+ const layout = await findPlatformLayout();
1469
+ if (!layout) {
1470
+ ctx.log("Error: Cannot create a UI module \u2014 no platform initialized in this directory.");
1400
1471
  return;
1401
1472
  }
1473
+ let manifest = await readManifest(layout.rootDir, layout.coreDirName);
1474
+ if (manifest.applications.length === 0) {
1475
+ ctx.log("No applications found in this platform.");
1476
+ const createFirst = await ctx.confirm("Would you like to create an application first?");
1477
+ if (!createFirst) return;
1478
+ await createApplicationUiController(ctx);
1479
+ manifest = await readManifest(layout.rootDir, layout.coreDirName);
1480
+ if (manifest.applications.length === 0) {
1481
+ ctx.log("Error: No applications available after creation attempt.");
1482
+ return;
1483
+ }
1484
+ }
1485
+ const applicationName = await ctx.select(
1486
+ "Select application:",
1487
+ manifest.applications.map((a) => ({ label: `${a.name} \u2014 ${a.displayName}`, value: a.name }))
1488
+ );
1489
+ const platformName = manifest.product.name;
1490
+ const includeSuffix = await ctx.confirm(`Include "-ui" suffix? (${platformName}-${applicationName}-ui)`, true);
1491
+ const uiModuleSuffix = includeSuffix ? "ui" : null;
1402
1492
  const applicationDisplayName = await ctx.prompt("Application display name:");
1403
1493
  await createUiModuleService(
1404
- { applicationName, applicationDisplayName },
1494
+ { applicationName, applicationDisplayName, uiModuleSuffix },
1405
1495
  ctx
1406
1496
  );
1407
1497
  }
@@ -1416,16 +1506,20 @@ var uiControllers = /* @__PURE__ */ new Map([
1416
1506
  ]);
1417
1507
 
1418
1508
  // src/hooks/use-command-runner.ts
1419
- function useCommandRunner({ appendStaticItem, setState }) {
1509
+ function useCommandRunner({ appendStaticItem, setState, onCommandComplete }) {
1420
1510
  const outputCountRef = useRef(0);
1421
1511
  const abortControllerRef = useRef(null);
1422
1512
  const promptResolveRef = useRef(null);
1423
1513
  const [promptMessage, setPromptMessage] = useState2("");
1424
1514
  const [promptValue, setPromptValue] = useState2("");
1515
+ const [promptMode, setPromptMode] = useState2({ kind: "text" });
1516
+ const [selectIndex, setSelectIndex] = useState2(0);
1517
+ const [confirmValue, setConfirmValue] = useState2(true);
1425
1518
  const abortExecution = useCallback(() => {
1426
1519
  abortControllerRef.current?.abort();
1427
1520
  abortControllerRef.current = null;
1428
1521
  promptResolveRef.current = null;
1522
+ setPromptMode({ kind: "text" });
1429
1523
  }, []);
1430
1524
  const runCommand2 = useCallback(
1431
1525
  (cmd) => {
@@ -1440,14 +1534,15 @@ function useCommandRunner({ appendStaticItem, setState }) {
1440
1534
  appendStaticItem({ kind: "output", id, line: message });
1441
1535
  }
1442
1536
  },
1443
- prompt(message) {
1537
+ prompt(message, defaultValue) {
1444
1538
  return new Promise((resolve5, reject) => {
1445
1539
  if (controller.signal.aborted) {
1446
1540
  reject(new DOMException("Aborted", "AbortError"));
1447
1541
  return;
1448
1542
  }
1449
1543
  setPromptMessage(message);
1450
- setPromptValue("");
1544
+ setPromptValue(defaultValue ?? "");
1545
+ setPromptMode({ kind: "text" });
1451
1546
  promptResolveRef.current = resolve5;
1452
1547
  setState(APP_STATE.PROMPTING);
1453
1548
  controller.signal.addEventListener(
@@ -1456,6 +1551,42 @@ function useCommandRunner({ appendStaticItem, setState }) {
1456
1551
  { once: true }
1457
1552
  );
1458
1553
  });
1554
+ },
1555
+ select(message, options) {
1556
+ return new Promise((resolve5, reject) => {
1557
+ if (controller.signal.aborted) {
1558
+ reject(new DOMException("Aborted", "AbortError"));
1559
+ return;
1560
+ }
1561
+ setPromptMessage(message);
1562
+ setPromptMode({ kind: "select", options });
1563
+ setSelectIndex(0);
1564
+ promptResolveRef.current = resolve5;
1565
+ setState(APP_STATE.PROMPTING);
1566
+ controller.signal.addEventListener(
1567
+ "abort",
1568
+ () => reject(new DOMException("Aborted", "AbortError")),
1569
+ { once: true }
1570
+ );
1571
+ });
1572
+ },
1573
+ confirm(message, defaultValue) {
1574
+ return new Promise((resolve5, reject) => {
1575
+ if (controller.signal.aborted) {
1576
+ reject(new DOMException("Aborted", "AbortError"));
1577
+ return;
1578
+ }
1579
+ setPromptMessage(message);
1580
+ setPromptMode({ kind: "confirm" });
1581
+ setConfirmValue(defaultValue ?? true);
1582
+ promptResolveRef.current = (value) => resolve5(value === "true");
1583
+ setState(APP_STATE.PROMPTING);
1584
+ controller.signal.addEventListener(
1585
+ "abort",
1586
+ () => reject(new DOMException("Aborted", "AbortError")),
1587
+ { once: true }
1588
+ );
1589
+ });
1459
1590
  }
1460
1591
  };
1461
1592
  const uiController = uiControllers.get(cmd.name);
@@ -1468,20 +1599,23 @@ function useCommandRunner({ appendStaticItem, setState }) {
1468
1599
  uiController(ctx).then(() => {
1469
1600
  if (!controller.signal.aborted) {
1470
1601
  setState(APP_STATE.IDLE);
1602
+ onCommandComplete?.();
1471
1603
  }
1472
1604
  }).catch((err) => {
1473
1605
  if (controller.signal.aborted) return;
1474
1606
  const id = `output-${outputCountRef.current++}`;
1475
1607
  appendStaticItem({ kind: "output", id, line: `Error: ${formatError(err)}` });
1476
1608
  setState(APP_STATE.IDLE);
1609
+ onCommandComplete?.();
1477
1610
  });
1478
1611
  },
1479
- [appendStaticItem, setState]
1612
+ [appendStaticItem, setState, onCommandComplete]
1480
1613
  );
1481
1614
  const handlePromptSubmit = useCallback(
1482
1615
  (value) => {
1483
1616
  const resolve5 = promptResolveRef.current;
1484
1617
  promptResolveRef.current = null;
1618
+ setPromptMode({ kind: "text" });
1485
1619
  setState(APP_STATE.EXECUTING);
1486
1620
  resolve5?.(value);
1487
1621
  },
@@ -1493,12 +1627,17 @@ function useCommandRunner({ appendStaticItem, setState }) {
1493
1627
  abortExecution,
1494
1628
  promptMessage,
1495
1629
  promptValue,
1496
- setPromptValue
1630
+ setPromptValue,
1631
+ promptMode,
1632
+ selectIndex,
1633
+ setSelectIndex,
1634
+ confirmValue,
1635
+ setConfirmValue
1497
1636
  };
1498
1637
  }
1499
1638
 
1500
1639
  // src/app.tsx
1501
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1640
+ import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
1502
1641
  var require2 = createRequire(import.meta.url);
1503
1642
  var { version } = require2("../package.json");
1504
1643
  function App() {
@@ -1507,12 +1646,33 @@ function App() {
1507
1646
  const [inputValue, setInputValue] = useState3("");
1508
1647
  const [selectedIndex, setSelectedIndex] = useState3(0);
1509
1648
  const [staticItems, setStaticItems] = useState3([{ kind: "banner", id: "banner" }]);
1649
+ const [platformInitialized, setPlatformInitialized] = useState3(false);
1650
+ useEffect2(() => {
1651
+ isPlatformInitialized().then(setPlatformInitialized).catch(() => {
1652
+ });
1653
+ }, []);
1510
1654
  const appendStaticItem = useCallback2((item) => {
1511
1655
  setStaticItems((prev) => [...prev, item]);
1512
1656
  }, []);
1513
- const { runCommand: runCommand2, handlePromptSubmit, abortExecution, promptMessage, promptValue, setPromptValue } = useCommandRunner({ appendStaticItem, setState });
1657
+ const handleCommandComplete = useCallback2(() => {
1658
+ isPlatformInitialized().then(setPlatformInitialized).catch(() => {
1659
+ });
1660
+ }, []);
1661
+ const {
1662
+ runCommand: runCommand2,
1663
+ handlePromptSubmit,
1664
+ abortExecution,
1665
+ promptMessage,
1666
+ promptValue,
1667
+ setPromptValue,
1668
+ promptMode,
1669
+ selectIndex,
1670
+ setSelectIndex,
1671
+ confirmValue,
1672
+ setConfirmValue
1673
+ } = useCommandRunner({ appendStaticItem, setState, onCommandComplete: handleCommandComplete });
1514
1674
  const query = inputValue.startsWith("/") ? inputValue.slice(1) : "";
1515
- const filteredCommands = registry.search(query);
1675
+ const filteredCommands = registry.searchVisible(query, { platformInitialized });
1516
1676
  useInput(
1517
1677
  (input, key) => {
1518
1678
  if (key.ctrl && input === "c") {
@@ -1524,6 +1684,40 @@ function App() {
1524
1684
  setState(APP_STATE.IDLE);
1525
1685
  return;
1526
1686
  }
1687
+ if (state === APP_STATE.PROMPTING) {
1688
+ if (promptMode.kind === "select") {
1689
+ if (key.upArrow) {
1690
+ setSelectIndex((prev) => Math.max(0, prev - 1));
1691
+ return;
1692
+ }
1693
+ if (key.downArrow) {
1694
+ setSelectIndex((prev) => Math.min(promptMode.options.length - 1, prev + 1));
1695
+ return;
1696
+ }
1697
+ if (key.return) {
1698
+ const selected = promptMode.options[selectIndex];
1699
+ if (selected) handlePromptSubmit(selected.value);
1700
+ return;
1701
+ }
1702
+ return;
1703
+ }
1704
+ if (promptMode.kind === "confirm") {
1705
+ if (key.leftArrow || key.upArrow) {
1706
+ setConfirmValue(true);
1707
+ return;
1708
+ }
1709
+ if (key.rightArrow || key.downArrow) {
1710
+ setConfirmValue(false);
1711
+ return;
1712
+ }
1713
+ if (key.return) {
1714
+ handlePromptSubmit(confirmValue ? "true" : "false");
1715
+ return;
1716
+ }
1717
+ return;
1718
+ }
1719
+ return;
1720
+ }
1527
1721
  if (state === APP_STATE.PALETTE) {
1528
1722
  if (key.escape) {
1529
1723
  setState(APP_STATE.IDLE);
@@ -1585,17 +1779,19 @@ function App() {
1585
1779
  function renderActiveArea() {
1586
1780
  switch (state) {
1587
1781
  case APP_STATE.EXECUTING:
1588
- return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "row", gap: 1, children: [
1589
- /* @__PURE__ */ jsx7(Spinner, { label: "Running\u2026" }),
1590
- /* @__PURE__ */ jsx7(Text7, { dimColor: true, children: "(esc to cancel)" })
1782
+ return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "row", gap: 1, children: [
1783
+ /* @__PURE__ */ jsx9(Spinner, { label: "Running\u2026" }),
1784
+ /* @__PURE__ */ jsx9(Text9, { dimColor: true, children: "(esc to cancel)" })
1591
1785
  ] });
1592
1786
  case APP_STATE.PROMPTING:
1593
- return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "column", children: [
1594
- /* @__PURE__ */ jsx7(Text7, { color: "cyan", children: promptMessage }),
1595
- /* @__PURE__ */ jsx7(Prompt, { value: promptValue, onChange: setPromptValue, onSubmit: handlePromptSubmit })
1787
+ return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
1788
+ /* @__PURE__ */ jsx9(Text9, { color: "cyan", children: promptMessage }),
1789
+ promptMode.kind === "select" && /* @__PURE__ */ jsx9(SelectList, { options: promptMode.options, selectedIndex: selectIndex }),
1790
+ promptMode.kind === "confirm" && /* @__PURE__ */ jsx9(ConfirmPrompt, { value: confirmValue }),
1791
+ promptMode.kind === "text" && /* @__PURE__ */ jsx9(Prompt, { value: promptValue, onChange: setPromptValue, onSubmit: handlePromptSubmit })
1596
1792
  ] });
1597
1793
  default:
1598
- return /* @__PURE__ */ jsx7(
1794
+ return /* @__PURE__ */ jsx9(
1599
1795
  Prompt,
1600
1796
  {
1601
1797
  value: inputValue,
@@ -1608,10 +1804,10 @@ function App() {
1608
1804
  );
1609
1805
  }
1610
1806
  }
1611
- return /* @__PURE__ */ jsxs6(Box4, { flexDirection: "column", children: [
1612
- /* @__PURE__ */ jsx7(ScrollbackHistory, { items: staticItems, version }),
1807
+ return /* @__PURE__ */ jsxs7(Box6, { flexDirection: "column", children: [
1808
+ /* @__PURE__ */ jsx9(ScrollbackHistory, { items: staticItems, version }),
1613
1809
  renderActiveArea(),
1614
- state === APP_STATE.PALETTE && /* @__PURE__ */ jsx7(CommandPalette, { commands: filteredCommands, selectedIndex })
1810
+ state === APP_STATE.PALETTE && /* @__PURE__ */ jsx9(CommandPalette, { commands: filteredCommands, selectedIndex })
1615
1811
  ] });
1616
1812
  }
1617
1813
 
@@ -1654,13 +1850,13 @@ async function initCliController(args2) {
1654
1850
  console.error("Error: Cannot initialize a new platform \u2014 a platform is already initialized in this directory.");
1655
1851
  process.exit(1);
1656
1852
  }
1657
- const { organizationName, platformName, platformDisplayName } = args2;
1853
+ const { organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix } = args2;
1658
1854
  if (!organizationName || !platformName || !platformDisplayName) {
1659
1855
  console.error("Error: organizationName, platformName, and platformDisplayName are required.");
1660
1856
  process.exit(1);
1661
1857
  }
1662
1858
  await initService(
1663
- { organizationName, platformName, platformDisplayName },
1859
+ { organizationName, platformName, platformDisplayName, bootstrapServiceSuffix, customizationUiSuffix },
1664
1860
  { log: console.log }
1665
1861
  );
1666
1862
  }
@@ -2132,10 +2328,10 @@ function parseKeyValueArgs(args2) {
2132
2328
  }
2133
2329
 
2134
2330
  // src/index.tsx
2135
- import { jsx as jsx8 } from "react/jsx-runtime";
2331
+ import { jsx as jsx10 } from "react/jsx-runtime";
2136
2332
  var args = process.argv.slice(2);
2137
2333
  if (args.length === 0) {
2138
- render(/* @__PURE__ */ jsx8(App, {}));
2334
+ render(/* @__PURE__ */ jsx10(App, {}));
2139
2335
  } else {
2140
2336
  const commandName = args[0];
2141
2337
  const params = parseKeyValueArgs(args.slice(1));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bluealba/platform-cli",
3
- "version": "0.3.1-feature-platform-cli-prefix-221",
3
+ "version": "0.3.1-feature-platform-cli-224",
4
4
  "description": "Blue Alba Platform CLI",
5
5
  "license": "PolyForm-Noncommercial-1.0.0",
6
6
  "type": "module",
@@ -14,7 +14,8 @@
14
14
  "scripts": {
15
15
  "build": "tsup",
16
16
  "dev": "tsup --watch",
17
- "start": "node dist/index.js"
17
+ "start": "node dist/index.js",
18
+ "install-local": "bash scripts/install-local.sh"
18
19
  },
19
20
  "dependencies": {
20
21
  "chalk": "^5.3.0",