crewly 1.4.61 → 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/package.json +1 -1
|
@@ -58,9 +58,9 @@ case "$ACTION" in
|
|
|
58
58
|
require_param "scheduledDate" "$SCHEDULED_DATE"
|
|
59
59
|
|
|
60
60
|
# Validate platform
|
|
61
|
-
VALID_PLATFORMS="x|linkedin|xiaohongshu|substack|youtube|github|reddit"
|
|
61
|
+
VALID_PLATFORMS="x|linkedin|xiaohongshu|substack|youtube|github|reddit|instagram|facebook"
|
|
62
62
|
if ! echo "$PLATFORM" | grep -qE "^(${VALID_PLATFORMS})$"; then
|
|
63
|
-
error_exit "Invalid platform: $PLATFORM. Valid: x, linkedin, xiaohongshu, substack, youtube, github, reddit"
|
|
63
|
+
error_exit "Invalid platform: $PLATFORM. Valid: x, linkedin, instagram, facebook, xiaohongshu, substack, youtube, github, reddit"
|
|
64
64
|
fi
|
|
65
65
|
|
|
66
66
|
# Validate status
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: brand-onboarding
|
|
3
|
+
description: Interactive brand onboarding questionnaire that collects business information and generates a Brand Voice Guide for marketing team agents.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: marketing
|
|
6
|
+
assignableRoles:
|
|
7
|
+
- strategist
|
|
8
|
+
- orchestrator
|
|
9
|
+
triggers:
|
|
10
|
+
- on_team_create
|
|
11
|
+
- manual
|
|
12
|
+
tags:
|
|
13
|
+
- marketing
|
|
14
|
+
- onboarding
|
|
15
|
+
- brand
|
|
16
|
+
- setup
|
|
17
|
+
execution:
|
|
18
|
+
type: script
|
|
19
|
+
script: execute.sh
|
|
20
|
+
interpreter: bash
|
|
21
|
+
timeout: 60000
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
# Brand Onboarding
|
|
25
|
+
|
|
26
|
+
Collects brand information from the business owner and generates a Brand Voice Guide
|
|
27
|
+
that all marketing team agents use to maintain brand consistency.
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
### Start onboarding session
|
|
32
|
+
```bash
|
|
33
|
+
bash execute.sh '{"action":"start","teamId":"team-123","templateId":"marketing-team"}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Submit an answer (step-by-step)
|
|
37
|
+
```bash
|
|
38
|
+
bash execute.sh '{"action":"answer","sessionId":"session-uuid","value":"Sunrise Bakery"}'
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Submit all answers at once (batch mode)
|
|
42
|
+
```bash
|
|
43
|
+
bash execute.sh '{"action":"batch","teamId":"team-123","templateId":"marketing-team","answers":{"business_name":"Sunrise Bakery","industry":"Food","description":"Artisan bakery","target_customer":"Health-conscious millennials","competitors":"Blue Apron, HelloFresh","personality":"Warm, Authentic, Passionate","tone":"casual","goals":"Brand awareness, Community building","platforms":"Instagram, X (Twitter)","content_examples":"https://example.com"}}'
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Complete onboarding (generate guide)
|
|
47
|
+
```bash
|
|
48
|
+
bash execute.sh '{"action":"complete","sessionId":"session-uuid","outputDir":"/path/to/knowledge/docs"}'
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Get current question
|
|
52
|
+
```bash
|
|
53
|
+
bash execute.sh '{"action":"question","sessionId":"session-uuid"}'
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Questions (10 total)
|
|
57
|
+
|
|
58
|
+
1. Business name
|
|
59
|
+
2. Industry
|
|
60
|
+
3. One-sentence business description
|
|
61
|
+
4. Target customer profile
|
|
62
|
+
5. Top 3 competitors
|
|
63
|
+
6. Brand personality (3 words)
|
|
64
|
+
7. Content tone (Formal/Casual/Playful/Authoritative)
|
|
65
|
+
8. Marketing goals
|
|
66
|
+
9. Social platforms
|
|
67
|
+
10. Content examples
|
|
68
|
+
|
|
69
|
+
## Output
|
|
70
|
+
|
|
71
|
+
Generates `brand-voice-guide.md` in the specified output directory with:
|
|
72
|
+
- Business Profile section
|
|
73
|
+
- Brand Voice (personality, tone, do's, don'ts)
|
|
74
|
+
- Content Strategy (goals, platforms, content mix ratios)
|
|
75
|
+
- Platform-specific Guidelines
|
|
76
|
+
- Writing Rules
|
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# brand-onboarding — Interactive brand questionnaire & voice guide generator
|
|
4
|
+
#
|
|
5
|
+
# Manages the onboarding session via the Crewly API's BrandOnboardingService.
|
|
6
|
+
# Falls back to local file-based operation if API is not available.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash execute.sh '{"action":"start","teamId":"team-123","templateId":"marketing-team"}'
|
|
10
|
+
# bash execute.sh '{"action":"answer","sessionId":"uuid","value":"Sunrise Bakery"}'
|
|
11
|
+
# bash execute.sh '{"action":"batch","teamId":"team-123","answers":{...}}'
|
|
12
|
+
# bash execute.sh '{"action":"complete","sessionId":"uuid","outputDir":"/path/to/docs"}'
|
|
13
|
+
# bash execute.sh '{"action":"question","sessionId":"uuid"}'
|
|
14
|
+
# =============================================================================
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
INPUT="${1:-"{}"}"
|
|
19
|
+
|
|
20
|
+
# Parse input fields
|
|
21
|
+
ACTION=$(echo "$INPUT" | jq -r '.action // "start"')
|
|
22
|
+
TEAM_ID=$(echo "$INPUT" | jq -r '.teamId // ""')
|
|
23
|
+
TEMPLATE_ID=$(echo "$INPUT" | jq -r '.templateId // "marketing-team"')
|
|
24
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // ""')
|
|
25
|
+
VALUE=$(echo "$INPUT" | jq -r '.value // ""')
|
|
26
|
+
OUTPUT_DIR=$(echo "$INPUT" | jq -r '.outputDir // ""')
|
|
27
|
+
PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // ""')
|
|
28
|
+
|
|
29
|
+
# Crewly API base URL
|
|
30
|
+
API_BASE="${CREWLY_API_URL:-http://localhost:3000}"
|
|
31
|
+
|
|
32
|
+
# Local storage directory
|
|
33
|
+
LOCAL_DIR="${PROJECT_PATH:-.}/.crewly/onboarding"
|
|
34
|
+
|
|
35
|
+
# =============================================================================
|
|
36
|
+
# Questions definition (matches BrandOnboardingService)
|
|
37
|
+
# =============================================================================
|
|
38
|
+
QUESTIONS='[
|
|
39
|
+
{"id":"business_name","text":"What is your business name?","required":true,"order":1},
|
|
40
|
+
{"id":"industry","text":"What industry are you in? (e.g., Fashion, Food, Fitness, Tech, Education)","required":true,"order":2},
|
|
41
|
+
{"id":"description","text":"Describe your business in one sentence.","required":true,"order":3},
|
|
42
|
+
{"id":"target_customer","text":"Who is your target customer? (Age, interests, problems they have)","required":true,"order":4},
|
|
43
|
+
{"id":"competitors","text":"What are your top 3 competitors?","required":false,"order":5},
|
|
44
|
+
{"id":"personality","text":"Describe your brand personality in 3 words. (e.g., Professional, Friendly, Bold)","required":true,"order":6},
|
|
45
|
+
{"id":"tone","text":"What tone should your content have? (Formal / Casual / Playful / Authoritative)","required":true,"order":7},
|
|
46
|
+
{"id":"goals","text":"What are your marketing goals? (Brand awareness / Lead generation / Sales / Community)","required":true,"order":8},
|
|
47
|
+
{"id":"platforms","text":"Which platforms do you use? (X / LinkedIn / Instagram / Facebook)","required":true,"order":9},
|
|
48
|
+
{"id":"content_examples","text":"Share 2-3 examples of content you like (URLs or descriptions).","required":false,"order":10}
|
|
49
|
+
]'
|
|
50
|
+
|
|
51
|
+
# =============================================================================
|
|
52
|
+
# Helper: generate brand voice guide from answers
|
|
53
|
+
# =============================================================================
|
|
54
|
+
generate_guide() {
|
|
55
|
+
local ANSWERS_FILE="$1"
|
|
56
|
+
local OUT_DIR="$2"
|
|
57
|
+
|
|
58
|
+
# Extract answers
|
|
59
|
+
local BIZ_NAME=$(jq -r '.business_name // "Your Business"' "$ANSWERS_FILE")
|
|
60
|
+
local INDUSTRY=$(jq -r '.industry // "General"' "$ANSWERS_FILE")
|
|
61
|
+
local DESCRIPTION=$(jq -r '.description // ""' "$ANSWERS_FILE")
|
|
62
|
+
local TARGET=$(jq -r '.target_customer // ""' "$ANSWERS_FILE")
|
|
63
|
+
local COMPETITORS=$(jq -r '.competitors // "Not specified"' "$ANSWERS_FILE")
|
|
64
|
+
local PERSONALITY=$(jq -r '.personality // "Professional, Friendly"' "$ANSWERS_FILE")
|
|
65
|
+
local TONE=$(jq -r '.tone // "casual"' "$ANSWERS_FILE")
|
|
66
|
+
local GOALS=$(jq -r '.goals // "Brand awareness"' "$ANSWERS_FILE")
|
|
67
|
+
local PLATFORMS=$(jq -r '.platforms // "Instagram"' "$ANSWERS_FILE")
|
|
68
|
+
local EXAMPLES=$(jq -r '.content_examples // "Not specified"' "$ANSWERS_FILE")
|
|
69
|
+
|
|
70
|
+
# Generate tone-specific rules
|
|
71
|
+
local DOS=""
|
|
72
|
+
local DONTS=""
|
|
73
|
+
case "$(echo "$TONE" | tr '[:upper:]' '[:lower:]')" in
|
|
74
|
+
formal)
|
|
75
|
+
DOS="- Use complete sentences and proper grammar\n- Cite data and statistics to support claims"
|
|
76
|
+
DONTS="- Use slang, abbreviations, or internet speak\n- Use excessive emojis (max 1 per post)"
|
|
77
|
+
;;
|
|
78
|
+
playful)
|
|
79
|
+
DOS="- Use humor, wordplay, and pop culture references\n- Include emojis to add personality"
|
|
80
|
+
DONTS="- Be serious or dry in tone\n- Make jokes that could offend or alienate"
|
|
81
|
+
;;
|
|
82
|
+
authoritative)
|
|
83
|
+
DOS="- Lead with expertise and industry knowledge\n- Share original insights and thought leadership"
|
|
84
|
+
DONTS="- Hedge or use uncertain language (\"maybe\", \"perhaps\")\n- Copy competitor messaging or talking points"
|
|
85
|
+
;;
|
|
86
|
+
*)
|
|
87
|
+
DOS="- Write like you are talking to a friend\n- Use contractions and conversational language"
|
|
88
|
+
DONTS="- Be overly corporate or stiff\n- Use jargon that your audience might not understand"
|
|
89
|
+
;;
|
|
90
|
+
esac
|
|
91
|
+
DOS="${DOS}\n- Stay consistent with the brand personality across all platforms\n- Engage authentically with comments and messages"
|
|
92
|
+
DONTS="${DONTS}\n- Use generic AI-sounding language\n- Post identical content across all platforms (adapt per platform)"
|
|
93
|
+
|
|
94
|
+
mkdir -p "$OUT_DIR"
|
|
95
|
+
|
|
96
|
+
# Capitalize first letter of tone (compatible with bash 3.x)
|
|
97
|
+
TONE_CAP="$(echo "$TONE" | awk '{print toupper(substr($0,1,1)) tolower(substr($0,2))}')"
|
|
98
|
+
|
|
99
|
+
# Write guide using printf to avoid heredoc substitution issues
|
|
100
|
+
{
|
|
101
|
+
printf '# Brand Voice Guide — %s\n\n' "$BIZ_NAME"
|
|
102
|
+
printf '> Auto-generated by Crewly AI Marketing Team onboarding.\n'
|
|
103
|
+
printf '> Last updated: %s\n\n' "$(date +%Y-%m-%d)"
|
|
104
|
+
printf '## Business Profile\n\n'
|
|
105
|
+
printf '%s\n' "- **Name**: $BIZ_NAME"
|
|
106
|
+
printf '%s\n' "- **Industry**: $INDUSTRY"
|
|
107
|
+
printf '%s\n' "- **Description**: $DESCRIPTION"
|
|
108
|
+
printf '%s\n' "- **Target Customer**: $TARGET"
|
|
109
|
+
printf '%s\n\n' "- **Competitors**: $COMPETITORS"
|
|
110
|
+
printf '## Brand Voice\n\n'
|
|
111
|
+
printf '%s\n' "- **Personality**: $PERSONALITY"
|
|
112
|
+
printf '%s\n\n' "- **Tone**: $TONE_CAP"
|
|
113
|
+
printf "### Do's\n"
|
|
114
|
+
printf '%b\n\n' "$DOS"
|
|
115
|
+
printf "### Don'ts\n"
|
|
116
|
+
printf '%b\n\n' "$DONTS"
|
|
117
|
+
printf '## Content Strategy\n\n'
|
|
118
|
+
printf '%s\n' "- **Goals**: $GOALS"
|
|
119
|
+
printf '%s\n' "- **Platforms**: $PLATFORMS"
|
|
120
|
+
printf '%s\n\n' "- **Content Mix**: 60% Educational, 20% Behind-the-scenes, 15% Promotional, 5% Interactive"
|
|
121
|
+
printf '## Writing Rules\n\n'
|
|
122
|
+
printf '1. Always write as if you ARE %s, not about %s.\n' "$BIZ_NAME" "$BIZ_NAME"
|
|
123
|
+
printf '2. Match the %s tone consistently across all platforms.\n' "$TONE"
|
|
124
|
+
printf '3. Use the brand personality (%s) to guide word choices.\n' "$PERSONALITY"
|
|
125
|
+
printf '4. Never use generic AI cliches like "In today'\''s digital landscape" or "Unlock the power of".\n'
|
|
126
|
+
printf '5. Every post must have a clear purpose: educate, entertain, sell, or engage.\n'
|
|
127
|
+
printf '6. Include a call-to-action in at least 50%% of posts.\n'
|
|
128
|
+
printf '7. Use hashtags strategically — 3-5 for Twitter/X, 10-15 for Instagram, 2-3 for LinkedIn.\n\n'
|
|
129
|
+
printf '## Style Examples\n\n'
|
|
130
|
+
echo "$EXAMPLES" | tr ',' '\n' | sed 's/^ */- /'
|
|
131
|
+
} > "${OUT_DIR}/brand-voice-guide.md"
|
|
132
|
+
|
|
133
|
+
echo "${OUT_DIR}/brand-voice-guide.md"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
# =============================================================================
|
|
137
|
+
# Actions
|
|
138
|
+
# =============================================================================
|
|
139
|
+
|
|
140
|
+
case "$ACTION" in
|
|
141
|
+
start)
|
|
142
|
+
if [ -z "$TEAM_ID" ]; then
|
|
143
|
+
echo '{"success":false,"error":"teamId is required"}'
|
|
144
|
+
exit 1
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
SESSION_ID="onboard-$(date +%s)-$RANDOM"
|
|
148
|
+
mkdir -p "$LOCAL_DIR"
|
|
149
|
+
|
|
150
|
+
# Create session file
|
|
151
|
+
jq -n \
|
|
152
|
+
--arg id "$SESSION_ID" \
|
|
153
|
+
--arg teamId "$TEAM_ID" \
|
|
154
|
+
--arg templateId "$TEMPLATE_ID" \
|
|
155
|
+
--argjson questions "$QUESTIONS" \
|
|
156
|
+
'{
|
|
157
|
+
id: $id,
|
|
158
|
+
teamId: $teamId,
|
|
159
|
+
templateId: $templateId,
|
|
160
|
+
status: "in_progress",
|
|
161
|
+
currentQuestionIndex: 0,
|
|
162
|
+
totalQuestions: ($questions | length),
|
|
163
|
+
answers: {},
|
|
164
|
+
createdAt: (now | todate)
|
|
165
|
+
}' > "${LOCAL_DIR}/${SESSION_ID}.json"
|
|
166
|
+
|
|
167
|
+
FIRST_Q=$(echo "$QUESTIONS" | jq -r '.[0].text')
|
|
168
|
+
|
|
169
|
+
echo "{\"success\":true,\"sessionId\":\"${SESSION_ID}\",\"status\":\"in_progress\",\"totalQuestions\":10,\"currentQuestion\":1,\"questionText\":\"${FIRST_Q}\"}"
|
|
170
|
+
;;
|
|
171
|
+
|
|
172
|
+
question)
|
|
173
|
+
if [ -z "$SESSION_ID" ]; then
|
|
174
|
+
echo '{"success":false,"error":"sessionId is required"}'
|
|
175
|
+
exit 1
|
|
176
|
+
fi
|
|
177
|
+
|
|
178
|
+
SESSION_FILE="${LOCAL_DIR}/${SESSION_ID}.json"
|
|
179
|
+
if [ ! -f "$SESSION_FILE" ]; then
|
|
180
|
+
echo '{"success":false,"error":"Session not found"}'
|
|
181
|
+
exit 1
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
IDX=$(jq -r '.currentQuestionIndex' "$SESSION_FILE")
|
|
185
|
+
TOTAL=$(echo "$QUESTIONS" | jq 'length')
|
|
186
|
+
|
|
187
|
+
if [ "$IDX" -ge "$TOTAL" ]; then
|
|
188
|
+
echo '{"success":true,"status":"completed","message":"All questions answered. Run action=complete to generate the Brand Voice Guide."}'
|
|
189
|
+
else
|
|
190
|
+
Q_TEXT=$(echo "$QUESTIONS" | jq -r ".[$IDX].text")
|
|
191
|
+
Q_ID=$(echo "$QUESTIONS" | jq -r ".[$IDX].id")
|
|
192
|
+
Q_REQ=$(echo "$QUESTIONS" | jq -r ".[$IDX].required")
|
|
193
|
+
echo "{\"success\":true,\"questionNumber\":$((IDX + 1)),\"totalQuestions\":${TOTAL},\"questionId\":\"${Q_ID}\",\"questionText\":\"${Q_TEXT}\",\"required\":${Q_REQ}}"
|
|
194
|
+
fi
|
|
195
|
+
;;
|
|
196
|
+
|
|
197
|
+
answer)
|
|
198
|
+
if [ -z "$SESSION_ID" ]; then
|
|
199
|
+
echo '{"success":false,"error":"sessionId is required"}'
|
|
200
|
+
exit 1
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
SESSION_FILE="${LOCAL_DIR}/${SESSION_ID}.json"
|
|
204
|
+
if [ ! -f "$SESSION_FILE" ]; then
|
|
205
|
+
echo '{"success":false,"error":"Session not found"}'
|
|
206
|
+
exit 1
|
|
207
|
+
fi
|
|
208
|
+
|
|
209
|
+
IDX=$(jq -r '.currentQuestionIndex' "$SESSION_FILE")
|
|
210
|
+
Q_ID=$(echo "$QUESTIONS" | jq -r ".[$IDX].id")
|
|
211
|
+
Q_REQ=$(echo "$QUESTIONS" | jq -r ".[$IDX].required")
|
|
212
|
+
TOTAL=$(echo "$QUESTIONS" | jq 'length')
|
|
213
|
+
|
|
214
|
+
# Validate required
|
|
215
|
+
if [ "$Q_REQ" = "true" ] && [ -z "$VALUE" ]; then
|
|
216
|
+
echo '{"success":false,"error":"This question requires an answer"}'
|
|
217
|
+
exit 1
|
|
218
|
+
fi
|
|
219
|
+
|
|
220
|
+
# Save answer and advance
|
|
221
|
+
jq --arg qid "$Q_ID" --arg val "$VALUE" \
|
|
222
|
+
'.answers[$qid] = $val | .currentQuestionIndex += 1' \
|
|
223
|
+
"$SESSION_FILE" > "${SESSION_FILE}.tmp" && mv "${SESSION_FILE}.tmp" "$SESSION_FILE"
|
|
224
|
+
|
|
225
|
+
NEXT_IDX=$((IDX + 1))
|
|
226
|
+
if [ "$NEXT_IDX" -ge "$TOTAL" ]; then
|
|
227
|
+
jq '.status = "completed"' "$SESSION_FILE" > "${SESSION_FILE}.tmp" && mv "${SESSION_FILE}.tmp" "$SESSION_FILE"
|
|
228
|
+
echo "{\"success\":true,\"status\":\"completed\",\"message\":\"All questions answered! Run action=complete to generate the Brand Voice Guide.\"}"
|
|
229
|
+
else
|
|
230
|
+
NEXT_Q=$(echo "$QUESTIONS" | jq -r ".[$NEXT_IDX].text")
|
|
231
|
+
echo "{\"success\":true,\"questionNumber\":$((NEXT_IDX + 1)),\"totalQuestions\":${TOTAL},\"nextQuestion\":\"${NEXT_Q}\"}"
|
|
232
|
+
fi
|
|
233
|
+
;;
|
|
234
|
+
|
|
235
|
+
batch)
|
|
236
|
+
if [ -z "$TEAM_ID" ]; then
|
|
237
|
+
echo '{"success":false,"error":"teamId is required"}'
|
|
238
|
+
exit 1
|
|
239
|
+
fi
|
|
240
|
+
|
|
241
|
+
SESSION_ID="onboard-$(date +%s)-$RANDOM"
|
|
242
|
+
mkdir -p "$LOCAL_DIR"
|
|
243
|
+
|
|
244
|
+
# Extract answers from input
|
|
245
|
+
ANSWERS=$(echo "$INPUT" | jq '.answers // {}')
|
|
246
|
+
|
|
247
|
+
# Create completed session
|
|
248
|
+
echo "$ANSWERS" | jq --arg id "$SESSION_ID" --arg teamId "$TEAM_ID" \
|
|
249
|
+
'{
|
|
250
|
+
id: $id,
|
|
251
|
+
teamId: $teamId,
|
|
252
|
+
status: "completed",
|
|
253
|
+
currentQuestionIndex: 10,
|
|
254
|
+
totalQuestions: 10,
|
|
255
|
+
answers: .,
|
|
256
|
+
createdAt: (now | todate)
|
|
257
|
+
}' > "${LOCAL_DIR}/${SESSION_ID}.json"
|
|
258
|
+
|
|
259
|
+
# Auto-generate if outputDir provided
|
|
260
|
+
if [ -n "$OUTPUT_DIR" ]; then
|
|
261
|
+
ANSWERS_FILE="${LOCAL_DIR}/${SESSION_ID}-answers.json"
|
|
262
|
+
echo "$ANSWERS" > "$ANSWERS_FILE"
|
|
263
|
+
GUIDE_PATH=$(generate_guide "$ANSWERS_FILE" "$OUTPUT_DIR")
|
|
264
|
+
rm -f "$ANSWERS_FILE"
|
|
265
|
+
echo "{\"success\":true,\"sessionId\":\"${SESSION_ID}\",\"status\":\"done\",\"guidePath\":\"${GUIDE_PATH}\"}"
|
|
266
|
+
else
|
|
267
|
+
echo "{\"success\":true,\"sessionId\":\"${SESSION_ID}\",\"status\":\"completed\",\"message\":\"Answers saved. Run action=complete with outputDir to generate guide.\"}"
|
|
268
|
+
fi
|
|
269
|
+
;;
|
|
270
|
+
|
|
271
|
+
complete)
|
|
272
|
+
if [ -z "$SESSION_ID" ]; then
|
|
273
|
+
echo '{"success":false,"error":"sessionId is required"}'
|
|
274
|
+
exit 1
|
|
275
|
+
fi
|
|
276
|
+
if [ -z "$OUTPUT_DIR" ]; then
|
|
277
|
+
echo '{"success":false,"error":"outputDir is required"}'
|
|
278
|
+
exit 1
|
|
279
|
+
fi
|
|
280
|
+
|
|
281
|
+
SESSION_FILE="${LOCAL_DIR}/${SESSION_ID}.json"
|
|
282
|
+
if [ ! -f "$SESSION_FILE" ]; then
|
|
283
|
+
echo '{"success":false,"error":"Session not found"}'
|
|
284
|
+
exit 1
|
|
285
|
+
fi
|
|
286
|
+
|
|
287
|
+
STATUS=$(jq -r '.status' "$SESSION_FILE")
|
|
288
|
+
if [ "$STATUS" != "completed" ]; then
|
|
289
|
+
echo '{"success":false,"error":"Session is not complete. Answer all questions first."}'
|
|
290
|
+
exit 1
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# Extract answers to temp file
|
|
294
|
+
ANSWERS_FILE="${LOCAL_DIR}/${SESSION_ID}-answers.json"
|
|
295
|
+
jq '.answers' "$SESSION_FILE" > "$ANSWERS_FILE"
|
|
296
|
+
|
|
297
|
+
# Generate guide
|
|
298
|
+
GUIDE_PATH=$(generate_guide "$ANSWERS_FILE" "$OUTPUT_DIR")
|
|
299
|
+
rm -f "$ANSWERS_FILE"
|
|
300
|
+
|
|
301
|
+
# Update session
|
|
302
|
+
jq --arg path "$GUIDE_PATH" '.status = "done" | .guidePath = $path' \
|
|
303
|
+
"$SESSION_FILE" > "${SESSION_FILE}.tmp" && mv "${SESSION_FILE}.tmp" "$SESSION_FILE"
|
|
304
|
+
|
|
305
|
+
echo "{\"success\":true,\"sessionId\":\"${SESSION_ID}\",\"status\":\"done\",\"guidePath\":\"${GUIDE_PATH}\",\"message\":\"Brand Voice Guide generated successfully.\"}"
|
|
306
|
+
;;
|
|
307
|
+
|
|
308
|
+
*)
|
|
309
|
+
echo "{\"success\":false,\"error\":\"Unknown action: ${ACTION}. Use start, question, answer, batch, or complete.\"}"
|
|
310
|
+
exit 1
|
|
311
|
+
;;
|
|
312
|
+
esac
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: submit-for-approval
|
|
3
|
+
description: Submit marketing content (posts, articles) for human approval via Slack. Used by Writer and Strategist agents in the Marketing Team template.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: marketing
|
|
6
|
+
assignableRoles:
|
|
7
|
+
- writer
|
|
8
|
+
- strategist
|
|
9
|
+
- content-writer
|
|
10
|
+
- content-strategist
|
|
11
|
+
triggers:
|
|
12
|
+
- before_publish
|
|
13
|
+
- manual
|
|
14
|
+
tags:
|
|
15
|
+
- marketing
|
|
16
|
+
- approval
|
|
17
|
+
- slack
|
|
18
|
+
- content
|
|
19
|
+
execution:
|
|
20
|
+
type: script
|
|
21
|
+
script: execute.sh
|
|
22
|
+
interpreter: bash
|
|
23
|
+
timeout: 30000
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
# Submit for Approval
|
|
27
|
+
|
|
28
|
+
Submits marketing content for human review and approval via Slack.
|
|
29
|
+
|
|
30
|
+
## Usage
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bash execute.sh '{"action":"submit","platform":"Instagram","contentType":"post","content":"Your post text here...","hashtags":["#tag1","#tag2"],"visualDirection":"Photo of product","scheduledTime":"2026-03-25T09:00:00Z"}'
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Actions
|
|
37
|
+
|
|
38
|
+
### submit
|
|
39
|
+
Submit content for approval. Sends a formatted message to the configured Slack channel.
|
|
40
|
+
|
|
41
|
+
**Parameters:**
|
|
42
|
+
- `platform` (required): Target platform (Instagram, X, LinkedIn, Facebook)
|
|
43
|
+
- `contentType` (required): Content type (post, article, newsletter, thread)
|
|
44
|
+
- `content` (required): The actual content text
|
|
45
|
+
- `hashtags` (optional): Array of hashtags
|
|
46
|
+
- `visualDirection` (optional): Visual/image brief
|
|
47
|
+
- `scheduledTime` (optional): When to publish
|
|
48
|
+
|
|
49
|
+
### status
|
|
50
|
+
Check the status of a pending approval.
|
|
51
|
+
|
|
52
|
+
**Parameters:**
|
|
53
|
+
- `approvalId` (required): The approval request ID
|
|
54
|
+
|
|
55
|
+
### list
|
|
56
|
+
List all pending approvals for the current team.
|
|
57
|
+
|
|
58
|
+
## Response Format
|
|
59
|
+
|
|
60
|
+
```json
|
|
61
|
+
{
|
|
62
|
+
"success": true,
|
|
63
|
+
"approvalId": "abc-123",
|
|
64
|
+
"status": "pending",
|
|
65
|
+
"message": "Content submitted for approval. The business owner will review via Slack."
|
|
66
|
+
}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Integration
|
|
70
|
+
|
|
71
|
+
This skill integrates with the ContentApprovalService in the Crewly backend.
|
|
72
|
+
The approval message is sent to Slack where the business owner can reply
|
|
73
|
+
with "approve" or "reject [reason]" to resolve the request.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# =============================================================================
|
|
3
|
+
# submit-for-approval — Submit marketing content for human approval
|
|
4
|
+
#
|
|
5
|
+
# Submits content to the ContentApprovalService via the Crewly API.
|
|
6
|
+
# The approval message is sent to the configured Slack channel.
|
|
7
|
+
#
|
|
8
|
+
# Usage:
|
|
9
|
+
# bash execute.sh '{"action":"submit","platform":"Instagram","contentType":"post","content":"...","hashtags":["#tag"]}'
|
|
10
|
+
# bash execute.sh '{"action":"status","approvalId":"abc-123"}'
|
|
11
|
+
# bash execute.sh '{"action":"list"}'
|
|
12
|
+
# =============================================================================
|
|
13
|
+
|
|
14
|
+
set -euo pipefail
|
|
15
|
+
|
|
16
|
+
INPUT="${1:-"{}"}"
|
|
17
|
+
|
|
18
|
+
# Parse input fields
|
|
19
|
+
ACTION=$(echo "$INPUT" | jq -r '.action // "submit"')
|
|
20
|
+
PLATFORM=$(echo "$INPUT" | jq -r '.platform // ""')
|
|
21
|
+
CONTENT_TYPE=$(echo "$INPUT" | jq -r '.contentType // "post"')
|
|
22
|
+
CONTENT=$(echo "$INPUT" | jq -r '.content // ""')
|
|
23
|
+
HASHTAGS=$(echo "$INPUT" | jq -r '.hashtags // [] | join(", ")')
|
|
24
|
+
VISUAL_DIRECTION=$(echo "$INPUT" | jq -r '.visualDirection // ""')
|
|
25
|
+
SCHEDULED_TIME=$(echo "$INPUT" | jq -r '.scheduledTime // ""')
|
|
26
|
+
APPROVAL_ID=$(echo "$INPUT" | jq -r '.approvalId // ""')
|
|
27
|
+
TEAM_ID=$(echo "$INPUT" | jq -r '.teamId // ""')
|
|
28
|
+
PROJECT_PATH=$(echo "$INPUT" | jq -r '.projectPath // ""')
|
|
29
|
+
|
|
30
|
+
# Crewly API base URL
|
|
31
|
+
API_BASE="${CREWLY_API_URL:-http://localhost:3000}"
|
|
32
|
+
|
|
33
|
+
case "$ACTION" in
|
|
34
|
+
submit)
|
|
35
|
+
# Validate required fields
|
|
36
|
+
if [ -z "$PLATFORM" ]; then
|
|
37
|
+
echo '{"success":false,"error":"platform is required"}'
|
|
38
|
+
exit 1
|
|
39
|
+
fi
|
|
40
|
+
if [ -z "$CONTENT" ]; then
|
|
41
|
+
echo '{"success":false,"error":"content is required"}'
|
|
42
|
+
exit 1
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# Build the approval request payload
|
|
46
|
+
PAYLOAD=$(jq -n \
|
|
47
|
+
--arg teamId "$TEAM_ID" \
|
|
48
|
+
--arg submittedBy "${CREWLY_SESSION_NAME:-unknown}" \
|
|
49
|
+
--arg platform "$PLATFORM" \
|
|
50
|
+
--arg contentType "$CONTENT_TYPE" \
|
|
51
|
+
--arg content "$CONTENT" \
|
|
52
|
+
--arg hashtags "$HASHTAGS" \
|
|
53
|
+
--arg visualDirection "$VISUAL_DIRECTION" \
|
|
54
|
+
--arg scheduledTime "$SCHEDULED_TIME" \
|
|
55
|
+
'{
|
|
56
|
+
teamId: $teamId,
|
|
57
|
+
submittedBy: $submittedBy,
|
|
58
|
+
platform: $platform,
|
|
59
|
+
contentType: $contentType,
|
|
60
|
+
content: $content,
|
|
61
|
+
hashtags: (if $hashtags == "" then [] else ($hashtags | split(", ")) end),
|
|
62
|
+
visualDirection: (if $visualDirection == "" then null else $visualDirection end),
|
|
63
|
+
scheduledTime: (if $scheduledTime == "" then null else $scheduledTime end)
|
|
64
|
+
}')
|
|
65
|
+
|
|
66
|
+
# Submit via API
|
|
67
|
+
RESPONSE=$(curl -s -X POST "${API_BASE}/api/content-approvals" \
|
|
68
|
+
-H "Content-Type: application/json" \
|
|
69
|
+
-d "$PAYLOAD" 2>/dev/null || echo '{"error":"API unreachable"}')
|
|
70
|
+
|
|
71
|
+
# Check if API call succeeded
|
|
72
|
+
if echo "$RESPONSE" | jq -e '.id' >/dev/null 2>&1; then
|
|
73
|
+
APPROVAL_ID=$(echo "$RESPONSE" | jq -r '.id')
|
|
74
|
+
echo "{\"success\":true,\"approvalId\":\"${APPROVAL_ID}\",\"status\":\"pending\",\"message\":\"Content submitted for approval. The business owner will review via Slack.\"}"
|
|
75
|
+
else
|
|
76
|
+
# Fallback: store locally if API is not available
|
|
77
|
+
APPROVAL_ID="local-$(date +%s)-$RANDOM"
|
|
78
|
+
APPROVAL_DIR="${PROJECT_PATH:-.}/.crewly/content-approvals"
|
|
79
|
+
mkdir -p "$APPROVAL_DIR"
|
|
80
|
+
|
|
81
|
+
echo "$PAYLOAD" | jq --arg id "$APPROVAL_ID" --arg status "pending" \
|
|
82
|
+
'. + {id: $id, status: $status, submittedAt: (now | todate)}' \
|
|
83
|
+
> "${APPROVAL_DIR}/${APPROVAL_ID}.json"
|
|
84
|
+
|
|
85
|
+
echo "{\"success\":true,\"approvalId\":\"${APPROVAL_ID}\",\"status\":\"pending\",\"message\":\"Content saved locally for approval (API not available). File: ${APPROVAL_DIR}/${APPROVAL_ID}.json\"}"
|
|
86
|
+
fi
|
|
87
|
+
;;
|
|
88
|
+
|
|
89
|
+
status)
|
|
90
|
+
if [ -z "$APPROVAL_ID" ]; then
|
|
91
|
+
echo '{"success":false,"error":"approvalId is required"}'
|
|
92
|
+
exit 1
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# Check via API
|
|
96
|
+
RESPONSE=$(curl -s "${API_BASE}/api/content-approvals/${APPROVAL_ID}" 2>/dev/null || echo '{"error":"API unreachable"}')
|
|
97
|
+
|
|
98
|
+
if echo "$RESPONSE" | jq -e '.id' >/dev/null 2>&1; then
|
|
99
|
+
STATUS=$(echo "$RESPONSE" | jq -r '.status')
|
|
100
|
+
FEEDBACK=$(echo "$RESPONSE" | jq -r '.feedback // "none"')
|
|
101
|
+
echo "{\"success\":true,\"approvalId\":\"${APPROVAL_ID}\",\"status\":\"${STATUS}\",\"feedback\":\"${FEEDBACK}\"}"
|
|
102
|
+
else
|
|
103
|
+
# Fallback: check local file
|
|
104
|
+
APPROVAL_DIR="${PROJECT_PATH:-.}/.crewly/content-approvals"
|
|
105
|
+
LOCAL_FILE="${APPROVAL_DIR}/${APPROVAL_ID}.json"
|
|
106
|
+
if [ -f "$LOCAL_FILE" ]; then
|
|
107
|
+
STATUS=$(jq -r '.status' "$LOCAL_FILE")
|
|
108
|
+
echo "{\"success\":true,\"approvalId\":\"${APPROVAL_ID}\",\"status\":\"${STATUS}\",\"source\":\"local\"}"
|
|
109
|
+
else
|
|
110
|
+
echo "{\"success\":false,\"error\":\"Approval ${APPROVAL_ID} not found\"}"
|
|
111
|
+
fi
|
|
112
|
+
fi
|
|
113
|
+
;;
|
|
114
|
+
|
|
115
|
+
list)
|
|
116
|
+
# List pending approvals via API
|
|
117
|
+
RESPONSE=$(curl -s "${API_BASE}/api/content-approvals?teamId=${TEAM_ID}&status=pending" 2>/dev/null || echo '[]')
|
|
118
|
+
|
|
119
|
+
if echo "$RESPONSE" | jq -e '.[0]' >/dev/null 2>&1; then
|
|
120
|
+
COUNT=$(echo "$RESPONSE" | jq 'length')
|
|
121
|
+
echo "{\"success\":true,\"count\":${COUNT},\"approvals\":${RESPONSE}}"
|
|
122
|
+
else
|
|
123
|
+
# Fallback: list local files
|
|
124
|
+
APPROVAL_DIR="${PROJECT_PATH:-.}/.crewly/content-approvals"
|
|
125
|
+
if [ -d "$APPROVAL_DIR" ]; then
|
|
126
|
+
COUNT=$(ls -1 "$APPROVAL_DIR"/*.json 2>/dev/null | wc -l | tr -d ' ')
|
|
127
|
+
echo "{\"success\":true,\"count\":${COUNT},\"source\":\"local\",\"directory\":\"${APPROVAL_DIR}\"}"
|
|
128
|
+
else
|
|
129
|
+
echo "{\"success\":true,\"count\":0,\"approvals\":[]}"
|
|
130
|
+
fi
|
|
131
|
+
fi
|
|
132
|
+
;;
|
|
133
|
+
|
|
134
|
+
*)
|
|
135
|
+
echo "{\"success\":false,\"error\":\"Unknown action: ${ACTION}. Use submit, status, or list.\"}"
|
|
136
|
+
exit 1
|
|
137
|
+
;;
|
|
138
|
+
esac
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: weekly-content-planning
|
|
3
|
+
description: Generates a weekly content calendar with 5-7 posts across specified platforms. Wraps the content-calendar skill to create batch entries for the upcoming week.
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
category: marketing
|
|
6
|
+
skillType: claude-skill
|
|
7
|
+
assignableRoles:
|
|
8
|
+
- strategist
|
|
9
|
+
triggers:
|
|
10
|
+
- weekly content plan
|
|
11
|
+
- plan next week
|
|
12
|
+
- content planning
|
|
13
|
+
- weekly calendar
|
|
14
|
+
tags:
|
|
15
|
+
- marketing
|
|
16
|
+
- content
|
|
17
|
+
- planning
|
|
18
|
+
- calendar
|
|
19
|
+
- weekly
|
|
20
|
+
execution:
|
|
21
|
+
type: script
|
|
22
|
+
script:
|
|
23
|
+
file: execute.sh
|
|
24
|
+
interpreter: bash
|
|
25
|
+
timeoutMs: 30000
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
# Weekly Content Planning
|
|
29
|
+
|
|
30
|
+
Generates a weekly content calendar with 5-7 posts across specified platforms. Uses the content-calendar skill to persist each entry.
|
|
31
|
+
|
|
32
|
+
## Input Parameters
|
|
33
|
+
|
|
34
|
+
| Parameter | Required | Description |
|
|
35
|
+
|-----------|----------|-------------|
|
|
36
|
+
| `businessName` | Yes | Name of the business |
|
|
37
|
+
| `industry` | Yes | Business industry/vertical |
|
|
38
|
+
| `platforms` | Yes | JSON array of target platforms (e.g., `["x","linkedin"]`) |
|
|
39
|
+
| `contentMix` | No | JSON object with content type ratios (default: `{"educational":40,"engagement":30,"promotional":20,"community":10}`) |
|
|
40
|
+
| `weekStartDate` | Yes | Start date of the week in `YYYY-MM-DD` format |
|
|
41
|
+
| `projectPath` | No | Project path for calendar storage |
|
|
42
|
+
| `postCount` | No | Number of posts to plan (default: 5) |
|
|
43
|
+
|
|
44
|
+
## Example
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
bash execute.sh '{"businessName":"Acme AI","industry":"artificial intelligence","platforms":["x","linkedin","instagram"],"weekStartDate":"2026-03-23","projectPath":"/path/to/project"}'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## Output
|
|
51
|
+
|
|
52
|
+
Returns JSON with `success: true` and a `calendar` field containing the generated entries as a markdown table, plus an `entries` array with the raw entry data.
|