showpane 0.2.1 → 0.3.1

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
@@ -132,7 +132,7 @@ AUTH_SECRET="${authSecret}"
132
132
  } catch {
133
133
  blue("Skipped example portal (seed failed \u2014 not a problem)");
134
134
  }
135
- const skillsSource = path.join(dirName, "skills");
135
+ const skillsSource = path.resolve(process.cwd(), dirName, "skills");
136
136
  const skillsTarget = path.join(os.homedir(), ".claude", "skills");
137
137
  const oldSymlink = path.join(skillsTarget, "showpane");
138
138
  try {
@@ -142,26 +142,45 @@ AUTH_SECRET="${authSecret}"
142
142
  }
143
143
  fs.mkdirSync(skillsTarget, { recursive: true });
144
144
  const skillDirs = fs.readdirSync(skillsSource, { withFileTypes: true }).filter((d) => d.isDirectory() && d.name.startsWith("portal-"));
145
+ const installedSkills = [];
145
146
  for (const dir of skillDirs) {
146
- const targetDir = path.join(skillsTarget, dir.name);
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
+ }
147
158
  fs.mkdirSync(targetDir, { recursive: true });
148
159
  const skillFile = path.join(targetDir, "SKILL.md");
149
160
  try {
150
161
  fs.unlinkSync(skillFile);
151
162
  } catch {
152
163
  }
153
- fs.symlinkSync(
154
- path.join(skillsSource, dir.name, "SKILL.md"),
155
- skillFile
156
- );
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
+ }
157
176
  }
158
- green(`${skillDirs.length} Claude Code skills installed`);
177
+ green(`${installedSkills.length} Claude Code skills installed`);
159
178
  const port = await findFreePort(3e3);
160
179
  green(`Server starting on port ${port}`);
161
180
  const url = `http://localhost:${port}`;
162
181
  blue(`Opening ${url}`);
163
182
  console.log();
164
- console.log(` ${GREEN}Ready!${RESET} ${skillDirs.length} Claude Code skills installed.`);
183
+ console.log(` ${GREEN}Ready!${RESET} ${installedSkills.length} Claude Code skills installed.`);
165
184
  console.log();
166
185
  console.log(` Open Claude Code and create your first portal:`);
167
186
  console.log(` ${DIM}cd ${dirName}/app${RESET}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "showpane",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
4
  "description": "CLI for Showpane — AI-generated client portals",
5
5
  "type": "module",
6
6
  "bin": {