vibebusiness 1.2.49 → 1.2.51

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 (70) hide show
  1. package/.next/standalone/.next/BUILD_ID +1 -1
  2. package/.next/standalone/.next/app-build-manifest.json +17 -17
  3. package/.next/standalone/.next/app-path-routes-manifest.json +1 -1
  4. package/.next/standalone/.next/build-manifest.json +2 -2
  5. package/.next/standalone/.next/prerender-manifest.json +1 -1
  6. package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  7. package/.next/standalone/.next/server/app/_not-found.html +1 -1
  8. package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
  9. package/.next/standalone/.next/server/app/api/analyze/route.js.nft.json +1 -1
  10. package/.next/standalone/.next/server/app/api/config/route.js.nft.json +1 -1
  11. package/.next/standalone/.next/server/app/api/epics/[id]/ideas/route.js.nft.json +1 -1
  12. package/.next/standalone/.next/server/app/api/epics/[id]/route.js.nft.json +1 -1
  13. package/.next/standalone/.next/server/app/api/epics/route.js.nft.json +1 -1
  14. package/.next/standalone/.next/server/app/api/goals/[id]/kpis/route.js.nft.json +1 -1
  15. package/.next/standalone/.next/server/app/api/goals/[id]/route.js.nft.json +1 -1
  16. package/.next/standalone/.next/server/app/api/goals/route.js.nft.json +1 -1
  17. package/.next/standalone/.next/server/app/api/hypotheses/[id]/route.js.nft.json +1 -1
  18. package/.next/standalone/.next/server/app/api/hypotheses/route.js.nft.json +1 -1
  19. package/.next/standalone/.next/server/app/api/ideas/[id]/comments/route.js.nft.json +1 -1
  20. package/.next/standalone/.next/server/app/api/ideas/[id]/implement/route.js.nft.json +1 -1
  21. package/.next/standalone/.next/server/app/api/ideas/[id]/route.js.nft.json +1 -1
  22. package/.next/standalone/.next/server/app/api/ideas/[id]/transition/route.js.nft.json +1 -1
  23. package/.next/standalone/.next/server/app/api/ideas/route.js.nft.json +1 -1
  24. package/.next/standalone/.next/server/app/api/implementations/route.js.nft.json +1 -1
  25. package/.next/standalone/.next/server/app/api/kpis/refresh/route.js +1 -1
  26. package/.next/standalone/.next/server/app/api/kpis/refresh/route.js.nft.json +1 -1
  27. package/.next/standalone/.next/server/app/api/social/[id]/publish/route.js.nft.json +1 -1
  28. package/.next/standalone/.next/server/app/api/social/[id]/route.js.nft.json +1 -1
  29. package/.next/standalone/.next/server/app/api/social/route.js.nft.json +1 -1
  30. package/.next/standalone/.next/server/app/goals/[id]/page_client-reference-manifest.js +1 -1
  31. package/.next/standalone/.next/server/app/goals/page.js.nft.json +1 -1
  32. package/.next/standalone/.next/server/app/goals/page_client-reference-manifest.js +1 -1
  33. package/.next/standalone/.next/server/app/hypotheses/[id]/page_client-reference-manifest.js +1 -1
  34. package/.next/standalone/.next/server/app/hypotheses/page.js +1 -1
  35. package/.next/standalone/.next/server/app/hypotheses/page.js.nft.json +1 -1
  36. package/.next/standalone/.next/server/app/hypotheses/page_client-reference-manifest.js +1 -1
  37. package/.next/standalone/.next/server/app/ideas/[id]/page.js.nft.json +1 -1
  38. package/.next/standalone/.next/server/app/ideas/[id]/page_client-reference-manifest.js +1 -1
  39. package/.next/standalone/.next/server/app/page.js +1 -1
  40. package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
  41. package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  42. package/.next/standalone/.next/server/app/roadmap/[id]/page_client-reference-manifest.js +1 -1
  43. package/.next/standalone/.next/server/app/roadmap/page.js.nft.json +1 -1
  44. package/.next/standalone/.next/server/app/roadmap/page_client-reference-manifest.js +1 -1
  45. package/.next/standalone/.next/server/app/sessions/page.js.nft.json +1 -1
  46. package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
  47. package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  48. package/.next/standalone/.next/server/app/settings.html +1 -1
  49. package/.next/standalone/.next/server/app/settings.rsc +1 -1
  50. package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
  51. package/.next/standalone/.next/server/app/social.html +1 -1
  52. package/.next/standalone/.next/server/app/social.rsc +1 -1
  53. package/.next/standalone/.next/server/app-paths-manifest.json +13 -13
  54. package/.next/standalone/.next/server/chunks/3794.js +64 -4
  55. package/.next/standalone/.next/server/pages/404.html +1 -1
  56. package/.next/standalone/.next/server/pages/500.html +1 -1
  57. package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
  58. package/.next/standalone/data/business-context.json +45 -6
  59. package/.next/standalone/data/goals.json +50 -10
  60. package/.next/standalone/data/ideas.json +1278 -597
  61. package/.next/standalone/data/implementations.json +101 -0
  62. package/.next/standalone/data/posthog.json +68 -0
  63. package/.next/standalone/data/sessions.json +5 -4
  64. package/.next/standalone/data/social.json +2 -2
  65. package/.next/standalone/package.json +1 -1
  66. package/.next/standalone/scripts/skills/social-media.ts +172 -2
  67. package/dist/scripts/heartbeat.js +286 -26
  68. package/package.json +1 -1
  69. /package/.next/static/{zIIaTqrawBK1MmHg37JOr → pCt9lOHHLoEA8SuY832zV}/_buildManifest.js +0 -0
  70. /package/.next/static/{zIIaTqrawBK1MmHg37JOr → pCt9lOHHLoEA8SuY832zV}/_ssgManifest.js +0 -0
