showpane 0.2.0 → 0.3.0

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/README.md CHANGED
@@ -14,7 +14,8 @@ npx showpane
14
14
  Scaffold a new Showpane portal project. Sets up Next.js, Prisma, and example portal.
15
15
 
16
16
  ### `showpane login`
17
- Authenticate with Showpane Cloud for portal deployment.
17
+ Authenticate with Showpane Cloud for hosted portal deployment.
18
+ This is auth only — org creation and billing happen in the Showpane Cloud web app.
18
19
 
19
20
  ## Requirements
20
21
 
package/dist/index.js CHANGED
@@ -3,12 +3,15 @@
3
3
  // src/index.ts
4
4
  import { createInterface } from "readline";
5
5
  import { execSync, spawn, exec } from "child_process";
6
- import { mkdirSync, readFileSync, writeFileSync } from "fs";
6
+ import fs from "fs";
7
7
  import { randomBytes } from "crypto";
8
8
  import { createServer } from "net";
9
- import { resolve, dirname, join } from "path";
10
- import { homedir } from "os";
9
+ import path from "path";
10
+ import os from "os";
11
11
  import { fileURLToPath } from "url";
12
+ var { mkdirSync, readFileSync, writeFileSync } = fs;
13
+ var { resolve, dirname, join } = path;
14
+ var { homedir } = os;
12
15
  var RESET = "\x1B[0m";
13
16
  var BOLD = "\x1B[1m";
14
17
  var DIM = "\x1B[2m";
@@ -129,17 +132,63 @@ AUTH_SECRET="${authSecret}"
129
132
  } catch {
130
133
  blue("Skipped example portal (seed failed \u2014 not a problem)");
131
134
  }
135
+ const skillsSource = path.resolve(process.cwd(), dirName, "skills");
136
+ const skillsTarget = path.join(os.homedir(), ".claude", "skills");
137
+ const oldSymlink = path.join(skillsTarget, "showpane");
138
+ try {
139
+ const stat = fs.lstatSync(oldSymlink);
140
+ if (stat.isSymbolicLink()) fs.unlinkSync(oldSymlink);
141
+ } catch {
142
+ }
143
+ fs.mkdirSync(skillsTarget, { recursive: true });
144
+ const skillDirs = fs.readdirSync(skillsSource, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("portal-"));
145
+ const installedSkills = [];
146
+ for (const dir of skillDirs) {
147
+ const skillMdPath = path.join(skillsSource, dir.name, "SKILL.md");
148
+ if (!fs.existsSync(skillMdPath)) continue;
149
+ const skillContent = fs.readFileSync(skillMdPath, "utf-8");
150
+ const nameMatch = skillContent.match(/^name:\s*(.+)$/m);
151
+ const skillName = nameMatch?.[1]?.trim() || dir.name;
152
+ const targetDir = path.join(skillsTarget, skillName);
153
+ try {
154
+ const stat = fs.lstatSync(targetDir);
155
+ if (stat.isSymbolicLink()) fs.unlinkSync(targetDir);
156
+ } catch {
157
+ }
158
+ fs.mkdirSync(targetDir, { recursive: true });
159
+ const skillFile = path.join(targetDir, "SKILL.md");
160
+ try {
161
+ fs.unlinkSync(skillFile);
162
+ } catch {
163
+ }
164
+ fs.symlinkSync(skillMdPath, skillFile);
165
+ installedSkills.push(skillName);
166
+ const prefixedDir = path.join(skillsTarget, `showpane-${skillName}`);
167
+ try {
168
+ if (fs.existsSync(prefixedDir) && fs.lstatSync(path.join(prefixedDir, "SKILL.md")).isSymbolicLink()) {
169
+ const linkDest = fs.readlinkSync(path.join(prefixedDir, "SKILL.md"));
170
+ if (linkDest.includes("showpane") || linkDest.includes("portal")) {
171
+ fs.rmSync(prefixedDir, { recursive: true });
172
+ }
173
+ }
174
+ } catch {
175
+ }
176
+ }
177
+ green(`${installedSkills.length} Claude Code skills installed`);
132
178
  const port = await findFreePort(3e3);
133
179
  green(`Server starting on port ${port}`);
134
180
  const url = `http://localhost:${port}`;
135
181
  blue(`Opening ${url}`);
136
182
  console.log();
137
- console.log(` ${GREEN}Ready!${RESET} Open Claude Code and create your first portal:`);
183
+ console.log(` ${GREEN}Ready!${RESET} ${installedSkills.length} Claude Code skills installed.`);
138
184
  console.log();
185
+ console.log(` Open Claude Code and create your first portal:`);
139
186
  console.log(` ${DIM}cd ${dirName}/app${RESET}`);
140
187
  console.log(` ${BOLD}claude${RESET}`);
141
188
  console.log(` ${DIM}> Create a portal for my call with [client name]${RESET}`);
142
189
  console.log();
190
+ console.log(` Available skills: /portal-create, /portal-deploy, /portal-list, ...`);
191
+ console.log();
143
192
  console.log(` ${DIM}Don't have Claude Code? Install from https://claude.ai/code${RESET}`);
144
193
  console.log();
145
194
  const devServer = spawn("npm", ["run", "dev"], {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "showpane",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "CLI for Showpane — AI-generated client portals",
5
5
  "type": "module",
6
6
  "bin": {