crewly 1.4.60 → 1.4.62

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 (107) hide show
  1. package/config/skills/agent/content-calendar/execute.sh +2 -2
  2. package/config/skills/agent/marketing/brand-onboarding/SKILL.md +76 -0
  3. package/config/skills/agent/marketing/brand-onboarding/execute.sh +312 -0
  4. package/config/skills/agent/marketing/submit-for-approval/SKILL.md +73 -0
  5. package/config/skills/agent/marketing/submit-for-approval/execute.sh +138 -0
  6. package/config/skills/agent/marketing/weekly-content-planning/SKILL.md +52 -0
  7. package/config/skills/agent/marketing/weekly-content-planning/execute.sh +202 -0
  8. package/config/skills/agent/marketing/weekly-content-planning/execute.test.sh +151 -0
  9. package/config/skills/agent/marketing/weekly-marketing-report/SKILL.md +51 -0
  10. package/config/skills/agent/marketing/weekly-marketing-report/execute.sh +190 -0
  11. package/config/skills/agent/marketing/weekly-marketing-report/execute.test.sh +241 -0
  12. package/config/skills/orchestrator/send-to-remote/SKILL.md +51 -0
  13. package/config/skills/orchestrator/send-to-remote/execute.sh +114 -0
  14. package/config/templates/marketing-team/README.md +42 -0
  15. package/config/templates/marketing-team/goals.md +21 -0
  16. package/config/templates/marketing-team/knowledge/docs/brand-voice-guide.md +61 -0
  17. package/config/templates/marketing-team/knowledge/docs/content-best-practices.md +64 -0
  18. package/config/templates/marketing-team/knowledge/index.json +24 -0
  19. package/config/templates/marketing-team/learned-patterns.json +5 -0
  20. package/config/templates/marketing-team/norms/brand-consistency.md +40 -0
  21. package/config/templates/marketing-team/norms/content-quality-checklist.md +45 -0
  22. package/config/templates/marketing-team/quality-gates.yaml +47 -0
  23. package/config/templates/marketing-team/roles/analyst.md +63 -0
  24. package/config/templates/marketing-team/roles/strategist.md +26 -0
  25. package/config/templates/marketing-team/roles/writer.md +58 -0
  26. package/config/templates/marketing-team/template.json +90 -0
  27. package/config/templates/marketing-team/workflows/weekly-content-cycle.yaml +48 -0
  28. package/dist/backend/backend/src/constants.d.ts +9 -0
  29. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  30. package/dist/backend/backend/src/constants.js +9 -0
  31. package/dist/backend/backend/src/constants.js.map +1 -1
  32. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts +16 -0
  33. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts.map +1 -0
  34. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js +140 -0
  35. package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js.map +1 -0
  36. package/dist/backend/backend/src/controllers/cross-machine/index.d.ts +7 -0
  37. package/dist/backend/backend/src/controllers/cross-machine/index.d.ts.map +1 -0
  38. package/dist/backend/backend/src/controllers/cross-machine/index.js +7 -0
  39. package/dist/backend/backend/src/controllers/cross-machine/index.js.map +1 -0
  40. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  41. package/dist/backend/backend/src/routes/api.routes.js +3 -0
  42. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  43. package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/agent/agent-registration.service.js +46 -6
  45. package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +13 -0
  47. package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
  48. package/dist/backend/backend/src/services/ai/prompt-builder.service.js +50 -5
  49. package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
  50. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts +8 -0
  51. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts.map +1 -1
  52. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js +52 -3
  53. package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js.map +1 -1
  54. package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts +5 -1
  55. package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts.map +1 -1
  56. package/dist/backend/backend/src/services/cloud/relay-client.service.js +14 -2
  57. package/dist/backend/backend/src/services/cloud/relay-client.service.js.map +1 -1
  58. package/dist/backend/backend/src/services/index.d.ts +2 -0
  59. package/dist/backend/backend/src/services/index.d.ts.map +1 -1
  60. package/dist/backend/backend/src/services/index.js +2 -0
  61. package/dist/backend/backend/src/services/index.js.map +1 -1
  62. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts +155 -0
  63. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts.map +1 -0
  64. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js +469 -0
  65. package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js.map +1 -0
  66. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts +107 -0
  67. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts.map +1 -0
  68. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js +104 -0
  69. package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js.map +1 -0
  70. package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts +124 -0
  71. package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts.map +1 -0
  72. package/dist/backend/backend/src/services/onboarding/content-approval.service.js +256 -0
  73. package/dist/backend/backend/src/services/onboarding/content-approval.service.js.map +1 -0
  74. package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts +80 -0
  75. package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts.map +1 -0
  76. package/dist/backend/backend/src/services/onboarding/content-approval.types.js +16 -0
  77. package/dist/backend/backend/src/services/onboarding/content-approval.types.js.map +1 -0
  78. package/dist/backend/backend/src/services/onboarding/index.d.ts +12 -0
  79. package/dist/backend/backend/src/services/onboarding/index.d.ts.map +1 -0
  80. package/dist/backend/backend/src/services/onboarding/index.js +10 -0
  81. package/dist/backend/backend/src/services/onboarding/index.js.map +1 -0
  82. package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts +147 -0
  83. package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts.map +1 -0
  84. package/dist/backend/backend/src/services/slack/cross-machine-message.service.js +306 -0
  85. package/dist/backend/backend/src/services/slack/cross-machine-message.service.js.map +1 -0
  86. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +7 -0
  87. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  88. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +76 -0
  89. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  90. package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
  91. package/dist/backend/backend/src/services/slack/slack.service.js +8 -2
  92. package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
  93. package/dist/backend/backend/src/types/cross-machine.types.d.ts +103 -0
  94. package/dist/backend/backend/src/types/cross-machine.types.d.ts.map +1 -0
  95. package/dist/backend/backend/src/types/cross-machine.types.js +47 -0
  96. package/dist/backend/backend/src/types/cross-machine.types.js.map +1 -0
  97. package/dist/cli/backend/src/constants.d.ts +9 -0
  98. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  99. package/dist/cli/backend/src/constants.js +9 -0
  100. package/dist/cli/backend/src/constants.js.map +1 -1
  101. package/dist/cli/cli/src/commands/cloud.d.ts +94 -0
  102. package/dist/cli/cli/src/commands/cloud.d.ts.map +1 -0
  103. package/dist/cli/cli/src/commands/cloud.js +323 -0
  104. package/dist/cli/cli/src/commands/cloud.js.map +1 -0
  105. package/dist/cli/cli/src/index.js +17 -0
  106. package/dist/cli/cli/src/index.js.map +1 -1
  107. package/package.json +1 -1
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Brand Onboarding Service for Marketing Team templates.
3
+ *
4
+ * Manages the onboarding questionnaire flow that collects brand information
5
+ * from SMB owners and generates a Brand Voice Guide. Supports two channels:
6
+ * - CLI: Interactive readline-based questionnaire
7
+ * - Programmatic: Step-by-step answer submission via API
8
+ *
9
+ * The generated Brand Voice Guide is stored in the team's knowledge directory
10
+ * and used by all marketing agents to maintain brand consistency.
11
+ *
12
+ * @module brand-onboarding-service
13
+ */
14
+ import { BrandProfile, GenerateGuideOptions, OnboardingQuestion, OnboardingSession } from './brand-onboarding.types.js';
15
+ /**
16
+ * Service that manages brand onboarding questionnaire sessions and
17
+ * generates Brand Voice Guides for marketing team templates.
18
+ *
19
+ * Follows the singleton pattern for consistency with other Crewly services.
20
+ */
21
+ export declare class BrandOnboardingService {
22
+ private static instance;
23
+ private sessions;
24
+ private crewlyHome;
25
+ /**
26
+ * Create a new BrandOnboardingService.
27
+ *
28
+ * @param crewlyHome - Path to the .crewly home directory (default: ~/.crewly)
29
+ */
30
+ constructor(crewlyHome?: string);
31
+ /**
32
+ * Get or create the singleton instance.
33
+ *
34
+ * @param crewlyHome - Optional override for .crewly home path
35
+ * @returns Singleton instance
36
+ */
37
+ static getInstance(crewlyHome?: string): BrandOnboardingService;
38
+ /**
39
+ * Reset the singleton (for testing).
40
+ */
41
+ static resetInstance(): void;
42
+ /**
43
+ * Start a new onboarding session for a team.
44
+ *
45
+ * @param teamId - The team this onboarding belongs to
46
+ * @param templateId - The template that triggered onboarding
47
+ * @returns The created onboarding session
48
+ */
49
+ startSession(teamId: string, templateId: string): OnboardingSession;
50
+ /**
51
+ * Get an existing onboarding session.
52
+ *
53
+ * @param sessionId - Session identifier
54
+ * @returns The session or null if not found
55
+ */
56
+ getSession(sessionId: string): OnboardingSession | null;
57
+ /**
58
+ * Get the current question for a session.
59
+ *
60
+ * @param sessionId - Session identifier
61
+ * @returns The current question or null if session complete/not found
62
+ */
63
+ getCurrentQuestion(sessionId: string): OnboardingQuestion | null;
64
+ /**
65
+ * Submit an answer to the current question and advance to the next.
66
+ *
67
+ * @param sessionId - Session identifier
68
+ * @param value - The answer value
69
+ * @returns Updated session with next question index, or null if session not found
70
+ * @throws Error if answer validation fails
71
+ */
72
+ submitAnswer(sessionId: string, value: string): OnboardingSession | null;
73
+ /**
74
+ * Submit all answers at once (batch mode for programmatic use).
75
+ *
76
+ * @param sessionId - Session identifier
77
+ * @param answers - Map of questionId → answer value
78
+ * @returns Updated session
79
+ */
80
+ submitAllAnswers(sessionId: string, answers: Record<string, string>): OnboardingSession | null;
81
+ /**
82
+ * Extract a structured BrandProfile from onboarding answers.
83
+ *
84
+ * @param session - Completed onboarding session with all answers
85
+ * @returns Structured brand profile
86
+ * @throws Error if session is not completed
87
+ */
88
+ extractBrandProfile(session: OnboardingSession): BrandProfile;
89
+ /**
90
+ * Generate the Brand Voice Guide markdown file from a brand profile.
91
+ *
92
+ * This creates a structured markdown document that all marketing agents
93
+ * reference to maintain brand consistency.
94
+ *
95
+ * @param options - Generation options including profile and output path
96
+ * @returns Path to the generated guide file
97
+ */
98
+ generateBrandVoiceGuide(options: GenerateGuideOptions): string;
99
+ /**
100
+ * Complete the onboarding flow: extract profile, generate guide, update session.
101
+ *
102
+ * @param sessionId - Session identifier
103
+ * @param teamKnowledgeDir - Path to the team's knowledge/docs/ directory
104
+ * @returns Updated session with guidePath populated, or null if not found
105
+ */
106
+ completeOnboarding(sessionId: string, teamKnowledgeDir: string): OnboardingSession | null;
107
+ /**
108
+ * Get all questions for display.
109
+ *
110
+ * @returns All onboarding questions in order
111
+ */
112
+ getQuestions(): OnboardingQuestion[];
113
+ /**
114
+ * Build the Brand Voice Guide markdown content from a brand profile.
115
+ *
116
+ * @param profile - Structured brand profile
117
+ * @returns Markdown string for the guide
118
+ */
119
+ private buildGuideMarkdown;
120
+ /**
121
+ * Generate Do's and Don'ts based on brand personality and tone.
122
+ *
123
+ * @param profile - Brand profile
124
+ * @returns Object with dos and donts arrays
125
+ */
126
+ private generateDosAndDonts;
127
+ /**
128
+ * Generate platform-specific content guidelines.
129
+ *
130
+ * @param platforms - List of active platforms
131
+ * @returns Markdown string with platform guidelines
132
+ */
133
+ private generatePlatformGuidelines;
134
+ /**
135
+ * Parse tone string to valid tone type.
136
+ *
137
+ * @param toneStr - Raw tone answer
138
+ * @returns Normalized tone value
139
+ */
140
+ private parseTone;
141
+ /**
142
+ * Persist session to disk for recovery.
143
+ *
144
+ * @param session - Session to save
145
+ */
146
+ private persistSession;
147
+ /**
148
+ * Load a session from disk.
149
+ *
150
+ * @param sessionId - Session to load
151
+ * @returns Loaded session or null
152
+ */
153
+ private loadSession;
154
+ }
155
+ //# sourceMappingURL=brand-onboarding.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-onboarding.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/brand-onboarding.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,OAAO,EACL,YAAY,EAEZ,oBAAoB,EAGpB,kBAAkB,EAClB,iBAAiB,EAElB,MAAM,6BAA6B,CAAC;AAgBrC;;;;;GAKG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAuC;IAC9D,OAAO,CAAC,QAAQ,CAA6C;IAC7D,OAAO,CAAC,UAAU,CAAS;IAE3B;;;;OAIG;gBACS,UAAU,CAAC,EAAE,MAAM;IAO/B;;;;;OAKG;IACH,MAAM,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,sBAAsB;IAO/D;;OAEG;IACH,MAAM,CAAC,aAAa,IAAI,IAAI;IAQ5B;;;;;;OAMG;IACH,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,iBAAiB;IAkBnE;;;;;OAKG;IACH,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAIvD;;;;;OAKG;IACH,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IAOhE;;;;;;;OAOG;IACH,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAmCxE;;;;;;OAMG;IACH,gBAAgB,CACd,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC9B,iBAAiB,GAAG,IAAI;IA6B3B;;;;;;OAMG;IACH,mBAAmB,CAAC,OAAO,EAAE,iBAAiB,GAAG,YAAY;IA2C7D;;;;;;;;OAQG;IACH,uBAAuB,CAAC,OAAO,EAAE,oBAAoB,GAAG,MAAM;IAgB9D;;;;;;OAMG;IACH,kBAAkB,CAChB,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,GACvB,iBAAiB,GAAG,IAAI;IA8B3B;;;;OAIG;IACH,YAAY,IAAI,kBAAkB,EAAE;IAQpC;;;;;OAKG;IACH,OAAO,CAAC,kBAAkB;IAsD1B;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAyC3B;;;;;OAKG;IACH,OAAO,CAAC,0BAA0B;IAoBlC;;;;;OAKG;IACH,OAAO,CAAC,SAAS;IAQjB;;;;OAIG;IACH,OAAO,CAAC,cAAc;IAatB;;;;;OAKG;IACH,OAAO,CAAC,WAAW;CAWpB"}
@@ -0,0 +1,469 @@
1
+ /**
2
+ * Brand Onboarding Service for Marketing Team templates.
3
+ *
4
+ * Manages the onboarding questionnaire flow that collects brand information
5
+ * from SMB owners and generates a Brand Voice Guide. Supports two channels:
6
+ * - CLI: Interactive readline-based questionnaire
7
+ * - Programmatic: Step-by-step answer submission via API
8
+ *
9
+ * The generated Brand Voice Guide is stored in the team's knowledge directory
10
+ * and used by all marketing agents to maintain brand consistency.
11
+ *
12
+ * @module brand-onboarding-service
13
+ */
14
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
15
+ import { join } from 'path';
16
+ import { randomUUID } from 'crypto';
17
+ import { DEFAULT_CONTENT_MIX, ONBOARDING_QUESTIONS, } from './brand-onboarding.types.js';
18
+ // =============================================================================
19
+ // Constants
20
+ // =============================================================================
21
+ /** Directory name for storing onboarding sessions. */
22
+ const ONBOARDING_DIR = 'onboarding';
23
+ /** File name for the generated Brand Voice Guide. */
24
+ const BRAND_VOICE_GUIDE_FILENAME = 'brand-voice-guide.md';
25
+ // =============================================================================
26
+ // Service
27
+ // =============================================================================
28
+ /**
29
+ * Service that manages brand onboarding questionnaire sessions and
30
+ * generates Brand Voice Guides for marketing team templates.
31
+ *
32
+ * Follows the singleton pattern for consistency with other Crewly services.
33
+ */
34
+ export class BrandOnboardingService {
35
+ static instance = null;
36
+ sessions = new Map();
37
+ crewlyHome;
38
+ /**
39
+ * Create a new BrandOnboardingService.
40
+ *
41
+ * @param crewlyHome - Path to the .crewly home directory (default: ~/.crewly)
42
+ */
43
+ constructor(crewlyHome) {
44
+ this.crewlyHome = crewlyHome ?? join(process.env.HOME ?? process.env.USERPROFILE ?? '.', '.crewly');
45
+ }
46
+ /**
47
+ * Get or create the singleton instance.
48
+ *
49
+ * @param crewlyHome - Optional override for .crewly home path
50
+ * @returns Singleton instance
51
+ */
52
+ static getInstance(crewlyHome) {
53
+ if (!BrandOnboardingService.instance) {
54
+ BrandOnboardingService.instance = new BrandOnboardingService(crewlyHome);
55
+ }
56
+ return BrandOnboardingService.instance;
57
+ }
58
+ /**
59
+ * Reset the singleton (for testing).
60
+ */
61
+ static resetInstance() {
62
+ BrandOnboardingService.instance = null;
63
+ }
64
+ // ---------------------------------------------------------------------------
65
+ // Session Management
66
+ // ---------------------------------------------------------------------------
67
+ /**
68
+ * Start a new onboarding session for a team.
69
+ *
70
+ * @param teamId - The team this onboarding belongs to
71
+ * @param templateId - The template that triggered onboarding
72
+ * @returns The created onboarding session
73
+ */
74
+ startSession(teamId, templateId) {
75
+ const session = {
76
+ id: randomUUID(),
77
+ teamId,
78
+ templateId,
79
+ status: 'in_progress',
80
+ answers: [],
81
+ currentQuestionIndex: 0,
82
+ totalQuestions: ONBOARDING_QUESTIONS.length,
83
+ createdAt: new Date().toISOString(),
84
+ updatedAt: new Date().toISOString(),
85
+ };
86
+ this.sessions.set(session.id, session);
87
+ this.persistSession(session);
88
+ return session;
89
+ }
90
+ /**
91
+ * Get an existing onboarding session.
92
+ *
93
+ * @param sessionId - Session identifier
94
+ * @returns The session or null if not found
95
+ */
96
+ getSession(sessionId) {
97
+ return this.sessions.get(sessionId) ?? this.loadSession(sessionId);
98
+ }
99
+ /**
100
+ * Get the current question for a session.
101
+ *
102
+ * @param sessionId - Session identifier
103
+ * @returns The current question or null if session complete/not found
104
+ */
105
+ getCurrentQuestion(sessionId) {
106
+ const session = this.getSession(sessionId);
107
+ if (!session || session.status !== 'in_progress')
108
+ return null;
109
+ if (session.currentQuestionIndex >= ONBOARDING_QUESTIONS.length)
110
+ return null;
111
+ return ONBOARDING_QUESTIONS[session.currentQuestionIndex];
112
+ }
113
+ /**
114
+ * Submit an answer to the current question and advance to the next.
115
+ *
116
+ * @param sessionId - Session identifier
117
+ * @param value - The answer value
118
+ * @returns Updated session with next question index, or null if session not found
119
+ * @throws Error if answer validation fails
120
+ */
121
+ submitAnswer(sessionId, value) {
122
+ const session = this.getSession(sessionId);
123
+ if (!session)
124
+ return null;
125
+ if (session.status !== 'in_progress')
126
+ return session;
127
+ const question = ONBOARDING_QUESTIONS[session.currentQuestionIndex];
128
+ if (!question)
129
+ return session;
130
+ // Validate required fields
131
+ const trimmedValue = value.trim();
132
+ if (question.required && !trimmedValue) {
133
+ throw new Error(`Answer to "${question.text}" is required.`);
134
+ }
135
+ // Record the answer
136
+ const answer = {
137
+ questionId: question.id,
138
+ value: trimmedValue || question.defaultValue || '',
139
+ answeredAt: new Date().toISOString(),
140
+ };
141
+ session.answers.push(answer);
142
+ session.currentQuestionIndex += 1;
143
+ session.updatedAt = new Date().toISOString();
144
+ // Check if all questions answered
145
+ if (session.currentQuestionIndex >= ONBOARDING_QUESTIONS.length) {
146
+ session.status = 'completed';
147
+ }
148
+ this.sessions.set(session.id, session);
149
+ this.persistSession(session);
150
+ return session;
151
+ }
152
+ /**
153
+ * Submit all answers at once (batch mode for programmatic use).
154
+ *
155
+ * @param sessionId - Session identifier
156
+ * @param answers - Map of questionId → answer value
157
+ * @returns Updated session
158
+ */
159
+ submitAllAnswers(sessionId, answers) {
160
+ const session = this.getSession(sessionId);
161
+ if (!session)
162
+ return null;
163
+ for (const question of ONBOARDING_QUESTIONS) {
164
+ const value = answers[question.id] ?? question.defaultValue ?? '';
165
+ if (question.required && !value.trim()) {
166
+ throw new Error(`Answer to "${question.text}" is required.`);
167
+ }
168
+ session.answers.push({
169
+ questionId: question.id,
170
+ value: value.trim(),
171
+ answeredAt: new Date().toISOString(),
172
+ });
173
+ }
174
+ session.currentQuestionIndex = ONBOARDING_QUESTIONS.length;
175
+ session.status = 'completed';
176
+ session.updatedAt = new Date().toISOString();
177
+ this.sessions.set(session.id, session);
178
+ this.persistSession(session);
179
+ return session;
180
+ }
181
+ // ---------------------------------------------------------------------------
182
+ // Brand Profile Extraction
183
+ // ---------------------------------------------------------------------------
184
+ /**
185
+ * Extract a structured BrandProfile from onboarding answers.
186
+ *
187
+ * @param session - Completed onboarding session with all answers
188
+ * @returns Structured brand profile
189
+ * @throws Error if session is not completed
190
+ */
191
+ extractBrandProfile(session) {
192
+ if (session.status !== 'completed' && session.status !== 'generating' && session.status !== 'done') {
193
+ throw new Error('Cannot extract profile from incomplete session.');
194
+ }
195
+ const getAnswer = (questionId) => {
196
+ const answer = session.answers.find(a => a.questionId === questionId);
197
+ return answer?.value ?? '';
198
+ };
199
+ return {
200
+ businessName: getAnswer('business_name'),
201
+ industry: getAnswer('industry'),
202
+ description: getAnswer('description'),
203
+ targetCustomer: getAnswer('target_customer'),
204
+ competitors: getAnswer('competitors')
205
+ .split(/[,;]/)
206
+ .map(s => s.trim())
207
+ .filter(Boolean),
208
+ personality: getAnswer('personality')
209
+ .split(/[,;]/)
210
+ .map(s => s.trim())
211
+ .filter(Boolean),
212
+ tone: this.parseTone(getAnswer('tone')),
213
+ goals: getAnswer('goals')
214
+ .split(/[,;]/)
215
+ .map(s => s.trim())
216
+ .filter(Boolean),
217
+ platforms: getAnswer('platforms')
218
+ .split(/[,;]/)
219
+ .map(s => s.trim())
220
+ .filter(Boolean),
221
+ contentExamples: getAnswer('content_examples')
222
+ .split(/[,;]/)
223
+ .map(s => s.trim())
224
+ .filter(Boolean),
225
+ };
226
+ }
227
+ // ---------------------------------------------------------------------------
228
+ // Brand Voice Guide Generation
229
+ // ---------------------------------------------------------------------------
230
+ /**
231
+ * Generate the Brand Voice Guide markdown file from a brand profile.
232
+ *
233
+ * This creates a structured markdown document that all marketing agents
234
+ * reference to maintain brand consistency.
235
+ *
236
+ * @param options - Generation options including profile and output path
237
+ * @returns Path to the generated guide file
238
+ */
239
+ generateBrandVoiceGuide(options) {
240
+ const { profile, outputDir } = options;
241
+ const guide = this.buildGuideMarkdown(profile);
242
+ // Ensure output directory exists
243
+ if (!existsSync(outputDir)) {
244
+ mkdirSync(outputDir, { recursive: true });
245
+ }
246
+ const guidePath = join(outputDir, BRAND_VOICE_GUIDE_FILENAME);
247
+ writeFileSync(guidePath, guide, 'utf-8');
248
+ return guidePath;
249
+ }
250
+ /**
251
+ * Complete the onboarding flow: extract profile, generate guide, update session.
252
+ *
253
+ * @param sessionId - Session identifier
254
+ * @param teamKnowledgeDir - Path to the team's knowledge/docs/ directory
255
+ * @returns Updated session with guidePath populated, or null if not found
256
+ */
257
+ completeOnboarding(sessionId, teamKnowledgeDir) {
258
+ const session = this.getSession(sessionId);
259
+ if (!session || session.status !== 'completed')
260
+ return null;
261
+ session.status = 'generating';
262
+ session.updatedAt = new Date().toISOString();
263
+ this.sessions.set(session.id, session);
264
+ try {
265
+ const profile = this.extractBrandProfile(session);
266
+ const guidePath = this.generateBrandVoiceGuide({
267
+ profile,
268
+ outputDir: teamKnowledgeDir,
269
+ });
270
+ session.brandVoiceGuide = readFileSync(guidePath, 'utf-8');
271
+ session.guidePath = guidePath;
272
+ session.status = 'done';
273
+ session.updatedAt = new Date().toISOString();
274
+ }
275
+ catch (error) {
276
+ session.status = 'failed';
277
+ session.error = error instanceof Error ? error.message : String(error);
278
+ session.updatedAt = new Date().toISOString();
279
+ }
280
+ this.sessions.set(session.id, session);
281
+ this.persistSession(session);
282
+ return session;
283
+ }
284
+ /**
285
+ * Get all questions for display.
286
+ *
287
+ * @returns All onboarding questions in order
288
+ */
289
+ getQuestions() {
290
+ return [...ONBOARDING_QUESTIONS];
291
+ }
292
+ // ---------------------------------------------------------------------------
293
+ // Private Helpers
294
+ // ---------------------------------------------------------------------------
295
+ /**
296
+ * Build the Brand Voice Guide markdown content from a brand profile.
297
+ *
298
+ * @param profile - Structured brand profile
299
+ * @returns Markdown string for the guide
300
+ */
301
+ buildGuideMarkdown(profile) {
302
+ const dosAndDonts = this.generateDosAndDonts(profile);
303
+ const platformGuidelines = this.generatePlatformGuidelines(profile.platforms);
304
+ return `# Brand Voice Guide — ${profile.businessName}
305
+
306
+ > Auto-generated by Crewly AI Marketing Team onboarding.
307
+ > Last updated: ${new Date().toISOString().split('T')[0]}
308
+
309
+ ## Business Profile
310
+
311
+ - **Name**: ${profile.businessName}
312
+ - **Industry**: ${profile.industry}
313
+ - **Description**: ${profile.description}
314
+ - **Target Customer**: ${profile.targetCustomer}
315
+ - **Competitors**: ${profile.competitors.join(', ') || 'Not specified'}
316
+
317
+ ## Brand Voice
318
+
319
+ - **Personality**: ${profile.personality.join(', ')}
320
+ - **Tone**: ${profile.tone.charAt(0).toUpperCase() + profile.tone.slice(1)}
321
+
322
+ ### Do's
323
+ ${dosAndDonts.dos.map(d => `- ${d}`).join('\n')}
324
+
325
+ ### Don'ts
326
+ ${dosAndDonts.donts.map(d => `- ${d}`).join('\n')}
327
+
328
+ ## Content Strategy
329
+
330
+ - **Goals**: ${profile.goals.join(', ')}
331
+ - **Platforms**: ${profile.platforms.join(', ')}
332
+ - **Content Mix**: ${DEFAULT_CONTENT_MIX.educational}% Educational, ${DEFAULT_CONTENT_MIX.behindTheScenes}% Behind-the-scenes, ${DEFAULT_CONTENT_MIX.promotional}% Promotional, ${DEFAULT_CONTENT_MIX.interactive}% Interactive
333
+
334
+ ${platformGuidelines}
335
+
336
+ ## Style Examples
337
+
338
+ ${profile.contentExamples.length > 0
339
+ ? profile.contentExamples.map(ex => `- ${ex}`).join('\n')
340
+ : '- No examples provided yet. Add references as you discover content you like.'}
341
+
342
+ ## Writing Rules
343
+
344
+ 1. Always write as if you ARE ${profile.businessName}, not about ${profile.businessName}.
345
+ 2. Match the ${profile.tone} tone consistently across all platforms.
346
+ 3. Use the brand personality (${profile.personality.join(', ')}) to guide word choices.
347
+ 4. Never use generic AI cliches like "In today's digital landscape" or "Unlock the power of".
348
+ 5. Every post must have a clear purpose: educate, entertain, sell, or engage.
349
+ 6. Include a call-to-action in at least 50% of posts.
350
+ 7. Use hashtags strategically — 3-5 for Twitter/X, 10-15 for Instagram, 2-3 for LinkedIn.
351
+ `;
352
+ }
353
+ /**
354
+ * Generate Do's and Don'ts based on brand personality and tone.
355
+ *
356
+ * @param profile - Brand profile
357
+ * @returns Object with dos and donts arrays
358
+ */
359
+ generateDosAndDonts(profile) {
360
+ const dos = [];
361
+ const donts = [];
362
+ // Tone-based rules
363
+ switch (profile.tone) {
364
+ case 'formal':
365
+ dos.push('Use complete sentences and proper grammar');
366
+ dos.push('Cite data and statistics to support claims');
367
+ donts.push('Use slang, abbreviations, or internet speak');
368
+ donts.push('Use excessive emojis (max 1 per post)');
369
+ break;
370
+ case 'casual':
371
+ dos.push('Write like you are talking to a friend');
372
+ dos.push('Use contractions and conversational language');
373
+ donts.push('Be overly corporate or stiff');
374
+ donts.push('Use jargon that your audience might not understand');
375
+ break;
376
+ case 'playful':
377
+ dos.push('Use humor, wordplay, and pop culture references');
378
+ dos.push('Include emojis to add personality');
379
+ donts.push('Be serious or dry in tone');
380
+ donts.push('Make jokes that could offend or alienate');
381
+ break;
382
+ case 'authoritative':
383
+ dos.push('Lead with expertise and industry knowledge');
384
+ dos.push('Share original insights and thought leadership');
385
+ donts.push('Hedge or use uncertain language ("maybe", "perhaps")');
386
+ donts.push('Copy competitor messaging or talking points');
387
+ break;
388
+ }
389
+ // Universal rules
390
+ dos.push('Stay consistent with the brand personality across all platforms');
391
+ dos.push('Engage authentically with comments and messages');
392
+ donts.push('Use generic AI-sounding language');
393
+ donts.push('Post identical content across all platforms (adapt per platform)');
394
+ return { dos, donts };
395
+ }
396
+ /**
397
+ * Generate platform-specific content guidelines.
398
+ *
399
+ * @param platforms - List of active platforms
400
+ * @returns Markdown string with platform guidelines
401
+ */
402
+ generatePlatformGuidelines(platforms) {
403
+ const guidelines = ['## Platform Guidelines\n'];
404
+ const platformRules = {
405
+ 'X (Twitter)': '- Max 280 characters. Punchy and engaging.\n- Use threads for longer content.\n- 3-5 relevant hashtags.\n- Best times: 9-11am, 1-3pm weekdays.',
406
+ 'LinkedIn': '- Professional tone. 1200-1500 characters optimal.\n- Start with a hook (first line is critical).\n- 2-3 hashtags max.\n- Best times: Tue-Thu 8-10am.',
407
+ 'Instagram': '- Conversational and visual-first.\n- Include emojis naturally.\n- 10-15 hashtags (mix of popular and niche).\n- Best times: 11am-1pm, 7-9pm.',
408
+ 'Facebook': '- Mix of content lengths.\n- Community-focused — ask questions.\n- 1-2 hashtags max (or none).\n- Best times: 1-4pm weekdays.',
409
+ };
410
+ for (const platform of platforms) {
411
+ const rules = platformRules[platform];
412
+ if (rules) {
413
+ guidelines.push(`### ${platform}\n${rules}\n`);
414
+ }
415
+ }
416
+ return guidelines.join('\n');
417
+ }
418
+ /**
419
+ * Parse tone string to valid tone type.
420
+ *
421
+ * @param toneStr - Raw tone answer
422
+ * @returns Normalized tone value
423
+ */
424
+ parseTone(toneStr) {
425
+ const normalized = toneStr.toLowerCase().trim();
426
+ if (['formal', 'casual', 'playful', 'authoritative'].includes(normalized)) {
427
+ return normalized;
428
+ }
429
+ return 'casual'; // Default fallback
430
+ }
431
+ /**
432
+ * Persist session to disk for recovery.
433
+ *
434
+ * @param session - Session to save
435
+ */
436
+ persistSession(session) {
437
+ try {
438
+ const dir = join(this.crewlyHome, ONBOARDING_DIR);
439
+ if (!existsSync(dir)) {
440
+ mkdirSync(dir, { recursive: true });
441
+ }
442
+ const filePath = join(dir, `${session.id}.json`);
443
+ writeFileSync(filePath, JSON.stringify(session, null, 2), 'utf-8');
444
+ }
445
+ catch {
446
+ // Best-effort persistence — don't crash on write failure
447
+ }
448
+ }
449
+ /**
450
+ * Load a session from disk.
451
+ *
452
+ * @param sessionId - Session to load
453
+ * @returns Loaded session or null
454
+ */
455
+ loadSession(sessionId) {
456
+ try {
457
+ const filePath = join(this.crewlyHome, ONBOARDING_DIR, `${sessionId}.json`);
458
+ if (!existsSync(filePath))
459
+ return null;
460
+ const data = JSON.parse(readFileSync(filePath, 'utf-8'));
461
+ this.sessions.set(data.id, data);
462
+ return data;
463
+ }
464
+ catch {
465
+ return null;
466
+ }
467
+ }
468
+ }
469
+ //# sourceMappingURL=brand-onboarding.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brand-onboarding.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/onboarding/brand-onboarding.service.ts"],"names":[],"mappings":"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"}