@@ -9406,7 +9406,11 @@ var RestAPIAdapter = class {
9406
9406
  this.headers[key] = this.interpolateEnvVars(value);
9407
9407
  }
9408
9408
  }
9409
- this.fieldMapping = config.field_mapping || {};
9409
+ const raw = config.field_mapping || {};
9410
+ this.fieldMapping = {};
9411
+ for (const [k, v2] of Object.entries(raw)) {
9412
+ if (typeof v2 === "string") this.fieldMapping[k] = v2;
9413
+ }
9410
9414
  this.transforms = config.transforms || {};
9411
9415
  }
9412
9416
  /**
@@ -9496,7 +9500,7 @@ var ManualAdapter = class {
9496
9500
  };
9497
9501
 
9498
9502
  // src/lib/kpi-adapters/posthog.ts
9499
- var PostHogAdapter = class {
9503
+ var PostHogAdapter = class _PostHogAdapter {
9500
9504
  id;
9501
9505
  type = "posthog";
9502
9506
  fieldMapping;
@@ -9527,26 +9531,32 @@ var PostHogAdapter = class {
9527
9531
  const metadata = {};
9528
9532
  const end = endDate || (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
9529
9533
  const start = startDate || new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3).toISOString().split("T")[0];
9530
- const eventNames = Array.from(new Set(Object.values(this.fieldMapping)));
9531
- if (eventNames.length === 0) {
9534
+ const queryMap = /* @__PURE__ */ new Map();
9535
+ for (const [kpiId, mapping] of Object.entries(this.fieldMapping)) {
9536
+ const query = _PostHogAdapter.normalizeMapping(mapping);
9537
+ const key = _PostHogAdapter.queryCacheKey(query);
9538
+ const entry = queryMap.get(key);
9539
+ if (entry) {
9540
+ entry.kpiIds.push(kpiId);
9541
+ } else {
9542
+ queryMap.set(key, { query, kpiIds: [kpiId] });
9543
+ }
9544
+ }
9545
+ if (queryMap.size === 0) {
9532
9546
  return { values, metadata, fetched_at: (/* @__PURE__ */ new Date()).toISOString() };
9533
9547
  }
