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.
- package/config/skills/agent/content-calendar/execute.sh +2 -2
- package/config/skills/agent/marketing/brand-onboarding/SKILL.md +76 -0
- package/config/skills/agent/marketing/brand-onboarding/execute.sh +312 -0
- package/config/skills/agent/marketing/submit-for-approval/SKILL.md +73 -0
- package/config/skills/agent/marketing/submit-for-approval/execute.sh +138 -0
- package/config/skills/agent/marketing/weekly-content-planning/SKILL.md +52 -0
- package/config/skills/agent/marketing/weekly-content-planning/execute.sh +202 -0
- package/config/skills/agent/marketing/weekly-content-planning/execute.test.sh +151 -0
- package/config/skills/agent/marketing/weekly-marketing-report/SKILL.md +51 -0
- package/config/skills/agent/marketing/weekly-marketing-report/execute.sh +190 -0
- package/config/skills/agent/marketing/weekly-marketing-report/execute.test.sh +241 -0
- package/config/skills/orchestrator/send-to-remote/SKILL.md +51 -0
- package/config/skills/orchestrator/send-to-remote/execute.sh +114 -0
- package/config/templates/marketing-team/README.md +42 -0
- package/config/templates/marketing-team/goals.md +21 -0
- package/config/templates/marketing-team/knowledge/docs/brand-voice-guide.md +61 -0
- package/config/templates/marketing-team/knowledge/docs/content-best-practices.md +64 -0
- package/config/templates/marketing-team/knowledge/index.json +24 -0
- package/config/templates/marketing-team/learned-patterns.json +5 -0
- package/config/templates/marketing-team/norms/brand-consistency.md +40 -0
- package/config/templates/marketing-team/norms/content-quality-checklist.md +45 -0
- package/config/templates/marketing-team/quality-gates.yaml +47 -0
- package/config/templates/marketing-team/roles/analyst.md +63 -0
- package/config/templates/marketing-team/roles/strategist.md +26 -0
- package/config/templates/marketing-team/roles/writer.md +58 -0
- package/config/templates/marketing-team/template.json +90 -0
- package/config/templates/marketing-team/workflows/weekly-content-cycle.yaml +48 -0
- package/dist/backend/backend/src/constants.d.ts +9 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +9 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts +16 -0
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js +140 -0
- package/dist/backend/backend/src/controllers/cross-machine/cross-machine.controller.js.map +1 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.d.ts +7 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.js +7 -0
- package/dist/backend/backend/src/controllers/cross-machine/index.js.map +1 -0
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +46 -6
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts +13 -0
- package/dist/backend/backend/src/services/ai/prompt-builder.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js +50 -5
- package/dist/backend/backend/src/services/ai/prompt-builder.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts +8 -0
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js +52 -3
- package/dist/backend/backend/src/services/cloud/device-auto-discovery.service.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts +5 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/relay-client.service.js +14 -2
- package/dist/backend/backend/src/services/cloud/relay-client.service.js.map +1 -1
- package/dist/backend/backend/src/services/index.d.ts +2 -0
- package/dist/backend/backend/src/services/index.d.ts.map +1 -1
- package/dist/backend/backend/src/services/index.js +2 -0
- package/dist/backend/backend/src/services/index.js.map +1 -1
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts +155 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js +469 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.service.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts +107 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js +104 -0
- package/dist/backend/backend/src/services/onboarding/brand-onboarding.types.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts +124 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.js +256 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.service.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts +80 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.js +16 -0
- package/dist/backend/backend/src/services/onboarding/content-approval.types.js.map +1 -0
- package/dist/backend/backend/src/services/onboarding/index.d.ts +12 -0
- package/dist/backend/backend/src/services/onboarding/index.d.ts.map +1 -0
- package/dist/backend/backend/src/services/onboarding/index.js +10 -0
- package/dist/backend/backend/src/services/onboarding/index.js.map +1 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts +147 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.js +306 -0
- package/dist/backend/backend/src/services/slack/cross-machine-message.service.js.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts +7 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +76 -0
- package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack.service.js +8 -2
- package/dist/backend/backend/src/services/slack/slack.service.js.map +1 -1
- package/dist/backend/backend/src/types/cross-machine.types.d.ts +103 -0
- package/dist/backend/backend/src/types/cross-machine.types.d.ts.map +1 -0
- package/dist/backend/backend/src/types/cross-machine.types.js +47 -0
- package/dist/backend/backend/src/types/cross-machine.types.js.map +1 -0
- package/dist/cli/backend/src/constants.d.ts +9 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +9 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/cli/src/commands/cloud.d.ts +94 -0
- package/dist/cli/cli/src/commands/cloud.d.ts.map +1 -0
- package/dist/cli/cli/src/commands/cloud.js +323 -0
- package/dist/cli/cli/src/commands/cloud.js.map +1 -0
- package/dist/cli/cli/src/index.js +17 -0
- package/dist/cli/cli/src/index.js.map +1 -1
- 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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"}
|