vibebusiness 1.2.65 → 1.2.68
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/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-build-manifest.json +19 -19
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -1
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +1 -1
- package/.next/standalone/.next/server/app/api/investor-updates/generate/route.js +1 -1
- package/.next/standalone/.next/server/app/api/investor-updates/generate/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/social/[id]/publish/route.js +1 -1
- package/.next/standalone/.next/server/app/api/social/[id]/publish/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/goals/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/goals/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/hypotheses/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/hypotheses/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/ideas/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/roadmap/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/roadmap/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/sessions/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/settings.html +1 -1
- package/.next/standalone/.next/server/app/settings.rsc +1 -1
- package/.next/standalone/.next/server/app/social/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/social.html +1 -1
- package/.next/standalone/.next/server/app/social.rsc +1 -1
- package/.next/standalone/.next/server/app/updates/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/updates/new/page.js +1 -1
- package/.next/standalone/.next/server/app/updates/new/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/updates/new.html +1 -1
- package/.next/standalone/.next/server/app/updates/new.rsc +2 -2
- package/.next/standalone/.next/server/app/updates/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +13 -13
- package/.next/standalone/.next/server/chunks/{7599.js → 3794.js} +33 -25
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/data/business-context.json +1 -1
- package/.next/standalone/data/goals.json +93 -12
- package/.next/standalone/data/ideas.json +58 -17
- package/.next/standalone/data/implementations.json +53 -0
- package/.next/standalone/data/social.json +49 -1
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/scripts/investor-update.ts +249 -12
- package/.next/standalone/scripts/skills/social-media.ts +239 -34
- package/.next/static/chunks/app/updates/new/page-dcc67ffca587dcc2.js +1 -0
- package/dist/scripts/heartbeat.js +205 -30
- package/dist/scripts/social-routine.js +205 -30
- package/package.json +1 -1
- package/.next/standalone/.next/server/chunks/1685.js +0 -6
- package/.next/standalone/.next/server/chunks/7564.js +0 -81
- package/.next/standalone/scripts/skills/investor-update.ts +0 -476
- package/.next/static/chunks/app/updates/new/page-c518fba0d9ffae98.js +0 -1
- /package/.next/static/{yXi8K8u58qskYid0oHU5f → Gsf15cMKd-7htpbN5eZHd}/_buildManifest.js +0 -0
- /package/.next/static/{yXi8K8u58qskYid0oHU5f → Gsf15cMKd-7htpbN5eZHd}/_ssgManifest.js +0 -0
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
analyzeEngagement,
|
|
59
59
|
type TweetTopic,
|
|
60
60
|
} from '../lib/social/ai-content-generator';
|
|
61
|
-
import type { SocialConfig, SocialDraft, SocialDraftSource, SocialReplyTarget, TweetEngagement, BusinessGoal, PositioningData } from '../../src/lib/types';
|
|
61
|
+
import type { SocialConfig, SocialDraft, SocialDraftSource, SocialReplyTarget, TweetEngagement, BusinessGoal, PositioningData, BusinessIdea } from '../../src/lib/types';
|
|
62
62
|
|
|
63
63
|
// ─── State ───────────────────────────────────────────────────────────────────
|
|
64
64
|
|
|
@@ -222,16 +222,16 @@ export async function executeSocialTask(
|
|
|
222
222
|
return executeDraftShipVisual();
|
|
223
223
|
}
|
|
224
224
|
if (taskId === 'social-draft-ship') {
|
|
225
|
-
return executeDraftShip();
|
|
225
|
+
return await executeDraftShip();
|
|
226
226
|
}
|
|
227
227
|
if (taskId === 'social-draft-update') {
|
|
228
|
-
return executeDraftUpdate();
|
|
228
|
+
return await executeDraftUpdate();
|
|
229
229
|
}
|
|
230
230
|
if (taskId === 'social-draft-milestone') {
|
|
231
|
-
return executeDraftMilestone();
|
|
231
|
+
return await executeDraftMilestone();
|
|
232
232
|
}
|
|
233
233
|
if (taskId === 'social-draft-digest') {
|
|
234
|
-
return executeDraftDigest();
|
|
234
|
+
return await executeDraftDigest();
|
|
235
235
|
}
|
|
236
236
|
if (taskId.startsWith('social-draft-viral-')) {
|
|
237
237
|
const topic = taskId.replace('social-draft-viral-', '') as TweetTopic;
|
|
@@ -672,31 +672,64 @@ async function executeSetupX(): Promise<{ success: boolean; output: string }> {
|
|
|
672
672
|
};
|
|
673
673
|
}
|
|
674
674
|
|
|
675
|
-
function executeDraftShip(): { success: boolean; output: string } {
|
|
675
|
+
async function executeDraftShip(): Promise<{ success: boolean; output: string }> {
|
|
676
676
|
const state = loadState();
|
|
677
|
-
const text = generateShipTweet();
|
|
678
|
-
if (!text) {
|
|
679
|
-
return { success: false, output: 'No shipped ideas found to generate a tweet from.' };
|
|
680
|
-
}
|
|
681
677
|
|
|
682
|
-
//
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
'utf-8'
|
|
686
|
-
|
|
678
|
+
// Load all shipped ideas to find the latest
|
|
679
|
+
let allIdeas: BusinessIdea[] = [];
|
|
680
|
+
try {
|
|
681
|
+
const { ideas } = JSON.parse(fs.readFileSync(IDEAS_FILE, 'utf-8')) as { ideas: BusinessIdea[] };
|
|
682
|
+
allIdeas = ideas;
|
|
683
|
+
} catch {
|
|
684
|
+
return { success: false, output: 'Could not read ideas.json.' };
|
|
685
|
+
}
|
|
687
686
|
|
|
688
|
-
const latestShipped =
|
|
687
|
+
const latestShipped = allIdeas
|
|
689
688
|
.filter((i) => i.stage === 'shipped')
|
|
690
689
|
.sort((a, b) => b.updated_at.localeCompare(a.updated_at))[0];
|
|
691
690
|
|
|
692
|
-
if (latestShipped
|
|
691
|
+
if (!latestShipped) {
|
|
692
|
+
return { success: false, output: 'No shipped ideas found to generate a tweet from.' };
|
|
693
|
+
}
|
|
694
|
+
|
|
695
|
+
if (hasDraftOrPublished(state, latestShipped.id)) {
|
|
693
696
|
return { success: false, output: `Draft already exists for shipped idea ${latestShipped.id}` };
|
|
694
697
|
}
|
|
695
698
|
|
|
696
|
-
|
|
699
|
+
// Build rich context and call AI
|
|
700
|
+
let text: string;
|
|
701
|
+
let hookType: string | undefined;
|
|
702
|
+
let estimatedEngagement: string | undefined;
|
|
703
|
+
|
|
704
|
+
try {
|
|
705
|
+
const context = buildShipContext(latestShipped);
|
|
706
|
+
let positioning: PositioningData | null = null;
|
|
707
|
+
try {
|
|
708
|
+
if (fs.existsSync(POSITIONING_FILE)) {
|
|
709
|
+
positioning = JSON.parse(fs.readFileSync(POSITIONING_FILE, 'utf-8'));
|
|
710
|
+
}
|
|
711
|
+
} catch { /* ignore */ }
|
|
712
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
713
|
+
const result = await generateViralTweet({ topic: 'ship', context, positioning, pastPerformance });
|
|
714
|
+
text = result.text;
|
|
715
|
+
hookType = result.hook_type;
|
|
716
|
+
estimatedEngagement = result.estimated_engagement;
|
|
717
|
+
} catch {
|
|
718
|
+
// Fallback to template
|
|
719
|
+
const fallback = generateShipTweet();
|
|
720
|
+
if (!fallback) {
|
|
721
|
+
return { success: false, output: 'No shipped ideas found to generate a tweet from.' };
|
|
722
|
+
}
|
|
723
|
+
text = fallback;
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
const draft = createDraft(state, text, 'ship', latestShipped.id);
|
|
697
727
|
saveState(state);
|
|
698
728
|
|
|
699
|
-
|
|
729
|
+
const outputParts = [`Created ship draft: "${draft.text}" (${draft.id})`];
|
|
730
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
731
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
732
|
+
return { success: true, output: outputParts.join('\n') };
|
|
700
733
|
}
|
|
701
734
|
|
|
702
735
|
function executeDraftShipVisual(): { success: boolean; output: string } {
|
|
@@ -752,43 +785,195 @@ function executeDraftShipVisual(): { success: boolean; output: string } {
|
|
|
752
785
|
};
|
|
753
786
|
}
|
|
754
787
|
|
|
755
|
-
function executeDraftUpdate(): { success: boolean; output: string } {
|
|
788
|
+
async function executeDraftUpdate(): Promise<{ success: boolean; output: string }> {
|
|
756
789
|
const state = loadState();
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
790
|
+
|
|
791
|
+
let text: string;
|
|
792
|
+
let hookType: string | undefined;
|
|
793
|
+
let estimatedEngagement: string | undefined;
|
|
794
|
+
|
|
795
|
+
try {
|
|
796
|
+
let positioning: PositioningData | null = null;
|
|
797
|
+
try {
|
|
798
|
+
if (fs.existsSync(POSITIONING_FILE)) {
|
|
799
|
+
positioning = JSON.parse(fs.readFileSync(POSITIONING_FILE, 'utf-8'));
|
|
800
|
+
}
|
|
801
|
+
} catch { /* ignore */ }
|
|
802
|
+
|
|
803
|
+
const contextParts: string[] = [];
|
|
804
|
+
if (positioning?.current?.tagline) contextParts.push(`Tagline: ${positioning.current.tagline}`);
|
|
805
|
+
if (positioning?.current?.value_proposition) {
|
|
806
|
+
const s = positioning.current.value_proposition.split(/[.!]/)[0]?.trim();
|
|
807
|
+
if (s) contextParts.push(`Value prop: ${s}`);
|
|
808
|
+
}
|
|
809
|
+
if (positioning?.copy_bank?.pain_points?.length) {
|
|
810
|
+
const pains = positioning.copy_bank.pain_points.slice(0, 2).map((p) => p.pain);
|
|
811
|
+
contextParts.push(`Pain points: ${pains.join(', ')}`);
|
|
812
|
+
}
|
|
813
|
+
if (positioning?.current?.differentiators?.length) {
|
|
814
|
+
contextParts.push(`Differentiator: ${positioning.current.differentiators[0]}`);
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
if (contextParts.length === 0) {
|
|
818
|
+
throw new Error('No positioning context available');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
const context = contextParts.join('\n');
|
|
822
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
823
|
+
const result = await generateViralTweet({ topic: 'update', context, positioning, pastPerformance });
|
|
824
|
+
text = result.text;
|
|
825
|
+
hookType = result.hook_type;
|
|
826
|
+
estimatedEngagement = result.estimated_engagement;
|
|
827
|
+
} catch {
|
|
828
|
+
// Fallback to template
|
|
829
|
+
const fallback = generateProductUpdateTweet();
|
|
830
|
+
if (!fallback) {
|
|
831
|
+
return { success: false, output: 'No positioning data found to generate update tweet.' };
|
|
832
|
+
}
|
|
833
|
+
text = fallback;
|
|
760
834
|
}
|
|
761
835
|
|
|
762
836
|
const draft = createDraft(state, text, 'update', null);
|
|
763
837
|
saveState(state);
|
|
764
838
|
|
|
765
|
-
|
|
839
|
+
const outputParts = [`Created update draft: "${draft.text}" (${draft.id})`];
|
|
840
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
841
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
842
|
+
return { success: true, output: outputParts.join('\n') };
|
|
766
843
|
}
|
|
767
844
|
|
|
768
|
-
function executeDraftMilestone(): { success: boolean; output: string } {
|
|
845
|
+
async function executeDraftMilestone(): Promise<{ success: boolean; output: string }> {
|
|
769
846
|
const state = loadState();
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
847
|
+
|
|
848
|
+
let text: string;
|
|
849
|
+
let hookType: string | undefined;
|
|
850
|
+
let estimatedEngagement: string | undefined;
|
|
851
|
+
|
|
852
|
+
try {
|
|
853
|
+
// Auto-detect KPI that hit or is approaching its target
|
|
854
|
+
let kpiContext = '';
|
|
855
|
+
try {
|
|
856
|
+
if (fs.existsSync(GOALS_FILE)) {
|
|
857
|
+
const { goals } = JSON.parse(fs.readFileSync(GOALS_FILE, 'utf-8')) as { goals: BusinessGoal[] };
|
|
858
|
+
for (const goal of goals) {
|
|
859
|
+
for (const kpi of goal.kpis) {
|
|
860
|
+
if (kpi.current_value != null && kpi.target_value != null) {
|
|
861
|
+
const pct = kpi.current_value / kpi.target_value;
|
|
862
|
+
if (pct >= 0.8) {
|
|
863
|
+
kpiContext = [
|
|
864
|
+
`KPI: ${kpi.name}`,
|
|
865
|
+
`Current: ${kpi.current_value} ${kpi.unit}`,
|
|
866
|
+
`Target: ${kpi.target_value} ${kpi.unit}`,
|
|
867
|
+
`Goal: ${goal.title}`,
|
|
868
|
+
].join('\n');
|
|
869
|
+
break;
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
if (kpiContext) break;
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
} catch { /* ignore */ }
|
|
877
|
+
|
|
878
|
+
if (!kpiContext) {
|
|
879
|
+
throw new Error('No milestone KPI context available');
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
883
|
+
const result = await generateViralTweet({ topic: 'milestone', context: kpiContext, pastPerformance });
|
|
884
|
+
text = result.text;
|
|
885
|
+
hookType = result.hook_type;
|
|
886
|
+
estimatedEngagement = result.estimated_engagement;
|
|
887
|
+
} catch {
|
|
888
|
+
// Fallback to template
|
|
889
|
+
const fallback = generateMilestoneTweet();
|
|
890
|
+
if (!fallback) {
|
|
891
|
+
return { success: false, output: 'No KPI milestones found to celebrate.' };
|
|
892
|
+
}
|
|
893
|
+
text = fallback;
|
|
773
894
|
}
|
|
774
895
|
|
|
775
896
|
const draft = createDraft(state, text, 'milestone', null);
|
|
776
897
|
saveState(state);
|
|
777
898
|
|
|
778
|
-
|
|
899
|
+
const outputParts = [`Created milestone draft: "${draft.text}" (${draft.id})`];
|
|
900
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
901
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
902
|
+
return { success: true, output: outputParts.join('\n') };
|
|
779
903
|
}
|
|
780
904
|
|
|
781
|
-
function executeDraftDigest(): { success: boolean; output: string } {
|
|
905
|
+
async function executeDraftDigest(): Promise<{ success: boolean; output: string }> {
|
|
782
906
|
const state = loadState();
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
907
|
+
|
|
908
|
+
let text: string;
|
|
909
|
+
let hookType: string | undefined;
|
|
910
|
+
let estimatedEngagement: string | undefined;
|
|
911
|
+
|
|
912
|
+
try {
|
|
913
|
+
// Find ideas shipped in the last 7 days
|
|
914
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
915
|
+
let recentShips: Array<{ title: string; category: string }> = [];
|
|
916
|
+
let kpiMovement = '';
|
|
917
|
+
|
|
918
|
+
try {
|
|
919
|
+
if (fs.existsSync(IDEAS_FILE)) {
|
|
920
|
+
const { ideas } = JSON.parse(fs.readFileSync(IDEAS_FILE, 'utf-8')) as { ideas: BusinessIdea[] };
|
|
921
|
+
recentShips = ideas
|
|
922
|
+
.filter((i) => i.stage === 'shipped' && i.updated_at > sevenDaysAgo)
|
|
923
|
+
.sort((a, b) => b.updated_at.localeCompare(a.updated_at))
|
|
924
|
+
.slice(0, 5)
|
|
925
|
+
.map((i) => ({ title: i.title, category: i.category }));
|
|
926
|
+
}
|
|
927
|
+
} catch { /* ignore */ }
|
|
928
|
+
|
|
929
|
+
try {
|
|
930
|
+
if (fs.existsSync(GOALS_FILE)) {
|
|
931
|
+
const { goals } = JSON.parse(fs.readFileSync(GOALS_FILE, 'utf-8')) as { goals: BusinessGoal[] };
|
|
932
|
+
const highlights: string[] = [];
|
|
933
|
+
for (const goal of goals) {
|
|
934
|
+
for (const kpi of goal.kpis) {
|
|
935
|
+
if (kpi.current_value != null && kpi.target_value != null) {
|
|
936
|
+
const pct = Math.round((kpi.current_value / kpi.target_value) * 100);
|
|
937
|
+
highlights.push(`${kpi.name}: ${kpi.current_value}/${kpi.target_value} ${kpi.unit} (${pct}%)`);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
}
|
|
941
|
+
if (highlights.length > 0) kpiMovement = highlights.slice(0, 3).join(', ');
|
|
942
|
+
}
|
|
943
|
+
} catch { /* ignore */ }
|
|
944
|
+
|
|
945
|
+
if (recentShips.length === 0) {
|
|
946
|
+
throw new Error('No recent ships for digest context');
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
const contextParts = [
|
|
950
|
+
`Shipped this week (${recentShips.length} items): ${recentShips.map((s) => s.title).join(', ')}`,
|
|
951
|
+
`Categories: ${Array.from(new Set(recentShips.map((s) => s.category))).join(', ')}`,
|
|
952
|
+
];
|
|
953
|
+
if (kpiMovement) contextParts.push(`KPI movement: ${kpiMovement}`);
|
|
954
|
+
|
|
955
|
+
const context = contextParts.join('\n');
|
|
956
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
957
|
+
const result = await generateViralTweet({ topic: 'update', context, pastPerformance });
|
|
958
|
+
text = result.text;
|
|
959
|
+
hookType = result.hook_type;
|
|
960
|
+
estimatedEngagement = result.estimated_engagement;
|
|
961
|
+
} catch {
|
|
962
|
+
// Fallback to template
|
|
963
|
+
const fallback = generateDigestTweet();
|
|
964
|
+
if (!fallback) {
|
|
965
|
+
return { success: false, output: 'No recent activity to summarize in a digest.' };
|
|
966
|
+
}
|
|
967
|
+
text = fallback;
|
|
786
968
|
}
|
|
787
969
|
|
|
788
970
|
const draft = createDraft(state, text, 'digest', null);
|
|
789
971
|
saveState(state);
|
|
790
972
|
|
|
791
|
-
|
|
973
|
+
const outputParts = [`Created digest draft: "${draft.text}" (${draft.id})`];
|
|
974
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
975
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
976
|
+
return { success: true, output: outputParts.join('\n') };
|
|
792
977
|
}
|
|
793
978
|
|
|
794
979
|
async function executePublish(draftId: string): Promise<{ success: boolean; output: string }> {
|
|
@@ -1544,6 +1729,26 @@ async function executeEngagementReport(): Promise<{ success: boolean; output: st
|
|
|
1544
1729
|
|
|
1545
1730
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
1546
1731
|
|
|
1732
|
+
/**
|
|
1733
|
+
* Build a compact context string from a BusinessIdea for use in AI tweet generation.
|
|
1734
|
+
* Keeps only the first sentence of long fields to stay within Haiku's sweet spot (~300-500 chars).
|
|
1735
|
+
*/
|
|
1736
|
+
function buildShipContext(idea: BusinessIdea): string {
|
|
1737
|
+
const parts: string[] = [`Title: ${idea.title}`];
|
|
1738
|
+
if (idea.context) {
|
|
1739
|
+
const s = idea.context.split(/[.!]/)[0]?.trim();
|
|
1740
|
+
if (s) parts.push(`Background: ${s}`);
|
|
1741
|
+
}
|
|
1742
|
+
if (idea.rationale) {
|
|
1743
|
+
const s = idea.rationale.split(/[.!]/)[0]?.trim();
|
|
1744
|
+
if (s) parts.push(`Why it matters: ${s}`);
|
|
1745
|
+
}
|
|
1746
|
+
if (idea.summary) parts.push(`What was shipped: ${idea.summary}`);
|
|
1747
|
+
if (idea.success_metrics?.[0]) parts.push(`Success target: ${idea.success_metrics[0]}`);
|
|
1748
|
+
if (idea.category) parts.push(`Category: ${idea.category}`);
|
|
1749
|
+
return parts.join('\n');
|
|
1750
|
+
}
|
|
1751
|
+
|
|
1547
1752
|
/**
|
|
1548
1753
|
* Collect the latest engagement snapshot from each published draft.
|
|
1549
1754
|
*/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[940],{1554:function(e,t,s){Promise.resolve().then(s.bind(s,9525))},9205:function(e,t,s){"use strict";s.d(t,{Z:function(){return c}});var a=s(2265);let r=e=>e.replace(/([a-z0-9])([A-Z])/g,"$1-$2").toLowerCase(),n=function(){for(var e=arguments.length,t=Array(e),s=0;s<e;s++)t[s]=arguments[s];return t.filter((e,t,s)=>!!e&&s.indexOf(e)===t).join(" ")};var l={xmlns:"http://www.w3.org/2000/svg",width:24,height:24,viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:2,strokeLinecap:"round",strokeLinejoin:"round"};let i=(0,a.forwardRef)((e,t)=>{let{color:s="currentColor",size:r=24,strokeWidth:i=2,absoluteStrokeWidth:c,className:d="",children:o,iconNode:m,...h}=e;return(0,a.createElement)("svg",{ref:t,...l,width:r,height:r,stroke:s,strokeWidth:c?24*Number(i)/Number(r):i,className:n("lucide",d),...h},[...m.map(e=>{let[t,s]=e;return(0,a.createElement)(t,s)}),...Array.isArray(o)?o:[o]])}),c=(e,t)=>{let s=(0,a.forwardRef)((s,l)=>{let{className:c,...d}=s;return(0,a.createElement)(i,{ref:l,iconNode:t,className:n("lucide-".concat(r(e)),c),...d})});return s.displayName="".concat(e),s}},2660:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("ArrowLeft",[["path",{d:"m12 19-7-7 7-7",key:"1l729n"}],["path",{d:"M19 12H5",key:"x3x0zl"}]])},505:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("Calendar",[["path",{d:"M8 2v4",key:"1cmpym"}],["path",{d:"M16 2v4",key:"4m81vk"}],["rect",{width:"18",height:"18",x:"3",y:"4",rx:"2",key:"1hopcy"}],["path",{d:"M3 10h18",key:"8toen8"}]])},401:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("Check",[["path",{d:"M20 6 9 17l-5-5",key:"1gmf2c"}]])},8867:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("Copy",[["rect",{width:"14",height:"14",x:"8",y:"8",rx:"2",ry:"2",key:"17jyea"}],["path",{d:"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2",key:"zix9uf"}]])},2735:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("Download",[["path",{d:"M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4",key:"ih7n3h"}],["polyline",{points:"7 10 12 15 17 10",key:"2ggqvy"}],["line",{x1:"12",x2:"12",y1:"15",y2:"3",key:"1vk2je"}]])},8736:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("FileText",[["path",{d:"M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z",key:"1rqfz7"}],["path",{d:"M14 2v4a2 2 0 0 0 2 2h4",key:"tnqrlb"}],["path",{d:"M10 9H8",key:"b1mrlr"}],["path",{d:"M16 13H8",key:"t4e002"}],["path",{d:"M16 17H8",key:"z1uh3a"}]])},2023:function(e,t,s){"use strict";s.d(t,{Z:function(){return a}});let a=(0,s(9205).Z)("Sparkles",[["path",{d:"M9.937 15.5A2 2 0 0 0 8.5 14.063l-6.135-1.582a.5.5 0 0 1 0-.962L8.5 9.936A2 2 0 0 0 9.937 8.5l1.582-6.135a.5.5 0 0 1 .963 0L14.063 8.5A2 2 0 0 0 15.5 9.937l6.135 1.581a.5.5 0 0 1 0 .964L15.5 14.063a2 2 0 0 0-1.437 1.437l-1.582 6.135a.5.5 0 0 1-.963 0z",key:"4pj2yx"}],["path",{d:"M20 3v4",key:"1olli1"}],["path",{d:"M22 5h-4",key:"1gvqau"}],["path",{d:"M4 17v2",key:"vumght"}],["path",{d:"M5 18H3",key:"zchphs"}]])},7648:function(e,t,s){"use strict";s.d(t,{default:function(){return r.a}});var a=s(2972),r=s.n(a)},9376:function(e,t,s){"use strict";var a=s(5475);s.o(a,"usePathname")&&s.d(t,{usePathname:function(){return a.usePathname}}),s.o(a,"useRouter")&&s.d(t,{useRouter:function(){return a.useRouter}})},9525:function(e,t,s){"use strict";s.r(t),s.d(t,{default:function(){return u}});var a=s(7437),r=s(2265),n=s(2660),l=s(505),i=s(2023),c=s(8736),d=s(401),o=s(8867),m=s(2735),h=s(7648),x=s(9376);function u(){let e=(0,x.useRouter)(),[t,s]=(0,r.useState)(!1),[u,p]=(0,r.useState)("last_30"),[g,j]=(0,r.useState)(!0),[f,b]=(0,r.useState)(null),[y,w]=(0,r.useState)(!1),[N,v]=(0,r.useState)(null),k=async()=>{s(!0),v(null);try{let e=await fetch("/api/investor-updates/generate",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({dateRange:u,includeAI:g})});if(!e.ok){let t=(await e.json()).error||"Failed to generate update";console.error("API error:",t),v(t);return}let t=await e.json();b(t)}catch(e){console.error("Error generating update:",e),v(e instanceof Error?e.message:"Network error. Please try again.")}finally{s(!1)}},Z=async()=>{f&&(await navigator.clipboard.writeText(f.markdown),w(!0),setTimeout(()=>w(!1),2e3))};return(0,a.jsxs)("div",{className:"min-h-screen bg-slate-50",children:[(0,a.jsx)("div",{className:"bg-white border-b border-slate-200 px-6 py-4",children:(0,a.jsx)("div",{className:"max-w-7xl mx-auto flex items-center justify-between",children:(0,a.jsxs)("div",{className:"flex items-center gap-4",children:[(0,a.jsx)(h.default,{href:"/updates",className:"text-slate-600 hover:text-slate-900 transition-colors",children:(0,a.jsx)(n.Z,{className:"w-5 h-5"})}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h1",{className:"text-xl font-bold text-slate-900",children:"Generate Investor Update"}),(0,a.jsx)("p",{className:"text-sm text-slate-500",children:"Create a compelling monthly update with AI narrative"})]})]})})}),(0,a.jsx)("div",{className:"max-w-7xl mx-auto p-6",children:(0,a.jsxs)("div",{className:"grid grid-cols-1 lg:grid-cols-2 gap-6",children:[(0,a.jsxs)("div",{className:"bg-white rounded-lg border border-slate-200 p-6",children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-900 mb-4",children:"Configuration"}),(0,a.jsxs)("div",{className:"mb-6",children:[(0,a.jsxs)("label",{className:"block text-sm font-medium text-slate-700 mb-2",children:[(0,a.jsx)(l.Z,{className:"w-4 h-4 inline mr-1"}),"Time Period"]}),(0,a.jsxs)("div",{className:"space-y-2",children:[(0,a.jsxs)("label",{className:"flex items-center gap-2 cursor-pointer",children:[(0,a.jsx)("input",{type:"radio",name:"dateRange",value:"last_30",checked:"last_30"===u,onChange:e=>p(e.target.value),className:"w-4 h-4 text-emerald-600"}),(0,a.jsx)("span",{className:"text-sm text-slate-700",children:"Last 30 Days (Monthly)"})]}),(0,a.jsxs)("label",{className:"flex items-center gap-2 cursor-pointer",children:[(0,a.jsx)("input",{type:"radio",name:"dateRange",value:"last_90",checked:"last_90"===u,onChange:e=>p(e.target.value),className:"w-4 h-4 text-emerald-600"}),(0,a.jsx)("span",{className:"text-sm text-slate-700",children:"Last 90 Days (Quarterly)"})]}),(0,a.jsxs)("label",{className:"flex items-center gap-2 cursor-pointer opacity-50",children:[(0,a.jsx)("input",{type:"radio",name:"dateRange",value:"custom",disabled:!0,className:"w-4 h-4 text-emerald-600"}),(0,a.jsx)("span",{className:"text-sm text-slate-700",children:"Custom Range (Coming Soon)"})]})]})]}),(0,a.jsxs)("div",{className:"mb-6",children:[(0,a.jsxs)("label",{className:"flex items-center gap-2 cursor-pointer",children:[(0,a.jsx)("input",{type:"checkbox",checked:g,onChange:e=>j(e.target.checked),className:"w-4 h-4 text-emerald-600 rounded"}),(0,a.jsx)(i.Z,{className:"w-4 h-4 text-emerald-500"}),(0,a.jsx)("span",{className:"text-sm font-medium text-slate-700",children:"Include AI Narrative (Recommended)"})]}),(0,a.jsx)("p",{className:"text-xs text-slate-500 ml-6 mt-1",children:"Generates executive summary, strategic context, and asks rationale"})]}),N&&(0,a.jsxs)("div",{className:"mb-4 p-4 bg-red-50 border border-red-200 rounded-lg",children:[(0,a.jsx)("p",{className:"text-sm text-red-700 font-medium",children:"Error"}),(0,a.jsx)("p",{className:"text-xs text-red-600 mt-1",children:N})]}),(0,a.jsx)("button",{onClick:k,disabled:t,className:"w-full flex items-center justify-center gap-2 px-4 py-3 bg-emerald-600 text-white rounded-lg hover:bg-emerald-700 transition-colors disabled:bg-slate-300 disabled:cursor-not-allowed",children:t?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("div",{className:"w-4 h-4 border-2 border-white border-t-transparent rounded-full animate-spin"}),(0,a.jsx)("span",{children:"Generating..."})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(i.Z,{className:"w-4 h-4"}),(0,a.jsx)("span",{children:"Generate Update"})]})}),(0,a.jsxs)("div",{className:"mt-6 p-4 bg-blue-50 border border-blue-200 rounded-lg",children:[(0,a.jsx)("h3",{className:"text-sm font-semibold text-blue-900 mb-2",children:"What's included:"}),(0,a.jsxs)("ul",{className:"text-xs text-blue-700 space-y-1",children:[(0,a.jsx)("li",{children:"• Completed epics and shipped features"}),(0,a.jsx)("li",{children:"• Goal metrics with month-over-month growth"}),(0,a.jsx)("li",{children:"• Upcoming roadmap with strategic priorities"}),(0,a.jsx)("li",{children:"• Strategic insights from MEMORY.md"}),(0,a.jsx)("li",{children:"• AI-generated narrative (if enabled)"})]})]})]}),(0,a.jsxs)("div",{className:"bg-white rounded-lg border border-slate-200 p-6",children:[(0,a.jsx)("h2",{className:"text-lg font-semibold text-slate-900 mb-4",children:"Preview"}),f?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsxs)("div",{className:"flex items-center gap-2 mb-4",children:[(0,a.jsx)("button",{onClick:Z,className:"flex items-center gap-1 px-3 py-2 text-sm bg-slate-100 hover:bg-slate-200 rounded-lg transition-colors",children:y?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(d.Z,{className:"w-4 h-4 text-emerald-600"}),(0,a.jsx)("span",{children:"Copied!"})]}):(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)(o.Z,{className:"w-4 h-4"}),(0,a.jsx)("span",{children:"Copy Markdown"})]})}),(0,a.jsxs)("button",{onClick:()=>{if(!f)return;let e=new Blob([f.markdown],{type:"text/markdown"}),t=URL.createObjectURL(e),s=document.createElement("a");s.href=t,s.download="investor-update-".concat(new Date().toISOString().split("T")[0],".md"),s.click(),URL.revokeObjectURL(t)},className:"flex items-center gap-1 px-3 py-2 text-sm bg-slate-100 hover:bg-slate-200 rounded-lg transition-colors",children:[(0,a.jsx)(m.Z,{className:"w-4 h-4"}),(0,a.jsx)("span",{children:"Download"})]}),(0,a.jsxs)("button",{onClick:()=>{f&&e.push("/updates/".concat(f.id))},className:"flex items-center gap-1 px-3 py-2 text-sm bg-emerald-600 text-white hover:bg-emerald-700 rounded-lg transition-colors ml-auto",children:[(0,a.jsx)(c.Z,{className:"w-4 h-4"}),(0,a.jsx)("span",{children:"View Details"})]})]}),(0,a.jsx)("div",{className:"prose prose-sm max-w-none overflow-auto max-h-[600px] p-4 bg-slate-50 rounded-lg border border-slate-200",children:(0,a.jsx)("pre",{className:"whitespace-pre-wrap text-xs font-mono",children:f.markdown})})]}):(0,a.jsxs)("div",{className:"flex flex-col items-center justify-center h-64 text-center",children:[(0,a.jsx)(c.Z,{className:"w-12 h-12 text-slate-300 mb-4"}),(0,a.jsxs)("p",{className:"text-sm text-slate-500",children:["Configure your update and click Generate",(0,a.jsx)("br",{}),"to see a preview here"]})]})]})]})})]})}}},function(e){e.O(0,[972,971,117,744],function(){return e(e.s=1554)}),_N_E=e.O()}]);
|
|
@@ -31612,16 +31612,16 @@ async function executeSocialTask(taskId, description, _businessContext) {
|
|
|
31612
31612
|
return executeDraftShipVisual();
|
|
31613
31613
|
}
|
|
31614
31614
|
if (taskId === "social-draft-ship") {
|
|
31615
|
-
return executeDraftShip();
|
|
31615
|
+
return await executeDraftShip();
|
|
31616
31616
|
}
|
|
31617
31617
|
if (taskId === "social-draft-update") {
|
|
31618
|
-
return executeDraftUpdate();
|
|
31618
|
+
return await executeDraftUpdate();
|
|
31619
31619
|
}
|
|
31620
31620
|
if (taskId === "social-draft-milestone") {
|
|
31621
|
-
return executeDraftMilestone();
|
|
31621
|
+
return await executeDraftMilestone();
|
|
31622
31622
|
}
|
|
31623
31623
|
if (taskId === "social-draft-digest") {
|
|
31624
|
-
return executeDraftDigest();
|
|
31624
|
+
return await executeDraftDigest();
|
|
31625
31625
|
}
|
|
31626
31626
|
if (taskId.startsWith("social-draft-viral-")) {
|
|
31627
31627
|
const topic = taskId.replace("social-draft-viral-", "");
|
|
@@ -31946,23 +31946,52 @@ async function executeSetupX() {
|
|
|
31946
31946
|
output: `Connected to X as ${userInfo.username} (${userInfo.followersCount ?? 0} followers)`
|
|
31947
31947
|
};
|
|
31948
31948
|
}
|
|
31949
|
-
function executeDraftShip() {
|
|
31949
|
+
async function executeDraftShip() {
|
|
31950
31950
|
const state = loadState3();
|
|
31951
|
-
|
|
31952
|
-
|
|
31951
|
+
let allIdeas = [];
|
|
31952
|
+
try {
|
|
31953
|
+
const { ideas } = JSON.parse(fs18.readFileSync(IDEAS_FILE, "utf-8"));
|
|
31954
|
+
allIdeas = ideas;
|
|
31955
|
+
} catch {
|
|
31956
|
+
return { success: false, output: "Could not read ideas.json." };
|
|
31957
|
+
}
|
|
31958
|
+
const latestShipped = allIdeas.filter((i) => i.stage === "shipped").sort((a, b) => b.updated_at.localeCompare(a.updated_at))[0];
|
|
31959
|
+
if (!latestShipped) {
|
|
31953
31960
|
return { success: false, output: "No shipped ideas found to generate a tweet from." };
|
|
31954
31961
|
}
|
|
31955
|
-
|
|
31956
|
-
path12.join(path12.dirname(SOCIAL_FILE), "ideas.json"),
|
|
31957
|
-
"utf-8"
|
|
31958
|
-
));
|
|
31959
|
-
const latestShipped = ideas.filter((i) => i.stage === "shipped").sort((a, b) => b.updated_at.localeCompare(a.updated_at))[0];
|
|
31960
|
-
if (latestShipped && hasDraftOrPublished(state, latestShipped.id)) {
|
|
31962
|
+
if (hasDraftOrPublished(state, latestShipped.id)) {
|
|
31961
31963
|
return { success: false, output: `Draft already exists for shipped idea ${latestShipped.id}` };
|
|
31962
31964
|
}
|
|
31963
|
-
|
|
31965
|
+
let text;
|
|
31966
|
+
let hookType;
|
|
31967
|
+
let estimatedEngagement;
|
|
31968
|
+
try {
|
|
31969
|
+
const context = buildShipContext(latestShipped);
|
|
31970
|
+
let positioning = null;
|
|
31971
|
+
try {
|
|
31972
|
+
if (fs18.existsSync(POSITIONING_FILE)) {
|
|
31973
|
+
positioning = JSON.parse(fs18.readFileSync(POSITIONING_FILE, "utf-8"));
|
|
31974
|
+
}
|
|
31975
|
+
} catch {
|
|
31976
|
+
}
|
|
31977
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
31978
|
+
const result = await generateViralTweet({ topic: "ship", context, positioning, pastPerformance });
|
|
31979
|
+
text = result.text;
|
|
31980
|
+
hookType = result.hook_type;
|
|
31981
|
+
estimatedEngagement = result.estimated_engagement;
|
|
31982
|
+
} catch {
|
|
31983
|
+
const fallback = generateShipTweet();
|
|
31984
|
+
if (!fallback) {
|
|
31985
|
+
return { success: false, output: "No shipped ideas found to generate a tweet from." };
|
|
31986
|
+
}
|
|
31987
|
+
text = fallback;
|
|
31988
|
+
}
|
|
31989
|
+
const draft = createDraft(state, text, "ship", latestShipped.id);
|
|
31964
31990
|
saveState3(state);
|
|
31965
|
-
|
|
31991
|
+
const outputParts = [`Created ship draft: "${draft.text}" (${draft.id})`];
|
|
31992
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
31993
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
31994
|
+
return { success: true, output: outputParts.join("\n") };
|
|
31966
31995
|
}
|
|
31967
31996
|
function executeDraftShipVisual() {
|
|
31968
31997
|
const state = loadState3();
|
|
@@ -32003,35 +32032,166 @@ function executeDraftShipVisual() {
|
|
|
32003
32032
|
output: `Created ship draft with media: "${draft.text}" (${draft.id}) \u2014 ${mediaPaths.length} file(s) attached`
|
|
32004
32033
|
};
|
|
32005
32034
|
}
|
|
32006
|
-
function executeDraftUpdate() {
|
|
32035
|
+
async function executeDraftUpdate() {
|
|
32007
32036
|
const state = loadState3();
|
|
32008
|
-
|
|
32009
|
-
|
|
32010
|
-
|
|
32037
|
+
let text;
|
|
32038
|
+
let hookType;
|
|
32039
|
+
let estimatedEngagement;
|
|
32040
|
+
try {
|
|
32041
|
+
let positioning = null;
|
|
32042
|
+
try {
|
|
32043
|
+
if (fs18.existsSync(POSITIONING_FILE)) {
|
|
32044
|
+
positioning = JSON.parse(fs18.readFileSync(POSITIONING_FILE, "utf-8"));
|
|
32045
|
+
}
|
|
32046
|
+
} catch {
|
|
32047
|
+
}
|
|
32048
|
+
const contextParts = [];
|
|
32049
|
+
if (positioning?.current?.tagline) contextParts.push(`Tagline: ${positioning.current.tagline}`);
|
|
32050
|
+
if (positioning?.current?.value_proposition) {
|
|
32051
|
+
const s = positioning.current.value_proposition.split(/[.!]/)[0]?.trim();
|
|
32052
|
+
if (s) contextParts.push(`Value prop: ${s}`);
|
|
32053
|
+
}
|
|
32054
|
+
if (positioning?.copy_bank?.pain_points?.length) {
|
|
32055
|
+
const pains = positioning.copy_bank.pain_points.slice(0, 2).map((p) => p.pain);
|
|
32056
|
+
contextParts.push(`Pain points: ${pains.join(", ")}`);
|
|
32057
|
+
}
|
|
32058
|
+
if (positioning?.current?.differentiators?.length) {
|
|
32059
|
+
contextParts.push(`Differentiator: ${positioning.current.differentiators[0]}`);
|
|
32060
|
+
}
|
|
32061
|
+
if (contextParts.length === 0) {
|
|
32062
|
+
throw new Error("No positioning context available");
|
|
32063
|
+
}
|
|
32064
|
+
const context = contextParts.join("\n");
|
|
32065
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
32066
|
+
const result = await generateViralTweet({ topic: "update", context, positioning, pastPerformance });
|
|
32067
|
+
text = result.text;
|
|
32068
|
+
hookType = result.hook_type;
|
|
32069
|
+
estimatedEngagement = result.estimated_engagement;
|
|
32070
|
+
} catch {
|
|
32071
|
+
const fallback = generateProductUpdateTweet();
|
|
32072
|
+
if (!fallback) {
|
|
32073
|
+
return { success: false, output: "No positioning data found to generate update tweet." };
|
|
32074
|
+
}
|
|
32075
|
+
text = fallback;
|
|
32011
32076
|
}
|
|
32012
32077
|
const draft = createDraft(state, text, "update", null);
|
|
32013
32078
|
saveState3(state);
|
|
32014
|
-
|
|
32079
|
+
const outputParts = [`Created update draft: "${draft.text}" (${draft.id})`];
|
|
32080
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
32081
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
32082
|
+
return { success: true, output: outputParts.join("\n") };
|
|
32015
32083
|
}
|
|
32016
|
-
function executeDraftMilestone() {
|
|
32084
|
+
async function executeDraftMilestone() {
|
|
32017
32085
|
const state = loadState3();
|
|
32018
|
-
|
|
32019
|
-
|
|
32020
|
-
|
|
32086
|
+
let text;
|
|
32087
|
+
let hookType;
|
|
32088
|
+
let estimatedEngagement;
|
|
32089
|
+
try {
|
|
32090
|
+
let kpiContext = "";
|
|
32091
|
+
try {
|
|
32092
|
+
if (fs18.existsSync(GOALS_FILE)) {
|
|
32093
|
+
const { goals } = JSON.parse(fs18.readFileSync(GOALS_FILE, "utf-8"));
|
|
32094
|
+
for (const goal of goals) {
|
|
32095
|
+
for (const kpi of goal.kpis) {
|
|
32096
|
+
if (kpi.current_value != null && kpi.target_value != null) {
|
|
32097
|
+
const pct = kpi.current_value / kpi.target_value;
|
|
32098
|
+
if (pct >= 0.8) {
|
|
32099
|
+
kpiContext = [
|
|
32100
|
+
`KPI: ${kpi.name}`,
|
|
32101
|
+
`Current: ${kpi.current_value} ${kpi.unit}`,
|
|
32102
|
+
`Target: ${kpi.target_value} ${kpi.unit}`,
|
|
32103
|
+
`Goal: ${goal.title}`
|
|
32104
|
+
].join("\n");
|
|
32105
|
+
break;
|
|
32106
|
+
}
|
|
32107
|
+
}
|
|
32108
|
+
}
|
|
32109
|
+
if (kpiContext) break;
|
|
32110
|
+
}
|
|
32111
|
+
}
|
|
32112
|
+
} catch {
|
|
32113
|
+
}
|
|
32114
|
+
if (!kpiContext) {
|
|
32115
|
+
throw new Error("No milestone KPI context available");
|
|
32116
|
+
}
|
|
32117
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
32118
|
+
const result = await generateViralTweet({ topic: "milestone", context: kpiContext, pastPerformance });
|
|
32119
|
+
text = result.text;
|
|
32120
|
+
hookType = result.hook_type;
|
|
32121
|
+
estimatedEngagement = result.estimated_engagement;
|
|
32122
|
+
} catch {
|
|
32123
|
+
const fallback = generateMilestoneTweet();
|
|
32124
|
+
if (!fallback) {
|
|
32125
|
+
return { success: false, output: "No KPI milestones found to celebrate." };
|
|
32126
|
+
}
|
|
32127
|
+
text = fallback;
|
|
32021
32128
|
}
|
|
32022
32129
|
const draft = createDraft(state, text, "milestone", null);
|
|
32023
32130
|
saveState3(state);
|
|
32024
|
-
|
|
32131
|
+
const outputParts = [`Created milestone draft: "${draft.text}" (${draft.id})`];
|
|
32132
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
32133
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
32134
|
+
return { success: true, output: outputParts.join("\n") };
|
|
32025
32135
|
}
|
|
32026
|
-
function executeDraftDigest() {
|
|
32136
|
+
async function executeDraftDigest() {
|
|
32027
32137
|
const state = loadState3();
|
|
32028
|
-
|
|
32029
|
-
|
|
32030
|
-
|
|
32138
|
+
let text;
|
|
32139
|
+
let hookType;
|
|
32140
|
+
let estimatedEngagement;
|
|
32141
|
+
try {
|
|
32142
|
+
const sevenDaysAgo = new Date(Date.now() - 7 * 24 * 60 * 60 * 1e3).toISOString();
|
|
32143
|
+
let recentShips = [];
|
|
32144
|
+
let kpiMovement = "";
|
|
32145
|
+
try {
|
|
32146
|
+
if (fs18.existsSync(IDEAS_FILE)) {
|
|
32147
|
+
const { ideas } = JSON.parse(fs18.readFileSync(IDEAS_FILE, "utf-8"));
|
|
32148
|
+
recentShips = ideas.filter((i) => i.stage === "shipped" && i.updated_at > sevenDaysAgo).sort((a, b) => b.updated_at.localeCompare(a.updated_at)).slice(0, 5).map((i) => ({ title: i.title, category: i.category }));
|
|
32149
|
+
}
|
|
32150
|
+
} catch {
|
|
32151
|
+
}
|
|
32152
|
+
try {
|
|
32153
|
+
if (fs18.existsSync(GOALS_FILE)) {
|
|
32154
|
+
const { goals } = JSON.parse(fs18.readFileSync(GOALS_FILE, "utf-8"));
|
|
32155
|
+
const highlights = [];
|
|
32156
|
+
for (const goal of goals) {
|
|
32157
|
+
for (const kpi of goal.kpis) {
|
|
32158
|
+
if (kpi.current_value != null && kpi.target_value != null) {
|
|
32159
|
+
const pct = Math.round(kpi.current_value / kpi.target_value * 100);
|
|
32160
|
+
highlights.push(`${kpi.name}: ${kpi.current_value}/${kpi.target_value} ${kpi.unit} (${pct}%)`);
|
|
32161
|
+
}
|
|
32162
|
+
}
|
|
32163
|
+
}
|
|
32164
|
+
if (highlights.length > 0) kpiMovement = highlights.slice(0, 3).join(", ");
|
|
32165
|
+
}
|
|
32166
|
+
} catch {
|
|
32167
|
+
}
|
|
32168
|
+
if (recentShips.length === 0) {
|
|
32169
|
+
throw new Error("No recent ships for digest context");
|
|
32170
|
+
}
|
|
32171
|
+
const contextParts = [
|
|
32172
|
+
`Shipped this week (${recentShips.length} items): ${recentShips.map((s) => s.title).join(", ")}`,
|
|
32173
|
+
`Categories: ${Array.from(new Set(recentShips.map((s) => s.category))).join(", ")}`
|
|
32174
|
+
];
|
|
32175
|
+
if (kpiMovement) contextParts.push(`KPI movement: ${kpiMovement}`);
|
|
32176
|
+
const context = contextParts.join("\n");
|
|
32177
|
+
const pastPerformance = collectEngagementHistory(state);
|
|
32178
|
+
const result = await generateViralTweet({ topic: "update", context, pastPerformance });
|
|
32179
|
+
text = result.text;
|
|
32180
|
+
hookType = result.hook_type;
|
|
32181
|
+
estimatedEngagement = result.estimated_engagement;
|
|
32182
|
+
} catch {
|
|
32183
|
+
const fallback = generateDigestTweet();
|
|
32184
|
+
if (!fallback) {
|
|
32185
|
+
return { success: false, output: "No recent activity to summarize in a digest." };
|
|
32186
|
+
}
|
|
32187
|
+
text = fallback;
|
|
32031
32188
|
}
|
|
32032
32189
|
const draft = createDraft(state, text, "digest", null);
|
|
32033
32190
|
saveState3(state);
|
|
32034
|
-
|
|
32191
|
+
const outputParts = [`Created digest draft: "${draft.text}" (${draft.id})`];
|
|
32192
|
+
if (hookType) outputParts.push(`Hook type: ${hookType}`);
|
|
32193
|
+
if (estimatedEngagement) outputParts.push(`Estimated engagement: ${estimatedEngagement}`);
|
|
32194
|
+
return { success: true, output: outputParts.join("\n") };
|
|
32035
32195
|
}
|
|
32036
32196
|
async function executePublish(draftId) {
|
|
32037
32197
|
const state = loadState3();
|
|
@@ -32576,6 +32736,21 @@ async function executeEngagementReport() {
|
|
|
32576
32736
|
}
|
|
32577
32737
|
return { success: true, output: lines.join("\n") };
|
|
32578
32738
|
}
|
|
32739
|
+
function buildShipContext(idea) {
|
|
32740
|
+
const parts = [`Title: ${idea.title}`];
|
|
32741
|
+
if (idea.context) {
|
|
32742
|
+
const s = idea.context.split(/[.!]/)[0]?.trim();
|
|
32743
|
+
if (s) parts.push(`Background: ${s}`);
|
|
32744
|
+
}
|
|
32745
|
+
if (idea.rationale) {
|
|
32746
|
+
const s = idea.rationale.split(/[.!]/)[0]?.trim();
|
|
32747
|
+
if (s) parts.push(`Why it matters: ${s}`);
|
|
32748
|
+
}
|
|
32749
|
+
if (idea.summary) parts.push(`What was shipped: ${idea.summary}`);
|
|
32750
|
+
if (idea.success_metrics?.[0]) parts.push(`Success target: ${idea.success_metrics[0]}`);
|
|
32751
|
+
if (idea.category) parts.push(`Category: ${idea.category}`);
|
|
32752
|
+
return parts.join("\n");
|
|
32753
|
+
}
|
|
32579
32754
|
function collectEngagementHistory(state) {
|
|
32580
32755
|
const result = [];
|
|
32581
32756
|
for (const draft of state.drafts) {
|