9534
- for (const eventName of eventNames) {
9548
+ for (const [key, { query, kpiIds }] of Array.from(queryMap.entries())) {
9535
9549
  try {
9536
- const count = await this.fetchEventCount(eventName, start, end);
9537
- metadata[eventName] = { count, start, end };
9538
- for (const [kpiId, mappedEvent] of Object.entries(this.fieldMapping)) {
9539
- if (mappedEvent === eventName) {
9540
- values[kpiId] = count;
9541
- }
9550
+ const count = await this.fetchEventCount(query.event, start, end, query.properties);
9551
+ metadata[key] = { count, start, end, event: query.event, properties: query.properties };
9552
+ for (const kpiId of kpiIds) {
9553
+ values[kpiId] = count;
9542
9554
  }
9543
9555
  } catch (error) {
9544
9556
  const msg = error instanceof Error ? error.message : String(error);
9545
- console.warn(`PostHog: failed to fetch "${eventName}": ${msg}`);
9546
- for (const [kpiId, mappedEvent] of Object.entries(this.fieldMapping)) {
9547
- if (mappedEvent === eventName) {
9548
- values[kpiId] = null;
9549
- }
9557
+ console.warn(`PostHog: failed to fetch "${key}": ${msg}`);
9558
+ for (const kpiId of kpiIds) {
9559
+ values[kpiId] = null;
9550
9560
  }
9551
9561
  }
9552
9562
  }
@@ -9556,6 +9566,21 @@ var PostHogAdapter = class {
9556
9566
  fetched_at: (/* @__PURE__ */ new Date()).toISOString()
9557
9567
  };
9558
9568
  }
9569
+ /** Normalize a field_mapping value (string or object) into an EventQuery. */
9570
+ static normalizeMapping(mapping) {
9571
+ if (typeof mapping === "string") {
9572
+ return { event: mapping };
9573
+ }
9574
+ return { event: mapping.event, properties: mapping.properties };
9575
+ }
9576
+ /** Stable cache key for deduplicating identical queries. */
9577
+ static queryCacheKey(query) {
9578
+ if (!query.properties || Object.keys(query.properties).length === 0) {
9579
+ return query.event;
9580
+ }
9581
+ const sortedProps = Object.entries(query.properties).sort(([a], [b]) => a.localeCompare(b));
9582
+ return `${query.event}[${sortedProps.map(([k, v2]) => `${k}=${v2}`).join(",")}]`;
9583
+ }
9559
9584
  async healthCheck() {
9560
9585
  try {
9561
9586
  const res = await fetch(`${this.host}/api/projects/${this.projectId}/`, {
@@ -9570,17 +9595,25 @@ var PostHogAdapter = class {
9570
9595
  }
9571
9596
  /**
9572
9597
  * Fetch total event count from PostHog Trends API for a given date range.
9598
+ * Optionally filters by event properties (e.g. { command: "init" }).
9573
9599
  */
9574
- async fetchEventCount(eventName, dateFrom, dateTo) {
9600
+ async fetchEventCount(eventName, dateFrom, dateTo, properties) {
9575
9601
  const url = `${this.host}/api/projects/${this.projectId}/insights/trend/`;
9602
+ const eventDef = {
9603
+ id: eventName,
9604
+ math: "total",
9605
+ type: "events"
9606
+ };
9607
+ if (properties && Object.keys(properties).length > 0) {
9608
+ eventDef.properties = Object.entries(properties).map(([key, value]) => ({
9609
+ key,
9610
+ value,
9611
+ operator: "exact",
9612
+ type: "event"
9613
+ }));
9614
+ }
9576
9615
  const body = {
9577
- events: [
9578
- {
9579
- id: eventName,
9580
- math: "total",
9581
- type: "events"
9582
- }
9583
- ],
9616
+ events: [eventDef],
9584
9617
  date_from: dateFrom,
9585
9618
  date_to: dateTo,
9586
9619
  display: "ActionsLineGraph"
@@ -9613,7 +9646,11 @@ var WaitlistFileAdapter = class {
9613
9646
  fieldMapping;
9614
9647
  constructor(config) {
9615
9648
  this.id = config.id;
9616
- this.fieldMapping = config.field_mapping || {};
9649
+ const raw = config.field_mapping || {};
9650
+ this.fieldMapping = {};
9651
+ for (const [k, v2] of Object.entries(raw)) {
9652
+ if (typeof v2 === "string") this.fieldMapping[k] = v2;
9653
+ }
9617
9654
  const relativePath = config.config?.path || "website/data/waitlist.json";
9618
9655
  this.filePath = path.isAbsolute(relativePath) ? relativePath : path.resolve(process.cwd(), relativePath);
9619
9656
  }
@@ -30992,6 +31029,106 @@ Respond in this exact JSON format (no markdown, no code blocks):
30992
31029
  };
30993
31030
  }
30994
31031
  }
31032
+ async function generateWeekPlan(opts) {
31033
+ const positioning = opts.positioning ?? readJsonSafe2(POSITIONING_FILE, null);
31034
+ const today = /* @__PURE__ */ new Date();
31035
+ const dayOfWeek = today.getDay();
31036
+ const daysUntilMonday = dayOfWeek === 0 ? 1 : dayOfWeek === 1 ? 0 : 8 - dayOfWeek;
31037
+ const monday = new Date(today);
31038
+ monday.setDate(today.getDate() + daysUntilMonday);
31039
+ const weekDates = [];
31040
+ const dayNames = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday", "sunday"];
31041
+ for (let i = 0; i < 7; i++) {
31042
+ const d2 = new Date(monday);
31043
+ d2.setDate(monday.getDate() + i);
31044
+ weekDates.push({ day: dayNames[i], date: d2.toISOString().split("T")[0] });
31045
+ }
31046
+ const prompt = `You are a social media strategist for a developer-focused indie hacker audience on X/Twitter.
31047
+
31048
+ ${ALGORITHM_KNOWLEDGE}
31049
+
31050
+ ${HOOK_PATTERNS}
31051
+
31052
+ PRODUCT CONTEXT:
31053
+ - Product: ${positioning?.current?.tagline ?? "VibeBusiness"}
31054
+ - Value prop: ${positioning?.current?.value_proposition ?? "AI-powered business automation"}
31055
+ - Target audience: ${positioning?.current?.target_audience?.primary ?? "Indie hackers and solo founders"}
31056
+
31057
+ CURRENT GOALS:
31058
+ ${opts.goalsContext || "No specific goals set."}
31059
+
31060
+ RECENTLY SHIPPED:
31061
+ ${opts.recentShips || "No recent ships."}
31062
+
31063
+ KPI HIGHLIGHTS:
31064
+ ${opts.kpiHighlights || "No KPI data yet."}
31065
+
31066
+ HISTORICAL PERFORMANCE:
31067
+ ${formatPastPerformance(opts.pastPerformance ?? [])}
31068
+
31069
+ WEEK DATES:
31070
+ ${weekDates.map((d2) => `${d2.day}: ${d2.date}`).join("\n")}
31071
+
31072
+ Plan a full week of content (Monday-Sunday). Rules:
31073
+ - 5 content days (Mon-Fri) + 2 rest days (Sat-Sun)
31074
+ - Include 1-2 threads per week (threads get 200-300% more reach)
31075
+ - Mix content types: ship, viral, thread, milestone, insight, question
31076
+ - Align content with current goals (especially behind-schedule ones)
31077
+ - Use diverse hook patterns \u2014 don't repeat the same hook type consecutively
31078
+ - Write FULL tweet text for each content day (max 280 chars for single tweets)
31079
+ - For threads, provide all tweet texts (5-7 tweets each, max 280 chars per tweet)
31080
+ - Each rest day should have type "rest" with empty content
31081
+
31082
+ Respond in this exact JSON format (no markdown, no code blocks):
31083
+ {
31084
+ "days": [
31085
+ {
31086
+ "day": "monday",
31087
+ "date": "YYYY-MM-DD",
31088
+ "type": "thread",
31089
+ "topic": "topic description",
31090
+ "content": "First tweet / hook text",
31091
+ "thread_tweets": ["tweet 1", "tweet 2", "..."],
31092
+ "hook_type": "story",
31093
+ "rationale": "Why this content on this day"
31094
+ },
31095
+ {
31096
+ "day": "tuesday",
31097
+ "date": "YYYY-MM-DD",
31098
+ "type": "viral",
31099
+ "topic": "ship",
31100
+ "content": "Full tweet text here",
31101
+ "hook_type": "bold-claim",
31102
+ "rationale": "Why this content on this day"
31103
+ }
31104
+ ],
31105
+ "strategy_summary": "One paragraph explaining the week's strategy"
31106
+ }`;
31107
+ const result = await invokeAI({ prompt, model: "sonnet" });
31108
+ try {
31109
+ const parsed = JSON.parse(extractJson(result.output));
31110
+ for (const day of parsed.days) {
31111
+ if (day.type === "rest") continue;
31112
+ if (day.content && day.content.length > 280) {
31113
+ day.content = day.content.slice(0, 277).trimEnd() + "...";
31114
+ }
31115
+ if (day.thread_tweets) {
31116
+ day.thread_tweets = day.thread_tweets.map(
31117
+ (t) => t.length > 280 ? t.slice(0, 277).trimEnd() + "..." : t
31118
+ );
31119
+ }
31120
+ }
31121
+ return {
31122
+ days: parsed.days || [],
31123
+ strategy_summary: parsed.strategy_summary || ""
31124
+ };
31125
+ } catch {
31126
+ return {
31127
+ days: [],
31128
+ strategy_summary: "AI failed to generate a valid week plan."
31129
+ };
31130
+ }
31131
+ }
30995
31132
  function extractJson(text) {
30996
31133
  const codeBlockMatch = text.match(/```(?:json)?\s*([\s\S]*?)```/);
30997
31134
  if (codeBlockMatch) return codeBlockMatch[1].trim();
@@ -31078,6 +31215,7 @@ function createDraft(state, text, source, sourceRef, mediaPaths = null) {
31078
31215
  }
31079
31216
  var SOCIAL_PREFIXES = [
31080
31217
  "social-setup-x",
31218
+ "social-plan-week",
31081
31219
  "social-draft-ship-visual",
31082
31220
  "social-draft-ship",
31083
31221
  "social-draft-update",
@@ -31098,6 +31236,9 @@ async function executeSocialTask(taskId, description, _businessContext) {
31098
31236
  if (taskId === "social-setup-x") {
31099
31237
  return await executeSetupX();
31100
31238
  }
31239
+ if (taskId === "social-plan-week") {
31240
+ return await executePlanWeek();
31241
+ }
31101
31242
  if (taskId === "social-draft-ship-visual") {
31102
31243
  return executeDraftShipVisual();
31103
31244
  }
@@ -31161,6 +31302,10 @@ function readSocialFreshness() {
31161
31302
  bestEngagementRate = best.engagement_rate;
31162
31303
  }
31163
31304
  const threadCount = state.drafts.filter((d2) => d2.is_thread && d2.status === "published").length;
31305
+ const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
31306
+ const scheduledDraftCount = state.drafts.filter(
31307
+ (d2) => d2.status === "draft" && d2.scheduled_date && d2.scheduled_date >= today
31308
+ ).length;
31164
31309
  return {
31165
31310
  configured: state.credentials_valid,
31166
31311
  credentials_valid: state.credentials_valid,
@@ -31172,7 +31317,8 @@ function readSocialFreshness() {
31172
31317
  avg_engagement_rate: avgEngagementRate,
31173
31318
  best_tweet_id: bestTweetId,
31174
31319
  best_engagement_rate: bestEngagementRate,
31175
- thread_count: threadCount
31320
+ thread_count: threadCount,
31321
+ scheduled_draft_count: scheduledDraftCount
31176
31322
  };
31177
31323
  }
31178
31324
  async function executeSetupX() {
@@ -31417,6 +31563,117 @@ async function executeDraftThread(topic, description) {
31417
31563
  ].join("\n")
31418
31564
  };
31419
31565
  }
31566
+ async function executePlanWeek() {
31567
+ const state = loadState3();
31568
+ const pastPerformance = collectEngagementHistory(state);
31569
+ let positioning = null;
31570
+ try {
31571
+ if (fs18.existsSync(POSITIONING_FILE)) {
31572
+ positioning = JSON.parse(fs18.readFileSync(POSITIONING_FILE, "utf-8"));
31573
+ }
31574
+ } catch {
31575
+ }
31576
+ let goalsContext = "";
31577
+ try {
31578
+ if (fs18.existsSync(GOALS_FILE)) {
31579
+ const { goals } = JSON.parse(fs18.readFileSync(GOALS_FILE, "utf-8"));
31580
+ const relevant = goals.filter((g2) => g2.status === "on_track" || g2.status === "at_risk" || g2.status === "behind").map((g2) => {
31581
+ const progress = g2.current_value != null && g2.target_value ? `${Math.round(g2.current_value / g2.target_value * 100)}%` : "unknown";
31582
+ return `- ${g2.title} (${g2.status}, ${progress} complete, deadline: ${g2.deadline})`;
31583
+ });
31584
+ goalsContext = relevant.length > 0 ? relevant.join("\n") : "No active goals.";
31585
+ }
31586
+ } catch {
31587
+ }
31588
+ let recentShips = "";
31589
+ try {
31590
+ if (fs18.existsSync(IDEAS_FILE)) {
31591
+ const { ideas } = JSON.parse(fs18.readFileSync(IDEAS_FILE, "utf-8"));
31592
+ const twoWeeksAgo = new Date(Date.now() - 14 * 24 * 60 * 60 * 1e3).toISOString();
31593
+ const shipped = ideas.filter((i) => i.stage === "shipped" && i.updated_at > twoWeeksAgo).map((i) => `- ${i.title} (shipped ${i.updated_at.split("T")[0]})`);
31594
+ recentShips = shipped.length > 0 ? shipped.join("\n") : "No recent ships.";
31595
+ }
31596
+ } catch {
31597
+ }
31598
+ let kpiHighlights = "";
31599
+ try {
31600
+ if (fs18.existsSync(GOALS_FILE)) {
31601
+ const { goals } = JSON.parse(fs18.readFileSync(GOALS_FILE, "utf-8"));
31602
+ const highlights = [];
31603
+ for (const goal of goals) {
31604
+ for (const kpi of goal.kpis) {
31605
+ if (kpi.current_value != null && kpi.target_value != null) {
31606
+ const pct = Math.round(kpi.current_value / kpi.target_value * 100);
31607
+ highlights.push(`- ${kpi.name}: ${kpi.current_value}/${kpi.target_value} ${kpi.unit} (${pct}%)`);
31608
+ }
31609
+ }
31610
+ }
31611
+ kpiHighlights = highlights.length > 0 ? highlights.join("\n") : "No KPI data.";
31612
+ }
31613
+ } catch {
31614
+ }
31615
+ const plan = await generateWeekPlan({
31616
+ positioning,
31617
+ pastPerformance,
31618
+ goalsContext,
31619
+ recentShips,
31620
+ kpiHighlights
31621
+ });
31622
+ if (plan.days.length === 0) {
31623
+ return { success: false, output: "AI failed to generate a weekly content plan." };
31624
+ }
31625
+ const createdDrafts = [];
31626
+ for (const day of plan.days) {
31627
+ if (day.type === "rest" || !day.content) continue;
31628
+ if (day.type === "thread" && day.thread_tweets && day.thread_tweets.length > 0) {
31629
+ const draft = {
31630
+ id: `draft-${v4_default().slice(0, 8)}`,
31631
+ text: day.thread_tweets[0],
31632
+ source: "thread",
31633
+ source_ref: null,
31634
+ status: "draft",
31635
+ created_at: (/* @__PURE__ */ new Date()).toISOString(),
31636
+ published_at: null,
31637
+ tweet_id: null,
31638
+ tweet_url: null,
31639
+ is_thread: true,
31640
+ thread_tweets: day.thread_tweets,
31641
+ scheduled_date: day.date
31642
+ };
31643
+ state.drafts.push(draft);
31644
+ createdDrafts.push({ day: day.day, date: day.date, type: "thread", id: draft.id });
31645
+ } else {
31646
+ const source = ["ship", "milestone", "update", "digest", "insight"].includes(day.topic) ? day.topic : "insight";
31647
+ const draft = createDraft(state, day.content, source, null);
31648
+ draft.scheduled_date = day.date;
31649
+ createdDrafts.push({ day: day.day, date: day.date, type: day.type, id: draft.id });
31650
+ }
31651
+ }
31652
+ saveState3(state);
31653
+ const monday = plan.days.find((d2) => d2.day === "monday")?.date ?? "";
31654
+ const sunday = plan.days.find((d2) => d2.day === "sunday")?.date ?? "";
31655
+ const dateRange = monday && sunday ? `${formatDateShort(monday)} - ${formatDateShort(sunday)}` : "This Week";
31656
+ const lines = [`Weekly Content Plan (${dateRange})`, ""];
31657
+ for (const day of plan.days) {
31658
+ const dayLabel = day.day.charAt(0).toUpperCase() + day.day.slice(1, 3);
31659
+ if (day.type === "rest") {
31660
+ lines.push(`${dayLabel} -- Rest`);
31661
+ } else {
31662
+ const threadInfo = day.type === "thread" && day.thread_tweets ? ` (${day.thread_tweets.length} tweets)` : "";
31663
+ const preview = day.content.length > 60 ? `"${day.content.slice(0, 57)}..."` : `"${day.content}"`;
31664
+ lines.push(`${dayLabel} [${day.type}] ${preview}${threadInfo}`);
31665
+ lines.push(` Hook: ${day.hook_type} | ${day.rationale}`);
31666
+ }
31667
+ }
31668
+ lines.push("");
31669
+ lines.push(`${createdDrafts.length} drafts created. Review and edit at: vibebusiness start -> /social`);
31670
+ return { success: true, output: lines.join("\n") };
31671
+ }
31672
+ function formatDateShort(isoDate) {
31673
+ const d2 = /* @__PURE__ */ new Date(isoDate + "T00:00:00");
31674
+ const months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
31675
+ return `${months[d2.getMonth()]} ${d2.getDate()}`;
31676
+ }
31420
31677
  async function executeTrackMetrics() {
31421
31678
  const state = loadState3();
31422
31679
  const credentials = loadCredentials();
@@ -31539,6 +31796,7 @@ async function executeCheckStatus3() {
31539
31796
  }
31540
31797
  }
31541
31798
  lines.push(` Pending drafts: ${freshness.draft_count}`);
31799
+ lines.push(` Scheduled drafts: ${freshness.scheduled_draft_count}`);
31542
31800
  lines.push(` Post streak: ${freshness.post_streak_days} day(s)`);
31543
31801
  lines.push(` Total posts: ${freshness.total_posts}`);
31544
31802
  lines.push(` Threads published: ${freshness.thread_count}`);
@@ -34127,6 +34385,7 @@ These tasks generate tweet drafts for the founder's build-in-public journey.
34127
34385
 
34128
34386
  Task prefixes:
34129
34387
  - "social-setup-x" \u2192 Validate X API credentials from .env, save username
34388
+ - "social-plan-week" \u2192 Generate a 7-day content calendar with AI-optimized drafts (5 content days + 2 rest)
34130
34389
  - "social-draft-ship-visual" \u2192 Generate a "just shipped" tweet WITH ship card image or video attached
34131
34390
  - "social-draft-ship" \u2192 Generate a "just shipped" tweet (text only, no media)
34132
34391
  - "social-draft-update" \u2192 Generate a product update draft from positioning data
@@ -34150,6 +34409,7 @@ When to recommend social tasks:
34150
34409
  7. If social.total_posts > 0 AND social.avg_engagement_rate == null \u2192 suggest "social-track-metrics" to start tracking
34151
34410
  8. If social.thread_count == 0 AND social.total_posts >= 3 \u2192 suggest "social-draft-thread-{relevant-topic}" (threads get 200-300% more reach)
34152
34411
  9. Prefer AI-generated drafts (social-draft-viral-*) over template drafts (social-draft-ship/update/milestone) for higher engagement
34412
+ 10. If social.scheduled_draft_count == 0 AND social.configured == true \u2192 suggest "social-plan-week" to plan next week's content calendar
34153
34413
 
34154
34414
  PROMO COPY TASKS:
34155
34415
  The "promo_copy" field shows positioning-driven copy generation status.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibebusiness",
3
- "version": "1.2.49",
3
+ "version": "1.2.51",
4
4
  "description": "AI-powered autonomous product manager. Research. Build. Ship. Grow.",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "private": false,