showpane 0.4.5 → 0.4.7

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.
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
- "generatedAt": "2026-04-09T14:11:15.147Z",
4
- "scaffoldVersion": "0.2.2",
3
+ "generatedAt": "2026-04-09T14:38:37.519Z",
4
+ "scaffoldVersion": "0.2.3",
5
5
  "files": {
6
6
  ".env.example": "0dd692f1c7e6bcabdf5dbdfe9abb73797d79d8e90da150d6098b63ddc695dc29",
7
7
  ".gitignore": "998e5f43865ea56ac79a05acfd5d4b0d696f310bd5325a1ed458c3d40154d437",
8
- "VERSION": "998f6b887ed7a6ef44e84210d0237b715bed42ea7254f48dc418afec0a484103",
8
+ "VERSION": "3ab94c04d24986f3af288ba1cda2c0bbddbc5a89dff097182805f54578e1ea75",
9
9
  "docker-compose.yml": "420fd123da019c22f03662933537e24779b4c2c91f90c23abfec5965cd0f35ce",
10
10
  "docker/Caddyfile": "d9c58086986795f5b3e42ff9b5942e60b8df946a1a0c40351381616c0b4d2bed",
11
11
  "docker/Dockerfile": "340470e3735ea53b2c03003a13a91361652291add33c40a2bf13e6af2a8cb73a",
@@ -47,7 +47,7 @@
47
47
  "src/app/api/health/route.ts": "78fff55707372ce0cd6e9e49ef4f049622bc43cc42916d3f83e0162409d678b1",
48
48
  "src/app/globals.css": "28dcda76006d0e6af01b6dcf1a315dc5b5b6931c880fc53fd6565ff09d5dd13a",
49
49
  "src/app/layout.tsx": "c17aabeb2b486f023e777230343ace6cc06840f641a10b9dd9f65e092018f82f",
50
- "src/app/page.tsx": "df6abc817e751782df1f59ed770d6e0b47d3a24ea4ad3682485680bdf71845f8",
50
+ "src/app/page.tsx": "732ea54f313386b65bce1170785379b27bb26b5da26b833b1e50c3713b87be1a",
51
51
  "src/components/copy-button.tsx": "2f3d1d8a6a0a570c8d78e19c3c15519c44af17b5d8893ae5a5f57db5ecce7077",
52
52
  "src/components/portal-login.tsx": "8b0d91bb28674e1102fd2e5b5ddcc3a93755dd806fbd3d1b2dbea2646cffca5e",
53
53
  "src/components/portal-shell.tsx": "a4e16e118ef93f79e71fb69e80f1fac6e6fff90f0fbdacdf8deb821a57656877",
@@ -1 +1 @@
1
- 0.2.2
1
+ 0.2.3
@@ -4,6 +4,7 @@ import { prisma } from "@/lib/db";
4
4
  import { getRuntimeState, isRuntimeSnapshotMode } from "@/lib/runtime-state";
5
5
  import { ArrowUpRight, BookOpen, Command, MessageSquareQuote } from "lucide-react";
6
6
  import Link from "next/link";
7
+ import { existsSync, readFileSync } from "node:fs";
7
8
  import os from "node:os";
8
9
  import path from "node:path";
9
10
 
@@ -16,7 +17,20 @@ const PROMPT_EXAMPLES = [
16
17
  export default async function Home() {
17
18
  let portalCount = 0;
18
19
  const showpaneBinDir = path.join(os.homedir(), ".showpane", "bin");
19
- const prefersCanonicalCommand = (process.env.PATH ?? "").split(path.delimiter).includes(showpaneBinDir);
20
+ const configPath = path.join(os.homedir(), ".showpane", "config.json");
21
+ const configShellPathConfigured = existsSync(configPath)
22
+ ? (() => {
23
+ try {
24
+ const raw = readFileSync(configPath, "utf8");
25
+ return Boolean((JSON.parse(raw) as { shellPathConfigured?: boolean }).shellPathConfigured);
26
+ } catch {
27
+ return false;
28
+ }
29
+ })()
30
+ : false;
31
+ const prefersCanonicalCommand =
32
+ configShellPathConfigured ||
33
+ (process.env.PATH ?? "").split(path.delimiter).includes(showpaneBinDir);
20
34
  const primaryCommand = "showpane claude";
21
35
  const fallbackCommand = "npx showpane claude";
22
36
  try {
@@ -1 +1 @@
1
- 1.1.2 (requires app >= 0.2.2)
1
+ 1.1.3 (requires app >= 0.2.3)
package/dist/index.js CHANGED
@@ -243,35 +243,146 @@ function getResumeCommand() {
243
243
  function getResumeHint() {
244
244
  return isShowpaneShimOnPath() ? null : `Optional: add ${SHOWPANE_BIN_DIR} to your PATH to use ${BOLD}showpane${RESET} directly.`;
245
245
  }
246
+ function shellPathExportLine() {
247
+ return `export PATH="$HOME/.showpane/bin:$PATH"`;
248
+ }
249
+ function detectShellProfile() {
250
+ if (process.platform === "win32") {
251
+ return null;
252
+ }
253
+ const shell = basename(process.env.SHELL || "");
254
+ if (shell === "zsh") {
255
+ return join(homedir(), ".zshrc");
256
+ }
257
+ if (shell === "bash") {
258
+ return process.platform === "darwin" ? join(homedir(), ".bash_profile") : join(homedir(), ".bashrc");
259
+ }
260
+ if (shell === "fish") {
261
+ return join(homedir(), ".config", "fish", "config.fish");
262
+ }
263
+ return null;
264
+ }
265
+ function ensureShellPathEntry(profilePath) {
266
+ ensureDir(dirname(profilePath));
267
+ const exportLine = shellPathExportLine();
268
+ const contents = existsSync(profilePath) ? readFileSync(profilePath, "utf8") : "";
269
+ if (contents.includes(exportLine) || contents.includes(SHOWPANE_BIN_DIR)) {
270
+ return false;
271
+ }
272
+ const prefix = contents.length > 0 && !contents.endsWith("\n") ? "\n" : "";
273
+ writeFileSync(profilePath, `${contents}${prefix}${exportLine}
274
+ `);
275
+ return true;
276
+ }
277
+ async function maybeConfigureShellPath(config, options) {
278
+ const configuredProfile = typeof config.shellPathConfiguredProfile === "string" ? config.shellPathConfiguredProfile : null;
279
+ if (isShowpaneShimOnPath()) {
280
+ config.shellPathConfigured = true;
281
+ config.shellPathPrompted = true;
282
+ return {
283
+ command: "showpane claude",
284
+ configured: true,
285
+ profilePath: configuredProfile
286
+ };
287
+ }
288
+ if (config.shellPathConfigured) {
289
+ return {
290
+ command: "showpane claude",
291
+ configured: true,
292
+ profilePath: configuredProfile
293
+ };
294
+ }
295
+ if (options.yes || !process.stdin.isTTY || !process.stdout.isTTY) {
296
+ config.shellPathPrompted = true;
297
+ config.shellPathConfigured = false;
298
+ return {
299
+ command: "npx showpane claude",
300
+ configured: false,
301
+ profilePath: configuredProfile
302
+ };
303
+ }
304
+ const profilePath = detectShellProfile();
305
+ if (!profilePath) {
306
+ config.shellPathPrompted = true;
307
+ config.shellPathConfigured = false;
308
+ return {
309
+ command: "npx showpane claude",
310
+ configured: false,
311
+ profilePath: null
312
+ };
313
+ }
314
+ const answer = await ask(
315
+ ` ${BOLD}Add Showpane to your PATH so 'showpane' works in future terminals?${RESET} ${DIM}(recommended) [Y/n]${RESET} `
316
+ );
317
+ if (answer && !["y", "yes"].includes(answer.toLowerCase())) {
318
+ config.shellPathPrompted = true;
319
+ config.shellPathConfigured = false;
320
+ config.shellPathConfiguredProfile = profilePath;
321
+ return {
322
+ command: "npx showpane claude",
323
+ configured: false,
324
+ profilePath
325
+ };
326
+ }
327
+ ensureShellPathEntry(profilePath);
328
+ config.shellPathPrompted = true;
329
+ config.shellPathConfigured = true;
330
+ config.shellPathConfiguredProfile = profilePath;
331
+ console.log();
332
+ green(`Added Showpane to your PATH in ${DIM}${profilePath}${RESET}`);
333
+ return {
334
+ command: "showpane claude",
335
+ configured: true,
336
+ profilePath
337
+ };
338
+ }
246
339
  function getCommandOutput(errorLike) {
247
340
  const error2 = errorLike;
248
341
  const stdout = typeof error2?.stdout === "string" ? error2.stdout : error2?.stdout?.toString() ?? "";
249
342
  const stderr = typeof error2?.stderr === "string" ? error2.stderr : error2?.stderr?.toString() ?? "";
250
343
  return [stdout, stderr].filter(Boolean).join("\n").trim();
251
344
  }
252
- function runQuiet(command2, cwd, env) {
253
- try {
254
- execSync(command2, {
255
- cwd,
256
- env: env ? { ...process.env, ...env } : process.env,
257
- encoding: "utf8",
258
- stdio: ["ignore", "pipe", "pipe"],
259
- maxBuffer: 20 * 1024 * 1024
345
+ async function runQuietAsync(command2, cwd, env) {
346
+ const child = spawn(command2, {
347
+ cwd,
348
+ env: env ? { ...process.env, ...env } : process.env,
349
+ shell: true,
350
+ stdio: ["ignore", "pipe", "pipe"]
351
+ });
352
+ let output = "";
353
+ const appendOutput = (chunk) => {
354
+ output += chunk.toString();
355
+ if (output.length > 20 * 1024 * 1024) {
356
+ output = output.slice(-20 * 1024 * 1024);
357
+ }
358
+ };
359
+ child.stdout?.on("data", appendOutput);
360
+ child.stderr?.on("data", appendOutput);
361
+ await new Promise((resolvePromise, rejectPromise) => {
362
+ child.on("error", (errorLike) => {
363
+ rejectPromise(
364
+ new StepCommandError(
365
+ errorLike instanceof Error ? errorLike.message : String(errorLike),
366
+ output.trim()
367
+ )
368
+ );
260
369
  });
261
- } catch (errorLike) {
262
- const output = getCommandOutput(errorLike);
263
- throw new StepCommandError(
264
- errorLike instanceof Error ? errorLike.message : String(errorLike),
265
- output
266
- );
267
- }
370
+ child.on("close", (code, signal) => {
371
+ if (code === 0) {
372
+ resolvePromise();
373
+ return;
374
+ }
375
+ const reason = signal ? `Command terminated with signal ${signal}` : `Command exited with code ${code ?? "unknown"}`;
376
+ rejectPromise(new StepCommandError(reason, output.trim()));
377
+ });
378
+ });
268
379
  }
269
- function runInstallerCommand(command2, cwd, env, verbose) {
380
+ async function runInstallerCommand(command2, cwd, env, verbose) {
270
381
  if (verbose) {
271
382
  run(command2, cwd, env);
272
383
  return;
273
384
  }
274
- runQuiet(command2, cwd, env);
385
+ await runQuietAsync(command2, cwd, env);
275
386
  }
276
387
  var activeSpinner = null;
277
388
  function renderSpinner(label, frame, startedAt) {
@@ -691,39 +802,39 @@ function applyUpgradePlan(projectRoot, scaffoldSource, plan) {
691
802
  cpSync(sourcePath, targetPath);
692
803
  }
693
804
  }
694
- function installDependencies(projectRoot, verbose) {
805
+ async function installDependencies(projectRoot, verbose) {
695
806
  if (existsSync(join(projectRoot, "package-lock.json"))) {
696
807
  if (verbose === void 0) {
697
808
  run("npm ci", projectRoot, getInstallerEnv());
698
809
  } else {
699
- runInstallerCommand("npm ci", projectRoot, getInstallerEnv(), verbose);
810
+ await runInstallerCommand("npm ci", projectRoot, getInstallerEnv(), verbose);
700
811
  }
701
812
  } else {
702
813
  if (verbose === void 0) {
703
814
  run("npm install", projectRoot, getInstallerEnv());
704
815
  } else {
705
- runInstallerCommand("npm install", projectRoot, getInstallerEnv(), verbose);
816
+ await runInstallerCommand("npm install", projectRoot, getInstallerEnv(), verbose);
706
817
  }
707
818
  }
708
819
  }
709
- function generateLocalDatabase(projectRoot, databaseUrl, verbose) {
820
+ async function generateLocalDatabase(projectRoot, databaseUrl, verbose) {
710
821
  const env = getInstallerEnv({
711
822
  DATABASE_URL: databaseUrl
712
823
  });
713
824
  if (verbose === void 0) {
714
825
  run("npm run prisma:db-push", projectRoot, env);
715
826
  } else {
716
- runInstallerCommand("npm run prisma:db-push", projectRoot, env, verbose);
827
+ await runInstallerCommand("npm run prisma:db-push", projectRoot, env, verbose);
717
828
  }
718
829
  }
719
- function seedProject(projectRoot, databaseUrl, verbose) {
830
+ async function seedProject(projectRoot, databaseUrl, verbose) {
720
831
  const env = getInstallerEnv({
721
832
  DATABASE_URL: databaseUrl
722
833
  });
723
834
  if (verbose === void 0) {
724
835
  run("npx tsx prisma/seed.ts", projectRoot, env);
725
836
  } else {
726
- runInstallerCommand("npx tsx prisma/seed.ts", projectRoot, env, verbose);
837
+ await runInstallerCommand("npx tsx prisma/seed.ts", projectRoot, env, verbose);
727
838
  }
728
839
  }
729
840
  function maybeRunPostUpgradeSteps(projectRoot, changedPaths) {
@@ -794,9 +905,8 @@ function installSharedSkillProjection(toolchainRoot) {
794
905
  process.platform === "win32" ? "junction" : "dir"
795
906
  );
796
907
  }
797
- function printCreateSuccessCard(projectRoot, url) {
798
- const resumeCommand = getResumeCommand();
799
- const resumeHint = getResumeHint();
908
+ function printCreateSuccessCard(projectRoot, url, pathSetup) {
909
+ const resumeCommand = pathSetup.command;
800
910
  console.log();
801
911
  console.log(` ${GREEN}Showpane is ready${RESET}`);
802
912
  console.log();
@@ -807,13 +917,17 @@ function printCreateSuccessCard(projectRoot, url) {
807
917
  console.log(` ${BOLD}Next (in a new terminal window):${RESET}`);
808
918
  console.log(` ${DIM}${resumeCommand}${RESET}`);
809
919
  console.log();
810
- console.log(` ${DIM}Your current terminal is running the local app logs, so open a fresh terminal before you run that command.${RESET}`);
920
+ if (pathSetup.configured && pathSetup.profilePath) {
921
+ console.log(` ${DIM}Your current terminal is running the local app logs. Open a fresh terminal so ${BOLD}showpane${RESET}${DIM} is available from ${pathSetup.profilePath}.${RESET}`);
922
+ } else {
923
+ console.log(` ${DIM}Your current terminal is running the local app logs, so open a fresh terminal before you run that command.${RESET}`);
924
+ }
811
925
  console.log();
812
926
  console.log(` ${BOLD}Try:${RESET}`);
813
927
  console.log(` ${DIM}Create a portal for my call with Acme Health${RESET}`);
814
- if (resumeHint) {
928
+ if (!pathSetup.configured) {
815
929
  console.log();
816
- console.log(` ${DIM}${resumeHint}${RESET}`);
930
+ console.log(` ${DIM}${getResumeHint()}${RESET}`);
817
931
  }
818
932
  console.log();
819
933
  }
@@ -1045,6 +1159,12 @@ async function createProject(args) {
1045
1159
  }
1046
1160
  printBanner();
1047
1161
  ensureShowpaneShim();
1162
+ const config = readShowpaneConfig();
1163
+ let pathSetup = {
1164
+ command: getResumeCommand(),
1165
+ configured: isShowpaneShimOnPath() || Boolean(config.shellPathConfigured),
1166
+ profilePath: typeof config.shellPathConfiguredProfile === "string" ? config.shellPathConfiguredProfile : null
1167
+ };
1048
1168
  const companyName = options.companyName ?? await ask(` ${BOLD}What's your company name?${RESET} `);
1049
1169
  if (!companyName) {
1050
1170
  error("Company name is required.");
@@ -1060,20 +1180,20 @@ async function createProject(args) {
1060
1180
  console.log();
1061
1181
  blue(`Setting up ${BOLD}${companyName}${RESET} portal as ${DIM}${dirName}/${RESET}`);
1062
1182
  console.log();
1063
- stepStartForCreate("Create project", options);
1183
+ stepStartForCreate("Creating project", options);
1064
1184
  try {
1065
1185
  copyScaffoldFiles(join(bundleRoot, "scaffold"), projectRoot, scaffoldManifest);
1066
1186
  stepSuccessForCreate("Project created");
1067
1187
  } catch (errorLike) {
1068
- stepFailureForCreate("Create project", errorLike);
1188
+ stepFailureForCreate("Creating project", errorLike);
1069
1189
  }
1070
- stepStartForCreate("Install dependencies", options);
1190
+ stepStartForCreate("Installing dependencies", options);
1071
1191
  try {
1072
- installDependencies(projectRoot, options.verbose);
1192
+ await installDependencies(projectRoot, options.verbose);
1073
1193
  stepSuccessForCreate("Dependencies installed");
1074
1194
  } catch (errorLike) {
1075
1195
  stepFailureForCreate(
1076
- "Install dependencies",
1196
+ "Installing dependencies",
1077
1197
  errorLike,
1078
1198
  "Check your Node.js version and network connection, then try again."
1079
1199
  );
@@ -1086,28 +1206,28 @@ async function createProject(args) {
1086
1206
  AUTH_SECRET="${authSecret}"
1087
1207
  `
1088
1208
  );
1089
- stepStartForCreate("Configure database", options);
1209
+ stepStartForCreate("Configuring database", options);
1090
1210
  try {
1091
- generateLocalDatabase(projectRoot, databaseUrl, options.verbose);
1092
- seedProject(projectRoot, databaseUrl, options.verbose);
1211
+ await generateLocalDatabase(projectRoot, databaseUrl, options.verbose);
1212
+ await seedProject(projectRoot, databaseUrl, options.verbose);
1093
1213
  stepSuccessForCreate("Database configured");
1094
1214
  } catch (errorLike) {
1095
1215
  stepFailureForCreate(
1096
- "Configure database",
1216
+ "Configuring database",
1097
1217
  errorLike,
1098
1218
  "Check Prisma setup and the generated .env file, then retry the install."
1099
1219
  );
1100
1220
  }
1101
- stepStartForCreate("Install Claude skills", options);
1221
+ stepStartForCreate("Installing Claude skills", options);
1102
1222
  let toolchainInfo;
1103
1223
  try {
1104
1224
  toolchainInfo = syncToolchain(bundleRoot, showpaneVersion, false);
1105
- const config = readShowpaneConfig();
1106
1225
  updateWorkspaceFromConfig(config, projectRoot, {
1107
1226
  name: dirName,
1108
1227
  deployMode: "local",
1109
1228
  orgSlug: ""
1110
1229
  });
1230
+ pathSetup = await maybeConfigureShellPath(config, options);
1111
1231
  writeShowpaneConfig(config);
1112
1232
  writeProjectState(
1113
1233
  projectRoot,
@@ -1119,12 +1239,12 @@ AUTH_SECRET="${authSecret}"
1119
1239
  stepSuccessForCreate("Claude skills installed");
1120
1240
  } catch (errorLike) {
1121
1241
  stepFailureForCreate(
1122
- "Install Claude skills",
1242
+ "Installing Claude skills",
1123
1243
  errorLike,
1124
1244
  "Check permissions for ~/.showpane and ~/.claude/skills, then try again."
1125
1245
  );
1126
1246
  }
1127
- stepStartForCreate("Start app", options);
1247
+ stepStartForCreate("Starting app", options);
1128
1248
  let serverStart;
1129
1249
  try {
1130
1250
  serverStart = await startDevServer(
@@ -1135,12 +1255,12 @@ AUTH_SECRET="${authSecret}"
1135
1255
  );
1136
1256
  } catch (errorLike) {
1137
1257
  stepFailureForCreate(
1138
- "Start app",
1258
+ "Starting app",
1139
1259
  errorLike,
1140
1260
  `Run ${BOLD}cd ${dirName} && npm run dev${RESET} for more detail.`
1141
1261
  );
1142
1262
  }
1143
- printCreateSuccessCard(projectRoot, serverStart.url);
1263
+ printCreateSuccessCard(projectRoot, serverStart.url, pathSetup);
1144
1264
  serverStart.devServer.on("close", (code) => {
1145
1265
  if (code !== 0) {
1146
1266
  error(`Dev server exited with code ${code}`);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "showpane",
3
- "version": "0.4.5",
4
- "description": "CLI for Showpane AI-generated client portals",
3
+ "version": "0.4.7",
4
+ "description": "CLI for Showpane \u2014 AI-generated client portals",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "showpane": "./dist/index.js"