vibespot 0.4.4 → 0.5.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/dist/index.js CHANGED
@@ -1124,6 +1124,13 @@ function getHubspotRules() {
1124
1124
  return "";
1125
1125
  }
1126
1126
  }
1127
+ function getHumanifyGuide() {
1128
+ try {
1129
+ return readFile(resolveAsset("humanify-guide.md"));
1130
+ } catch {
1131
+ return "";
1132
+ }
1133
+ }
1127
1134
  function getPageTypeGuide(pageType) {
1128
1135
  try {
1129
1136
  const fullGuide = readFile(resolveAsset("page-types.md"));
@@ -1305,7 +1312,8 @@ var ClaudeCodeEngine = class {
1305
1312
  const child = spawn("claude", args, {
1306
1313
  cwd: themePath,
1307
1314
  stdio: ["pipe", "pipe", "pipe"],
1308
- env
1315
+ env,
1316
+ shell: true
1309
1317
  });
1310
1318
  child.stdout.on("data", (d) => {
1311
1319
  stdout += d.toString();
@@ -1642,7 +1650,7 @@ ${guide}`;
1642
1650
 
1643
1651
  // src/ai/claude-api.ts
1644
1652
  import Anthropic from "@anthropic-ai/sdk";
1645
- import { join as join7 } from "path";
1653
+ import { join as join7, basename as basename3 } from "path";
1646
1654
  import { readdirSync as readdirSync5 } from "fs";
1647
1655
  var ClaudeAPIEngine = class {
1648
1656
  client;
@@ -1655,7 +1663,7 @@ var ClaudeAPIEngine = class {
1655
1663
  async convert(opts) {
1656
1664
  const { sourceDir, themePath, conversionGuide, onProgress } = opts;
1657
1665
  const systemPrompt = buildSystemPrompt(conversionGuide);
1658
- const dirName = sourceDir.split("/").pop() || "page";
1666
+ const dirName = basename3(sourceDir) || "page";
1659
1667
  const pagePrefix = dirName.toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "").slice(0, 15);
1660
1668
  onProgress("css", "Analyzing design system...");
1661
1669
  const indexCss = this.findAndReadCSS(sourceDir);
@@ -1835,7 +1843,8 @@ var GeminiCLIEngine = class {
1835
1843
  const child = spawn2("gemini", ["-p", prompt], {
1836
1844
  cwd: themePath,
1837
1845
  stdio: ["pipe", "pipe", "pipe"],
1838
- env: { ...process.env }
1846
+ env: { ...process.env },
1847
+ shell: true
1839
1848
  });
1840
1849
  let stdout = "";
1841
1850
  let stderr = "";
@@ -1962,7 +1971,8 @@ var CodexCLIEngine = class {
1962
1971
  const child = spawn3("codex", ["exec", "--full-auto", prompt], {
1963
1972
  cwd: themePath,
1964
1973
  stdio: ["pipe", "pipe", "pipe"],
1965
- env: { ...process.env }
1974
+ env: { ...process.env },
1975
+ shell: true
1966
1976
  });
1967
1977
  let stdout = "";
1968
1978
  let stderr = "";
@@ -2417,7 +2427,7 @@ function validateModuleMeta(themePath) {
2417
2427
  }
2418
2428
 
2419
2429
  // src/wizard/uploader.ts
2420
- import { join as join12 } from "path";
2430
+ import { join as join12, basename as basename4 } from "path";
2421
2431
 
2422
2432
  // src/server/auto-fix.ts
2423
2433
  import { join as join11 } from "path";
@@ -2741,7 +2751,7 @@ function countUploadedFiles(output) {
2741
2751
  }
2742
2752
  async function runUpload(themePath) {
2743
2753
  await intro2("Uploading to HubSpot");
2744
- const themeName = themePath.split("/").pop() || themePath;
2754
+ const themeName = basename4(themePath) || themePath;
2745
2755
  const s = await spinner2();
2746
2756
  const MAX_RETRIES = 3;
2747
2757
  for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
@@ -2831,7 +2841,7 @@ The theme may work \u2014 check HubSpot Design Manager.`
2831
2841
  // src/wizard/next-steps.ts
2832
2842
  import { execSync as execSync2 } from "child_process";
2833
2843
  import { rmSync as rmSync3 } from "fs";
2834
- import { basename as basename3 } from "path";
2844
+ import { basename as basename5 } from "path";
2835
2845
  async function showNextSteps(opts) {
2836
2846
  const { portalId, sourceDir, themePath, wasCloned } = opts;
2837
2847
  await intro2("You're all set!");
@@ -2874,10 +2884,10 @@ Next steps:
2874
2884
  }
2875
2885
  const dirsToClean = [];
2876
2886
  if (wasCloned && fileExists(sourceDir)) {
2877
- dirsToClean.push({ path: sourceDir, label: `Cloned source (${basename3(sourceDir)})` });
2887
+ dirsToClean.push({ path: sourceDir, label: `Cloned source (${basename5(sourceDir)})` });
2878
2888
  }
2879
2889
  if (fileExists(themePath)) {
2880
- dirsToClean.push({ path: themePath, label: `Theme directory (${basename3(themePath)})` });
2890
+ dirsToClean.push({ path: themePath, label: `Theme directory (${basename5(themePath)})` });
2881
2891
  }
2882
2892
  if (dirsToClean.length > 0) {
2883
2893
  const cleanup = await confirm2({
@@ -3087,7 +3097,7 @@ import chalk2 from "chalk";
3087
3097
  // src/server/server.ts
3088
3098
  import { createServer } from "http";
3089
3099
  import { readFileSync as readFileSync5, existsSync as existsSync5, readdirSync as readdirSync11, appendFileSync, rmSync as rmSync5, renameSync as renameSync2 } from "fs";
3090
- import { join as join15, extname as extname2, basename as basename5 } from "path";
3100
+ import { join as join15, extname as extname2, basename as basename7 } from "path";
3091
3101
  import { homedir as homedir4 } from "os";
3092
3102
  import { execSync as execSync4 } from "child_process";
3093
3103
  import { WebSocketServer } from "ws";
@@ -3376,6 +3386,14 @@ function removeModule(moduleName) {
3376
3386
  activeSession.updatedAt = Date.now();
3377
3387
  syncFlatFieldsToTemplate();
3378
3388
  }
3389
+ function detachModule(moduleName) {
3390
+ if (!activeSession) return;
3391
+ activeSession.moduleOrder = activeSession.moduleOrder.filter(
3392
+ (n) => n !== moduleName
3393
+ );
3394
+ activeSession.updatedAt = Date.now();
3395
+ syncFlatFieldsToTemplate();
3396
+ }
3379
3397
  function updateFieldValue(moduleName, fieldPath, value) {
3380
3398
  if (!activeSession) return;
3381
3399
  const mod = activeSession.modules.find((m) => m.moduleName === moduleName);
@@ -3484,7 +3502,14 @@ function listSessions() {
3484
3502
  return readdirSync10(SESSIONS_DIR).filter((f) => f.endsWith(".json")).map((f) => {
3485
3503
  try {
3486
3504
  const data = JSON.parse(readFileSync4(join14(SESSIONS_DIR, f), "utf-8"));
3487
- return { id: data.id, themeName: data.themeName, updatedAt: data.updatedAt };
3505
+ const templates = data.templates || [];
3506
+ return {
3507
+ id: data.id,
3508
+ themeName: data.themeName,
3509
+ updatedAt: data.updatedAt,
3510
+ moduleCount: templates.reduce((n, t) => n + (t.modules?.length || 0), 0),
3511
+ templateCount: templates.length
3512
+ };
3488
3513
  } catch {
3489
3514
  return null;
3490
3515
  }
@@ -4364,19 +4389,28 @@ If the change affects shared CSS or JS, include those too.`;
4364
4389
  ## Page Type Context
4365
4390
  ${pageTypeSection}` : "";
4366
4391
  let brandPrompt = "";
4367
- if (!editMode && brandAssets) {
4368
- if (brandAssets.styleguide) {
4392
+ if (!editMode) {
4393
+ if (brandAssets?.styleguide) {
4369
4394
  brandPrompt += `
4370
4395
 
4371
4396
  ## Brand Style Guide
4372
4397
  ${brandAssets.styleguide}`;
4373
4398
  }
4374
- if (brandAssets.brandvoice) {
4399
+ if (brandAssets?.brandvoice) {
4375
4400
  brandPrompt += `
4376
4401
 
4377
4402
  ## Brand Voice
4378
4403
  ${brandAssets.brandvoice}`;
4379
4404
  }
4405
+ if (brandAssets?.humanify !== false) {
4406
+ const humanifyGuide = getHumanifyGuide();
4407
+ if (humanifyGuide) {
4408
+ brandPrompt += `
4409
+
4410
+ ## Anti-AI Copy Rules (Humanify)
4411
+ ${humanifyGuide}`;
4412
+ }
4413
+ }
4380
4414
  }
4381
4415
  if (editMode) {
4382
4416
  return core + pageTypePrompt + `
@@ -4714,7 +4748,8 @@ function spawnCLI(bin, args, prompt, onChunk) {
4714
4748
  delete env.CLAUDECODE;
4715
4749
  const child = spawn4(bin, args, {
4716
4750
  stdio: ["pipe", "pipe", "pipe"],
4717
- env
4751
+ env,
4752
+ shell: true
4718
4753
  });
4719
4754
  let stdout = "";
4720
4755
  let stderr = "";
@@ -5295,8 +5330,12 @@ function handleModulesRoute(method, req, res) {
5295
5330
  }
5296
5331
  if (method === "DELETE") {
5297
5332
  readBody(req, (body) => {
5298
- const { moduleName } = JSON.parse(body);
5299
- removeModule(moduleName);
5333
+ const { moduleName, deleteEntirely } = JSON.parse(body);
5334
+ if (deleteEntirely) {
5335
+ removeModule(moduleName);
5336
+ } else {
5337
+ detachModule(moduleName);
5338
+ }
5300
5339
  saveSession();
5301
5340
  jsonResponse(res, 200, { ok: true });
5302
5341
  });
@@ -5410,7 +5449,15 @@ function handleSetupInfoRoute(res) {
5410
5449
  if (entry.isDirectory()) {
5411
5450
  const themeJson = join15(WORKSPACE_DIR, entry.name, "theme.json");
5412
5451
  if (existsSync5(themeJson)) {
5413
- localThemes.push(entry.name);
5452
+ let moduleCount = 0;
5453
+ const modulesDir = join15(WORKSPACE_DIR, entry.name, "modules");
5454
+ if (existsSync5(modulesDir)) {
5455
+ try {
5456
+ moduleCount = readdirSync11(modulesDir, { withFileTypes: true }).filter((e) => e.isDirectory()).length;
5457
+ } catch {
5458
+ }
5459
+ }
5460
+ localThemes.push({ name: entry.name, moduleCount });
5414
5461
  }
5415
5462
  }
5416
5463
  }
@@ -5524,7 +5571,7 @@ function handleSetupOpenRoute(req, res) {
5524
5571
  jsonResponse(res, 400, { error: `Theme folder not found: ${themePath}` });
5525
5572
  return;
5526
5573
  }
5527
- const themeName = basename5(fullPath);
5574
+ const themeName = basename7(fullPath);
5528
5575
  createSession(fullPath, themeName);
5529
5576
  scanThemeFromDisk(fullPath);
5530
5577
  saveSession();
@@ -5867,17 +5914,19 @@ function handleSettingsCLIAuthRoute(req, res) {
5867
5914
  const key = apiKey.trim();
5868
5915
  process.env.OPENAI_API_KEY = key;
5869
5916
  saveConfig({ openaiApiKey: key });
5870
- const profileLine = `export OPENAI_API_KEY="${key}"`;
5871
- const shellProfile = process.env.SHELL?.includes("zsh") ? join15(homedir4(), ".zshrc") : join15(homedir4(), ".bashrc");
5872
- try {
5873
- const existing = existsSync5(shellProfile) ? readFileSync5(shellProfile, "utf-8") : "";
5874
- if (!existing.includes("OPENAI_API_KEY")) {
5875
- appendFileSync(shellProfile, `
5917
+ if (process.platform !== "win32") {
5918
+ const profileLine = `export OPENAI_API_KEY="${key}"`;
5919
+ const shellProfile = process.env.SHELL?.includes("zsh") ? join15(homedir4(), ".zshrc") : join15(homedir4(), ".bashrc");
5920
+ try {
5921
+ const existing = existsSync5(shellProfile) ? readFileSync5(shellProfile, "utf-8") : "";
5922
+ if (!existing.includes("OPENAI_API_KEY")) {
5923
+ appendFileSync(shellProfile, `
5876
5924
  # Added by vibeSpot
5877
5925
  ${profileLine}
5878
5926
  `);
5927
+ }
5928
+ } catch {
5879
5929
  }
5880
- } catch {
5881
5930
  }
5882
5931
  jsonResponse(res, 200, { ok: true, message: "API key saved" });
5883
5932
  } else {
@@ -6049,7 +6098,8 @@ function handleDashboardRoute(res) {
6049
6098
  })),
6050
6099
  brandAssets: {
6051
6100
  hasStyleguide: !!session.brandAssets?.styleguide,
6052
- hasBrandvoice: !!session.brandAssets?.brandvoice
6101
+ hasBrandvoice: !!session.brandAssets?.brandvoice,
6102
+ humanify: session.brandAssets?.humanify !== false
6053
6103
  }
6054
6104
  });
6055
6105
  }
@@ -6208,15 +6258,26 @@ function handleBrandAssetsRoute(method, req, res) {
6208
6258
  readBody(req, (body) => {
6209
6259
  try {
6210
6260
  const { type, content } = JSON.parse(body);
6211
- if (!type || !content) {
6212
- jsonResponse(res, 400, { error: "type and content are required" });
6261
+ if (!type) {
6262
+ jsonResponse(res, 400, { error: "type is required" });
6263
+ return;
6264
+ }
6265
+ if (!session.brandAssets) session.brandAssets = {};
6266
+ if (type === "humanify") {
6267
+ session.brandAssets.humanify = content === "on";
6268
+ session.updatedAt = Date.now();
6269
+ saveSession();
6270
+ jsonResponse(res, 200, { ok: true });
6271
+ return;
6272
+ }
6273
+ if (!content) {
6274
+ jsonResponse(res, 400, { error: "content is required" });
6213
6275
  return;
6214
6276
  }
6215
6277
  if (type !== "styleguide" && type !== "brandvoice") {
6216
6278
  jsonResponse(res, 400, { error: `Invalid type: ${type}. Must be "styleguide" or "brandvoice"` });
6217
6279
  return;
6218
6280
  }
6219
- if (!session.brandAssets) session.brandAssets = {};
6220
6281
  session.brandAssets[type] = content;
6221
6282
  session.updatedAt = Date.now();
6222
6283
  const assetDir = join15(session.themePath, ".vibespot");
@@ -6508,8 +6569,13 @@ async function vibeCommand() {
6508
6569
  console.log(accent(` v ${url}`));
6509
6570
  console.log(dim(" Press Ctrl+C to stop\n"));
6510
6571
  try {
6511
- const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
6512
- execSync5(`${openCmd} ${url}`, { stdio: "ignore" });
6572
+ if (process.platform === "darwin") {
6573
+ execSync5(`open "${url}"`, { stdio: "ignore" });
6574
+ } else if (process.platform === "win32") {
6575
+ execSync5(`cmd /c start "" "${url}"`, { stdio: "ignore" });
6576
+ } else {
6577
+ execSync5(`xdg-open "${url}"`, { stdio: "ignore" });
6578
+ }
6513
6579
  } catch {
6514
6580
  }
6515
6581
  await new Promise((resolve) => {